最终用户身份验证

Contact Center AI 平台使用最终用户身份验证来安全地识别使用您的宿主应用的用户。此机制可确保只有经过授权的用户才能访问 CCAI Platform API,并确保其聊天会话和数据始终安全地与其特定身份相关联。

为了识别最终用户,您的宿主应用必须提供一个代表该特定人员的通用唯一标识符 (UUID)。

CCAI 平台中的身份验证机制

CCAI 平台中的身份验证依赖于使用 JSON Web 令牌 (JWT) 的安全握手。JWT 是一种开放标准,用于以 JSON 对象的形式安全地传输信息。

以下是标准身份验证流程:

  1. 请求:当最终用户开始使用 CCAI Platform 客户服务时,CCAI Platform SDK 会向您的宿主应用请求已签名的 JWT。

  2. 签名:宿主应用使用您的共享 CCAI 平台公司密钥 (company_secret) 为 JWT 签名。

  3. 交换:宿主应用将已签名的 JWT 传递回 CCAI 平台 SDK。然后,SDK 会将此 JWT 发送到 CCAI 平台服务器。

  4. 验证:CCAI Platform 服务器验证签名。如果有效,CCAI 平台会颁发授权令牌,授予用户对 API 的访问权限,并将用户与其账号相关联(如果用户没有账号,则创建一个新账号)。

如果宿主应用未在 JWT 载荷中提供标识符,CCAI Platform 将为用户创建一个匿名访客账号。

前提条件

在实现身份验证之前,您需要获取唯一的 SDK 身份验证元素。这些密钥可安全地向 CCAI 平台 API 标识您的公司。

  1. 使用管理员账号登录 CCAI 平台。

  2. 依次前往设置 > 开发者设置 > API 凭据。

  3. 找到您的 SDK 密钥名称 (sdk_key_name)、SDK 密钥 (sdk_key) 和公司密钥 (company_secret)。

请妥善保管这些凭据。请仅与授权的系统和开发者分享这些密钥。

开发者实现指南

以下部分概述了开发者应如何在不同平台上构建 JWT 载荷并初始化 CCAI 平台 SDK。

JWT 载荷要求

如需将最终用户的信息传递给 CCAI Platform,宿主应用必须构建 JWT 载荷。回调方法会提供默认载荷(可能包含推送令牌和默认名称),但您可以使用以下预留键添加特定用户详细信息:

  • identifier(可选,但强烈建议提供 UUID)

  • name(可选)

  • email(可选)

  • phone(可选,必须采用 E.164 格式)

  • iss(可选,您的公司名称)

  • iat(必需,签发时间戳)

  • exp(必需,到期时间戳)

服务器端签名(推荐)

服务器上的 API 示例 (Ruby on Rails)

此示例展示了如何使用 Ruby on Rails 框架和 JWT gem 在服务器端对载荷进行签名。

该代码使用 jwt gem 设置 API 端点 (/api/ujet/sign),以使用公司密钥对载荷进行编码,并将编码后的令牌返回给客户端。

假设宿主应用的基本网址为 https://company.com/api/。 如需为 CCAI 平台签名载荷,您可以在 https://company.com/api/ujet/sign 添加另一个 API 端点。

该代码向应用添加了一个新路由,以处理对 /api/ujet/sign 网址的 POST 请求。

ujet_controller.rb 文件定义了 UjetController 类,该类具有一个端点 sign,用于为 CCAI 平台签名载荷。

在代码中,COMPANY_SECRET 被定义为用于为载荷签名的密钥。有效载荷从请求正文中提取,然后向其中添加各种值,例如 UNIQUE-IDENTIFIER、用户名、电子邮件地址、手机号码。

JWT.encode 方法用于将载荷和 ujet_secret 编码为 JSON Web 令牌 (JWT),然后以包含令牌键的 JSON 对象形式在响应中返回该令牌。

# routes.rb
post 'ujet/sign' => "ujet#sign"

# ujet_controller.rb
class UjetController
  def sign
    # In production, securely store and call your secret (e.g., via environment variables)
    ujet_secret = "COMPANY_SECRET"

    payload = body["payload"]
    payload["identifier"] = "UNIQUE-IDENTIFIER"
    payload["name"] = "user name"
    payload["email"] = "test@email.com"
    payload["phone"] = ""                     # E.164 format
    payload["iss"] = "YOUR_COMPANY_NAME"
    payload["iat"] = Time.now.to_i
    payload["exp"] = Time.now.to_i + 10.minutes # Valid for 10 minutes

    token = JWT.encode(payload, ujet_secret)

    render json: {token: token}
  end
end

客户端初始化

请按照以下指南初始化 CCAI 平台移动 SDK。

通过 iOS SDK 进行签名

此示例展示了如何使用 iOS SDK 对载荷进行签名,它会向网址 https://your.company.com/api/ujet/sign 的服务器发出 POST 请求,并将载荷作为请求正文。

服务器应在响应中返回签名令牌,然后以 JSON 对象中“token”键的形式将其传递给成功回调。

如果请求期间出现错误,则会将其传递给失败回调。

-   (void)signPayload:(NSDictionary *)payload payloadType:(UjetPayloadType)payloadType success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure
{
  if (payloadType == UjetPayloadAuthToken) {
    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] init];
    mutableRequest.URL = [NSURL URLWithString:@"https://your.company.com/api/ujet/sign"];
    mutableRequest.HTTPMethod = @"POST";
    NSError *error;
    NSDictionary *data = @{@"payload": payload};
    mutableRequest.HTTPBody = [NSJSONSerialization dataWithJSONObject:data options:0 error:&error];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:mutableRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if(error) {
            failure(error);
        }
        else {
            NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
            success(json[@"token"]);
        }
    }];

    [task resume];
  }
}

通过 Android SDK 进行签名

以下示例展示了如何使用 Android SDK 对载荷进行签名。

此示例使用 Android 的 HTTP 客户端 Retrofit 向您的服务器请求已签名的载荷。该函数以载荷、载荷类型和令牌回调作为输入。

如果载荷类型为 UjetPayloadType.AuthToken,则会创建一个 Retrofit 实例,使用该实例向 https://company.com/api API 端点发出请求以对载荷进行签名,并在 tokenCallback 实例的 onToken 方法中返回签名后的令牌。

如果失败,系统会显示一条 Toast 消息 "Authentication failed"。

public void onSignPayloadRequest(Map<String, Object> payload, UjetPayloadType ujetPayloadType, final UjetTokenCallback tokenCallback) {
    if (ujetPayloadType == UjetPayloadType.AuthToken) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://company.com/api/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        AuthService authService = retrofit.create(AuthService.class);
        Call<AuthToken> authenticate = authService.authenticate(new AuthRequest(payload));
        authenticate.enqueue(new Callback<AuthToken>() {
            @Override
            public void onResponse(Call<AuthToken> call, Response<AuthToken> response) {
                if (response.isSuccessful()) {
                    AuthToken authToken = response.body();
                    tokenCallback.onToken(authToken.getToken());
                } else {
                    Toast.makeText(ExampleApplication.this, "Authentication failed", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<AuthToken> call, Throwable t) {
                Toast.makeText(ExampleApplication.this, "Authentication failed", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

通过 Web SDK 进行签名

以下示例演示了如何在 Web SDK 中通过向服务器发送包含载荷的 API 请求来对载荷进行签名。

服务器应设置为处理 API 请求,并使用安全方法对载荷进行签名。然后,结果会返回到网页,该网页会使用回调函数将已签名的令牌传递回 CCAI 平台。

$(function() {
  UJET.initialize({
    // ... other parameters
    handlers: {
      authentication(callback) {
        $.ajax({
          type: 'POST',
          url: 'https://company.com/api/ujet/sign',
          data: JSON.stringify({
            payload: {
              identifier: 'UNIQUE-Identifier',
              name: 'Test user'
            }
          }),
          success: function(result) {
            callback({
              token: result.token,
              user: {
                identifier: 'UNIQUE-IDENTIFIER',
                name: 'Test user'
              }
            });
          }
        });
      }
    }
  }).then(function() {
    // successfully initialized
  }).catch(function(error) {
    // Handle initialization error
  });
});

会话中身份验证和 SDK 重新初始化

在某些情况下,最终用户可能会以访客身份(未经身份验证)开始聊天会话,并在会话中验证自己的身份(例如,通过虚拟助理或流程中的登录界面)。为确保有效会话得到安全更新并与新完成身份验证的用户相关联,请按以下步骤操作:

会话中身份验证的步骤

  1. 使用临时标识符初始化 SDK:当用户未通过身份验证时,使用随机标识符或访客标识符初始化 CCAI 平台 SDK。

  2. 引导用户进行身份验证:引导用户进入您的身份验证流程,例如 CCAI 平台虚拟助理 (VA) 或应用的内置登录界面。

  3. 通过 API 更新最终用户:在用户成功通过身份验证后(并且在用户仍处于虚拟客服或身份验证流程中时),您的后端必须调用 CCAI Platform Apps API,以使用用户的真实标识符更新最终用户的聊天会话。

  4. 使用新的 JWT 重新初始化 SDK:后端成功更新后,为经过身份验证的用户生成新的 JWT。使用此新 JWT 再次调用初始化方法,重新初始化 CCAI Platform SDK。

  5. 升级会话(如果使用虚拟助理):如果用户之前使用虚拟助理进行身份验证,虚拟客服现在应将消费者升级到其最终目标队列。

示例 (Web SDK)

如需重新初始化,请再次调用 UJET.initialize 函数。这会触发身份验证处理程序来获取新令牌。

// Call this after successful authentication and the CCAI Platform Apps API update
UJET.initialize({
  // ...include your existing parameters
  handlers: {
    authentication(callback) {
      // Fetch the new JWT for the newly authenticated user
      $.ajax({
        type: 'POST',
        url: 'https://your.company.com/api/ujet/sign',
        data: JSON.stringify({
          payload: {
            identifier: 'AUTHENTICATED-USER-UUID',
            name: 'Authenticated User Name'
          }
        }),
        success: function(result) {
          callback({
            token: result.token,
            user: {
              identifier: 'AUTHENTICATED-USER-UUID',
              name: 'Authenticated User Name'
            }
          });
        }
      });
    }
  }
}).then(function() {
  // SDK successfully reinitialized with the authenticated user
}).catch(function(error) {
  // Handle reinitialization errors
});

为何要这样做?

  • 这样可确保所有后续操作、路由和历史记录都安全地与已通过身份验证的用户相关联,而不是与匿名访客会话相关联。

  • 这样可以防止数据泄露,并保持 CRM 和用户记录的准确性。

摘要表

场景 需要采取行动
用户启动了经过身份验证的会话 使用用户的真实 UUID 和 JWT 初始化 SDK。
用户以未经身份验证的状态开始 使用访客/随机标识符初始化 SDK。
用户在会话中进行身份验证 使用 API 更新 CCAI 平台,生成新的 JWT,然后重新初始化 SDK。