> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/authentication/custom-token-provider.md).

# 编写自定义令牌提供程序

注册一个自定义凭证提供程序，让你的后端为每次连接自动签发短期 Convai 身份验证令牌。

实现 `IConvaiAuthTokenProvider` 当你的项目已经有一个后端或登录层，可以生成 Convai 身份验证令牌，并且你希望 SDK 在每次连接时自动调用它时使用本页。当你的项目处于身份验证令牌模式，并且你希望在不修改各调用点连接代码的情况下完成令牌解析时，也使用本页。

### 前提条件

* 项目的 `ConvaiSettings` 资源文件有 `AuthMode` 设置为 `AuthToken`。另请参见 [配置 Auth Token 模式](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/authentication/configure-auth-token-mode.md).
* 一个后端端点或 SDK，用于返回一个短时有效的 Convai 身份验证令牌（一个 `apiAuthToken`）供已登录的玩家使用。

### 实现该接口

`IConvaiAuthTokenProvider` 只有一个方法：

```csharp
public interface IConvaiAuthTokenProvider
{
    Task<AuthTokenResult> GetTokenAsync(CancellationToken cancellationToken);
}
```

SDK 会调用 `GetTokenAsync` 每次新的房间连接尝试都会调用一次。返回 `AuthTokenResult.Succeeded(token, expiresAtUtc)` 成功时，或 `AuthTokenResult.Failed(errorMessage)` 当无法解析令牌时。 `expiresAtUtc` 是可选的。

{% code title="Assets/Scripts/MyAuthTokenProvider.cs" %}

```csharp
using System;
using System.Threading;
using System.Threading.Tasks;
using Convai.Runtime.Core.Configuration;

public sealed class MyAuthTokenProvider : IConvaiAuthTokenProvider
{
    public async Task<AuthTokenResult> GetTokenAsync(CancellationToken cancellationToken)
    {
        try
        {
            string token = await MyBackend.RequestConvaiTokenAsync(cancellationToken);
            return string.IsNullOrWhiteSpace(token)
                ? AuthTokenResult.Failed("我的后端返回了空令牌。")
                : AuthTokenResult.Succeeded(token);
        }
        catch (Exception exception)
        {
            return AuthTokenResult.Failed($"我的后端请求失败（{exception.GetType().Name}）。");
        }
    }
}
```

{% endcode %}

{% hint style="warning" %}
`IConvaiAuthTokenProvider` 实现不得记录或持久化返回的令牌。请将其视为仅限于单次连接尝试的短时密钥。
{% endhint %}

### 在连接之前注册提供程序

使用 `ConvaiAuthTokenProviderRegistry` 在首次连接尝试之前，通常在一个早期的 `Awake`:

```csharp
using Convai.Runtime.Core.Configuration;
using UnityEngine;

public sealed class AuthBootstrap : MonoBehaviour
{
    private MyAuthTokenProvider _provider;

    private void Awake()
    {
        _provider = new MyAuthTokenProvider();
        ConvaiAuthTokenProviderRegistry.Register(_provider);
    }

    private void OnDestroy()
    {
        ConvaiAuthTokenProviderRegistry.Unregister(_provider);
    }
}
```

`ConvaiAuthTokenProviderRegistry.Register` 替换当前已注册的任意提供程序。 `Unregister(provider)` 仅当它仍是当前活动注册时才会将其移除； `Unregister()` 不带参数的 `Clear()` 都会移除当前活动的提供程序。

{% hint style="warning" %}
`ConvaiAuthTokenProviderRegistry` 是一个静态、进程本地的注册，会在 `RuntimeInitializeLoadType.SubsystemRegistration`。这会在每次域重新加载以及每次进入 Play 模式时运行，因此一次注册的提供程序不会保留——每次你的引导脚本运行时都要重新注册提供程序，不要假设一次调用就足够了。
{% endhint %}

### 使用委托处理简单情况

对于只需要一次异步查询的提供程序，将一个 lambda 包装在 `DelegateAuthTokenProvider` 而不是编写完整的类：

```csharp
using Convai.Runtime.Core.Configuration;

ConvaiAuthTokenProviderRegistry.Register(
    new DelegateAuthTokenProvider(async cancellationToken =>
        await MyBackend.RequestConvaiTokenAsync(cancellationToken)));
```

`DelegateAuthTokenProvider` 封装一个 `Func<CancellationToken, Task<string>>`。如果出现以下情况则会失败： `AuthTokenResult.Failed` 如果委托返回 `null`、空任务或空字符串，因此你的委托只需返回令牌字符串或抛出。

### 验证设置

进入 Play 模式并连接一个角色。如果提供程序解析正确，连接会在没有任何身份验证相关错误的情况下继续。如果解析失败，连接会显示来自 `AuthTokenResult.Failed` 通过正常连接错误路径。

### 下一步

{% content-ref url="/pages/1e83cf2de6c0f33600727e9d53472326fe916861" %}
[使用现有身份验证令牌连接](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/authentication/connect-with-auth-token.md)
{% endcontent-ref %}

{% content-ref url="/pages/fb645a3f7b0858dadf0b26ee09f168a43e9e98b7" %}
[身份验证脚本参考](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/authentication/scripting-reference.md)
{% endcontent-ref %}

{% content-ref url="/pages/2b331d8b6d61de917a4ae462f423b45b693555f4" %}
[身份验证故障排查](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/authentication/troubleshooting.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/authentication/custom-token-provider.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
