Passport 客户端API接入指南
Passport 客户端API接入指南
客户端API
Passport 客户端API是面向已有独立的ID系统,需要使用UOS Passport提供的其它服务(如好友,权利,排行榜等)的开发者提供的客户端API列表。
以下文档中 URL / Request / Response 中出现的 idDomainID 均为 UOS App ID
API Endpoint: https://p.unity.cn
用户身份认证(JWT)
使用 UOS Passport 提供的所有客户端API之前都需要将已有的独立的ID系统的用户/角色关联到 UOS 系统的用户与角色,并获取 JWT 用户认证信息。
如何关联 UOS 角色并获取 JWT ?
未集成客户端 SDK 的开发者, 可使用 External Login API 来关联 UOS 角色并获取 JWT。
JWT API 基本信息
- 接口功能:
关联 UOS 角色并获取 JWT - 请求方法:
POST - 请求地址:
https://p.unity.cn/v1/login/external - 请求体:
application/json
{
"externalUserID": "<userId>", //需要获取 JWT 的玩家 id,
"externalPersonaID": "<personaID>", //可选,非必填,需要获取 JWT 的角色 id,
"displayName": "<displayName>", //可选,非必填,角色名字(仅创建时传入生效),
"realmID": "<realmID>" //可选,非必填,服务器 id
}- 响应数据
{
"personaAccessToken": "<token>", //JWT(JSON Web Token), 可用于后续 API 的鉴权
"personaRefreshToken": "<token>", //角色 RefreshToken
"expiresAt": "<expiresAt>" //JWT 的过期时间
}- 是否需要认证:
需携带 Authorization
使用 Nonce Authorization 来获取 JWT,步骤如下:
在 UOS 网站上获取当前需要使用的 UOS APP 的 AppId 和 AppSecret
注:此处 AppId 和 AppSecret,可在 UOS 网站上获取


在请求 JWT API 的 Request Header 中增加如下 Header:
- X-TIMESTAMP: 当前时间戳
- X-NONCE: 随机生成的UUID
- X-APPID: 当前需要使用的 UOS APP 的 AppId
- Authorization: nonce hexadecimal(sha256(appId + ":" + appSecret + ":" + X-TIMESTAMP + ":" + X-NONCE))
完整获取 JWT 参考示例如下:
/// <summary>
/// 用户身份认证(JWT) - C#
/// </summary>
/// <param name="appID">uos app ID</param>
/// <param name="appSecret">uos app secret</param>
/// <param name="externalUserID">外部用户ID(唯一)</param>
/// <param name="externalPersonaID">外部角色ID,可选,可填和externalUserID相同</param>
/// <param name="displayName">角色名字(仅创建时传入生效)</param>
/// <param name="realmID">服务器ID,可选填</param>
public static async void ExternalLogin(string appID, string appSecret, string externalUserID, string externalPersonaID = null, string displayName = null, string realmID = null)
{
var req = new UnityWebRequest("https://p.unity.cn/v1/login/external", "POST");
var request = new ExternalLoginRequest
{
externalUserID = externalUserID,
externalPersonaID = externalPersonaID,
displayName = displayName,
realmID = realmID
};
var requestData = JsonConvert.SerializeObject(request);
req.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(requestData));
req.downloadHandler = new DownloadHandlerBuffer();
string nonce = Guid.NewGuid().ToString();
long timestamp = GetUnixTimeStampSeconds(DateTime.UtcNow);
string tokenContent = $"{appID}:{appSecret}:{timestamp}:{nonce}";
string token = HexString(Sha256(tokenContent));
req.SetRequestHeader("X-TIMESTAMP", $"{timestamp}");
req.SetRequestHeader("X-NONCE", nonce);
req.SetRequestHeader("X-APPID", appID);
req.SetRequestHeader("Authorization", $"nonce {token}");
var sendOperation = req.SendWebRequest();
while (!sendOperation.isDone)
{
await Task.Yield();
}
if (req.result != UnityWebRequest.Result.Success)
{
Debug.Log(req.error);
}
else
{
ExternalLoginResponse data = JsonUtility.FromJson<ExternalLoginResponse>(req.downloadHandler.text);
var jwt = data.personaAccessToken;
// 获取到的jwt,建议存储下来以便后续使用
Debug.Log(jwt);
}
}
public struct ExternalLoginRequest
{
public string uosAppID;
public string externalUserID;
public string externalPersonaID;
public string displayName;
public string realmID;
}
public struct ExternalLoginResponse
{
public string personaAccessToken;
public string personaRefreshToken;
public int expiresAt;
// 已对应的UOS Passport 角色信息,需要的话可以自行展开
// public Persona persona;
}
protected internal static string HexString(byte[] data)
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
builder.Append(data[i].ToString("X2").ToLower());
}
return builder.ToString();
}
protected internal static long GetUnixTimeStampSeconds(DateTime dt)
{
DateTime dateStart = new DateTime(1970, 1, 1, 0, 0, 0);
return Convert.ToInt64((dt - dateStart).TotalSeconds);
}
protected internal static byte[] Sha256(string data)
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
using (SHA256 mySHA256 = SHA256.Create())
{
byte[] hash = mySHA256.ComputeHash(bytes);
return hash;
}
}// 用户身份认证(JWT) - JavaScript
const axios = require('axios');
const crypto = require('crypto');
async function externalLogin(appId, appSecret, externalUserID, externalPersonaID = null, displayName = null, realmID = null) {
const url = "https://p.unity.cn/v1/login/external";
const payload = {
externalUserID,
externalPersonaID,
displayName,
realmID
};
const nonce = crypto.randomUUID();
const timestamp = Math.floor(Date.now() / 1000);
const signString = `${appId}:${appSecret}:${timestamp}:${nonce}`;
const token = crypto.createHash('sha256').update(signString, 'utf8').digest('hex');
const headers = {
'Content-Type': 'application/json',
'X-TIMESTAMP': String(timestamp),
'X-NONCE': nonce,
'X-APPID': appId,
'Authorization': `nonce ${token}`
};
const response = await axios.post(url, payload, { headers });
const data = response.data;
const jwt = data.personaAccessToken;
// 获取到的jwt,建议存储下来以便后续使用
return jwt;
}# 用户身份认证(JWT) - Python
import time
import uuid
import hashlib
import requests
def external_login(app_id, app_secret, external_user_id, external_persona_id=None, display_name=None, realm_id=None):
url = "https://p.unity.cn/v1/login/external"
payload = {
"externalUserID": external_user_id,
"externalPersonaID": external_persona_id,
"displayName": display_name,
"realmID": realm_id
}
nonce = str(uuid.uuid4())
timestamp = int(time.time())
sign_string = f"{app_id}:{app_secret}:{timestamp}:{nonce}"
token = hashlib.sha256(sign_string.encode("utf-8")).hexdigest()
headers = {
"Content-Type": "application/json",
"X-TIMESTAMP": str(timestamp),
"X-NONCE": nonce,
"X-APPID": app_id,
"Authorization": f"nonce {token}"
}
resp = requests.post(url, json=payload, headers=headers)
resp.raise_for_status()
data = resp.json()
jwt = data.get("personaAccessToken")
# 获取到的jwt,建议存储下来以便后续使用
return jwt:::
授权
获取用户的JWT身份认证信息后,在后续请求下列客户 API 的 Request Header 中增加如下 Header:
- Authorization: Bearer {jwt}
- X-TIMESTAMP: 当前时间戳
- X-NONCE: 随机生成的UUID
- X-APPID: 当前需要使用的 UOS APP 的 AppId
- X-NONCE-TOKEN: hexadecimal(sha256(appId + ":" + appSecret + ":" + X-TIMESTAMP + ":" + X-NONCE))
"Nonce Authorization"是一种身份验证机制,用于确保在通信中的请求是合法的。"Nonce" 是 "Number Used Once"(一次性数字)的缩写,是一个只能使用一次的随机或唯一值。Nonce 在身份验证中通常用于防止重放攻击。
// C# 使用示例
using System.Security.Cryptography;
using System.Text;
class Program
{
static Dictionary<string, string> SetNonce(string appId, string appSecret, string jwt)
{
string nonce = Guid.NewGuid().ToString();
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
string signString = $"{appId}:{appSecret}:{timestamp}:{nonce}";
string token = BitConverter.ToString(SHA256.HashData(Encoding.UTF8.GetBytes(signString))).Replace("-", "").ToLower();
return new Dictionary<string, string>
{
{ "Authorization", $"Bearer {jwt}" },
{ "Content-Type", "application/json" },
{ "X-TIMESTAMP", timestamp.ToString() },
{ "X-NONCE", nonce },
{ "X-NONCE-TOKEN", token },
{ "X-APPID", appId }
};
}
static async Task Main(string[] args)
{
// 替换 jwt 为获取到的 jwt
var headers = SetNonce(appId, appSecret, jwt);
using var httpClient = new HttpClient();
foreach (var header in headers)
{
httpClient.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
}
// 替换 url 为你需要请求的 url
var response = await httpClient.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
}# Python 使用示例
import time
import uuid
import hashlib
import requests
def set_nonce(app_id, app_secret, jwt):
nonce = str(uuid.uuid4())
timestamp = int(time.time())
sign_string = f"{app_id}:{app_secret}:{timestamp}:{nonce}"
token = hashlib.sha256(sign_string.encode("utf-8")).hexdigest()
return {
"Authorization": f"Bearer {jwt}",
"Content-Type": "application/json",
"X-TIMESTAMP": str(timestamp),
"X-NONCE": nonce,
"X-NONCE-TOKEN": token,
"X-APPID": app_id
}
# 替换 jwt 为获取到的 jwt
headers = set_nonce(app_id, app_secret, jwt)
# 替换 url 为你需要请求的 url
response = requests.get(url, headers=headers)// JavaScript 使用示例
const axios = require('axios');
const crypto = require('crypto');
function setNonce(appId, appSecret, jwt) {
const nonce = crypto.randomUUID();
const timestamp = Math.floor(Date.now() / 1000);
const signString = `${appId}:${appSecret}:${timestamp}:${nonce}`;
const token = crypto.createHash('sha256').update(signString, 'utf8').digest('hex');
return {
'Authorization': `Bearer ${jwt}`,
'Content-Type': 'application/json',
'X-TIMESTAMP': String(timestamp),
'X-NONCE': nonce,
'X-NONCE-TOKEN': token,
'X-APPID': appId
};
}
// 替换 jwt 为获取到的 jwt
const headers = setNonce(appId, appSecret, jwt);
// 替换 url 为你需要请求的 url
const response = await axios.get(url, { headers });客户端可供集成的 API 列表
Realm
Realm对应游戏中服务器的概念,支持服务器列表获取。
User
User对应游戏中用户账号的概念,支持实名认证验证。
Persona
Persona对应游戏服务器中角色的概念,支持角色信息获取,角色AccessToken刷新和角色选取。
Friends
支持好友关系管理、黑名单管理、好友信息管理和角色搜索等操作。
Report
支持在游戏中举报玩家。
AntiAddiction
支持检查当前玩家能否继续游戏、检查玩家充值消费金额是否受限和上报玩家充值金额。
Leaderboard
支持创获取单个或全部排行榜配置信息、获取成绩等操作。
Guild
支持公会配置、公会权限设置、查看公会列表和成员等操作。
Token
支持核销礼包码。
Achievement
支持解锁、更新、获取和同步玩家成就等操作。
Economy
支持搜索商品、虚拟购买以及背包资源管理等操作。
Inbox
支持邮件的查看、阅读、领取以及删除等操作。
Billboard
支持公告的查看、搜索操作。
BattlePass
支持角色战令的初始化、查看、升级、奖励领取等操作。
Quest
支持周期任务集同步、搜索、更新任务达成值、领取奖励等操作。