获取登录URL,打开此微软官方URL,登录成功后会获得一个开头为ms-xal-开头的重定向地址,地址url中有用户的code和state,再通过challengecode机制获取最终登录token。
本地生成一个jwt,格式为:
{
raw: {
privateKey: PrivateKeyObject [KeyObject] {
[Symbol(kKeyType)]: 'private',
[Symbol(kAsymmetricKeyType)]: 'ec'
}
},
jwt: {
kty: 'EC',
x: 'u0Q-iJFkhKvLjwyAan2QKYXktaDq_gjJ85b9Y--Kz0E',
y: 'ZiropjuVfZANazlM6uCxUyfoLqVWMYNYh8FKWThWUFQ',
crv: 'P-256',
d: '54NUDZdCyjElg1WsT7ntXEGO-EB5ucxPqNzEF-Suvsw'
}
}针对准备要请求的URL生成一个签名,用于生成HTTP请求的签名头部,需要针对请求的url、请求body、JWT生成一个唯一的签名。
请求头部统一为:
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
signature: ''
}Signature是一个base64字符串:AAAAAQHaoOqdI6AAk7sE2qAqJ2gqR4p0mFfeoI2V6H8mHJOHPZB9Q6FYscBkjPijiXkasR0rVGUom4AhpY8giaWPzkKKiIQtxoscAQ==
方法调用示例:
const signature = this.sign('https://device.auth.xboxlive.com/device/authenticate', '', body, jwtKeys).toString('base64')基于NODEJS的算法如下,不同端使用的加密库或许不一样,但总体思路不变:
sign(url: string, authorizationToken: string, payload: string, jwtKeys: string) {
// 计算windows时间戳
const windowsTimestamp = (BigInt((Date.now() / 1000) | 0) + BigInt(11644473600)) * BigInt(10000000)
const pathAndQuery = new URL(url).pathname
const allocSize = 5 + 9 + 5 + pathAndQuery.length + 1 + authorizationToken.length + 1 + payload.length + 1
const buf = Buffer.alloc(allocSize)
buf.writeInt32BE(1)
buf.writeUInt8(0, 4)
buf.writeBigUInt64BE(windowsTimestamp, 5)
buf.writeUInt8(0, 13)
let offset = 14
Buffer.from('POST').copy(buf, offset)
buf.writeUInt8(0, offset+4)
offset = offset+4+1
Buffer.from(pathAndQuery).copy(buf, offset)
buf.writeUInt8(0, offset+pathAndQuery.length)
offset = offset+pathAndQuery.length+1
Buffer.from(authorizationToken).copy(buf, offset)
buf.writeUInt8(0, offset+authorizationToken.length)
offset = offset+authorizationToken.length+1
Buffer.from(payload).copy(buf, offset)
buf.writeUInt8(0, offset+payload.length)
offset = offset+payload.length+1
const signature = crypto.sign('SHA256', buf, { key: jwtKeys.raw.privateKey, dsaEncoding: 'ieee-p1363' })
const header = Buffer.alloc(signature.length + 12)
header.writeInt32BE(1)
header.writeBigUInt64BE(windowsTimestamp, 4)
Buffer.from(signature).copy(header, 12)
return header
}请求地址:https://device.auth.xboxlive.com/device/authenticate
请求方式:POST
Headers:
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
Signature: ''
}Body:
{
Properties: {
AuthMethod: 'ProofOfPossession',
Id: '{UUID}', // 动态UUID,必须用{}包起来
DeviceType: 'Android',
SerialNumber: '{UUID}', // 同上
Version: '15.0',
ProofKey: {
use: 'sig',
alg: 'ES256',
kty: 'EC',
crv: 'P-256',
x: '', // 获取1.1生成的jwtKeys.jwt.x
y: '' // 获取1.1生成的jwtKeys.jwt.y
}
},
RelyingParty: 'http://auth.xboxlive.com',
TokenType: 'JWT'
}注1:共4个变量,两个UUID,两个jwt的key值,其他均为固定值
注2:post请求的body需要进行JSON.stringify(payload)
Resonse example:
{
IssueInstant: '2024-05-08T03:24:04.3321393Z',
NotAfter: '2024-05-22T03:24:04.3321393Z',
Token: '...',
DisplayClaims: {
xdi: {
did: 'F700EB21352DC10A',
dcs: '1'
}
}
}算法如下:
// 生成一个长度为32字节的伪随机字节序列,然后将其转换为base64url编码
const code_verifier = Buffer.from(crypto.pseudoRandomBytes(32)).toString('base64url')
// 使用SHA-256哈希算法对code_verifier进行哈希运算,得到code_challenge
const code_challenge = crypto
.createHash("sha256")
.update(code_verifier)
.digest();返回一个对象,结构为:
{
value: code_challenge.toString('base64url'),
method: 'S256',
verifier: code_verifier,
}示例:
{
value: 'b0MZRUnZ12KEyT_hiVIbMEuArvWq8lwKx6pOCYheQ7Y',
method: 'S256',
verifier: 'aP-cHR9T0zxbXQXB3eUn-jQBBDd3IEP1GtzFluK7Q9E'
}注:CodeChallange可以在一次登录流程中重复使用
生成一个64字节的随机状态,依靠crypto.randomBytes(64)方法实现,返回一个base64字符串,如:
lyzqu_x9HwhdP1udrPnJkLPZOQFe1VenZPMoqJkXwtrSxqFbrG9ZawTwc-A4mgvlPM5YpGzacVEKn7XmChvdWA
需要前置准备:
- deviceToken(1.3)
- codeChallange(1.4)
- Random state(1.5)
请求地址:
https://sisu.xboxlive.com/authenticate
请求方式:POST
Headers(跟1.3 device token请求头一样):
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
Signature: ''
}body:
{
AppId: '000000004c20a908', // 固定值
TitleId: '328178078', // 固定值
RedirectUri: 'ms-xal-000000004c20a908://auth', // 固定值
DeviceToken: '...', // 取值 deviceToken.Token
Sandbox: 'RETAIL', // 固定值
TokenType: 'code', // 固定值
Offers: [ 'service::user.auth.xboxlive.com::MBI_SSL' ], // 固定值
Query: {
display: 'android_phone', // 固定值
code_challenge: codeChallange.value, // 取值codeChallange的value
code_challenge_method: codeChallange.method, // 取值codeChallange的method
state: '...' // 取值random state
}
}注:post请求的body需要进行
JSON.stringify(payload)
Response example:
{
data: {
MsaOauthRedirect: 'https://login.live.com/oauth20_authorize.srf?lw=1&fl=dob,easi2&xsup=1&display=android_phone&code_challenge=aqWJrErGS-ZDRpxdS72wdF1WwK5XaANJpt1Bc7ww6-c&code_challenge_method=S256&state=2GGNyr-A8rjazo5mfdx0i5fhoJNYL44zN7ej_cHshNXNv7Mlpb5kKHtgNcnpPx5Eb8wbQ&client_id=000000004C20A908&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_uri=ms-xal-000000004c20a908%3A%2F%2Fauth&nopa=2', // 可以登录的url,在这个页面登录后,会重定向到一个redirectUri
MsaRequestParameters: {}
},
headers: {
'cache-control': 'no-cache, no-store',
'content-length': '477',
'content-type': 'application/json',
'ms-cv': 's6rHgZo16kK9jn/ydwo13Q.0',
'x-content-type-options': 'nosniff',
'x-cluster-affinity': 'https://sisu.xboxlive.com',
'x-sessionid': '8ce2abd6f2174e939e4cc0250458b77e3', // 重要,这个sessionId可以保存下来备用
date: 'Wed, 08 May 2024 05:54:25 GMT',
connection: 'close'
}
}https://login.live.com/oauth20_authorize.srf?*登录重定向示意图,会重定向到一个ms-xal-000000004c20a908:的url,里面包含了一个重要的code和state字段,其中state的内容应该和1.5的随机state一致
示例:
ms-xal-000000004c20a908://auth/?code=M.C507_BAY.2.U.fb5422cc-f95b-e6e4-27f8-187001998353&state=Yk6s-4K1YnotDJ3Ngw4ArfBYY1Hoqq2L8VfzNtLiwUi7uyMc7ohmDaI8JpHQfBjFxLbmsXVcBjFRA8d5OXhrYA
参数:
- 1.6 中重定向的url中的
code和state - 1.4 CodeChallange 的
verifier
state的内容应该和1.5的随机state一致,不一致视为登录出错
请求地址:
https://login.live.com/oauth20_token.srf
请求方式:POST
Headers:
{
'Content-Type': 'application/x-www-form-urlencoded',
'Cache-Control': 'no-store, must-revalidate, no-cache'
}Body:
{
client_id: '000000004c20a908', // 固定值,跟1.6的appid一致
code: 'M.C507_BAY.2.U.0ca196df-601a-8f91-e228-d1cebb6949e7', // 1.6重定向url里的code
code_verifier: 'vFAUzX5900dqSjX0mEwKxScEemm_Iqjh5QkTpoursYc', // 1.4的challengeCode.verifier
grant_type: 'authorization_code', // 固定值
redirect_uri: 'ms-xal-000000004c20a908://auth', // 固定值
scope: 'service::user.auth.xboxlive.com::MBI_SSL' // 固定值
}注:body需要通过
new URLSearchParams(payload).toString()转成如下格式:
client_id=000000004c20a908&code=M.C507_BAY.2.U.0ca196df-601a-8f91-e228-d1cebb6949e7&code_verifier=vFAUzX5900dqSjX0mEwKxScEemm_Iqjh5QkTpoursYc&grant_type=authorization_code&redirect_uri=ms-xal-000000004c20a908%3A%2F%2Fauth&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL
Response:
{
token_type: 'bearer',
expires_in: 86400,
scope: 'service::user.auth.xboxlive.com::MBI_SSL',
access_token: 'EwAIA+pvB....',
refresh_token: 'M.C507_BAY.0.U.-CpGUu5WuMEpuVG...', // 用于刷新userToken
user_id: '8397cf3feb56a78b'
}这一步返回的内容作为userToken保存到本地缓存中。
到了这一步本地缓存已经有以下token,这些token都是后续寻找终端和串流需要用到的认证信息。
{
"userToken": { // 1.7生成
"token_type": "bearer",
"expires_in": 86400,
"scope": "service::user.auth.xboxlive.com::MBI_SSL",
"access_token": "EwAIA+pvBAAUKods....",
"refresh_token": "M.C507_BAY.0.U.....",
"user_id": "8397cf3feb56a78b",
"expires_on": "2024-05-09T07:01:37.237Z"
},
"jwtKeys": {// 1.1 生成
"raw": {
"privateKey": {}
},
"jwt": {
"kty": "EC",
"x": "xMNk-xsKxVpCL4Zg10WvWhaprY5qqqyQ6nGwTaMMNiw",
"y": "MpJyurT3Q10H-nfUeYUcwqicXunD-njwuFrJjMMOZ1A",
"crv": "P-256",
"d": "66sH6uHyjA6Q8mGq7olbLW8nVTQpDV3x1h-_ib8XCEw"
}
}
}但这些信息还不够,还缺少sisuToken和一个刷新token的步骤
该过程和1.7使用code换取userToken的过程很相似,区别在body的参数,刷新userToken需要使用1.7返回的refresh_token再进行一次请求,刷新userToken。
请求地址:
https://login.live.com/oauth20_token.srf
请求方式:POST
Headers:
{
'Content-Type': 'application/x-www-form-urlencoded',
'Cache-Control': 'no-store, must-revalidate, no-cache',
}Body:
{
'client_id': '000000004c20a908', // 固定值,跟第六节的appid一致
'grant_type': 'refresh_token', // 固定值
'refresh_token': userToken.data.refresh_token, // 第七步的refresh_token
'scope': 'service::user.auth.xboxlive.com::MBI_SSL' // 固定值
}Response:
{
"token_type": "bearer",
"expires_in": 86400,
"scope": "service::user.auth.xboxlive.com::MBI_SSL",
"access_token": "EwAIA+pvBAAUKods63Ys1f...",
"refresh_token": "M.C507_BAY.0.U.-Cp...",
"user_id": "8397cf3feb56a78b",
"expires_on": "2024-05-09T07:51:56.371Z"
}重新执行第三步的getDeviceToken,用于1.8.3
准备参数:
- 1.8.1更新的userToken
- 1.8.2重新获取的deviceToken
请求地址:
https://sisu.xboxlive.com/authorize
请求方式:POST
Headers(跟device token请求头一样):
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
Signature: ''
}Body:
{
// AccessToken: 't='+userToken.data.access_token,
AccessToken: 't=EwAIA+pvBAAUKods6...', // 't=' + (8.1步中userToken.access_token)
AppId: '000000004c20a908', // 固定值
DeviceToken: 'eyJhbGciOiJSU0...', // 取8.2的deviceToken.token
Sandbox: 'RETAIL', // 固定值
SiteName: 'user.auth.xboxlive.com', // 固定值
UseModernGamertag: true, // 固定值
ProofKey: {
use: 'sig',
alg: 'ES256',
kty: 'EC',
crv: 'P-256',
x: 'wCUvXHBXMu6pkwR96IFyxJ--V6CGVzLvsx462ABTK7o', // 取jwtKeys.jwt.x,跟SisuAuthentication使用相同的jwtKeys
y: 'H5IjTzffUbuAyJO63hnlJBejicqrZniJhunqkjwigqY' // 取jwtKeys.jwt.y
}
}body需要进行JSON.stringify再进行post请求
Response:
{
"DeviceToken": "eyJhbGciOiJSU0EtT0FFUCIs...",
"TitleToken": {
"DisplayClaims": {
"xti": {
"tid": "1016898439"
}
},
"IssueInstant": "2024-05-08T08:05:46.4103849Z",
"NotAfter": "2024-05-22T08:05:46.4103849Z",
"Token": "eyJhbGciOiJSU0EtT0FFUCIsImV..."
},
"UserToken": {
"DisplayClaims": {
"xui": [
{
"uhs": "7333323338208403105"
}
]
},
"IssueInstant": "2024-05-08T08:05:46.3971359Z",
"NotAfter": "2024-05-12T08:05:46.3971359Z",
"Token": "eyJhbGciOiJSU0EtT0FFUC..."
},
"AuthorizationToken": {
"DisplayClaims": {
"xui": [
{
"gtg": "Geocld",
"xid": "2535466257699046",
"uhs": "7333323338208403105",
"mgt": "Geocld",
"umg": "Geocld",
"agg": "Adult",
"usr": "195 234",
"prv": "184 185 186 187 188 190 191 193 196 198 199 200 201 203 204 205 206 208 211 217 220 224 227 228 235 238 245 247 249 252 254 255"
}
]
},
"IssueInstant": "2024-05-08T08:05:46.7150743Z",
"NotAfter": "2024-05-09T00:05:46.7150743Z",
"Token": "eyJlbmMiOiJBMTI4..."
},
"WebPage": "https://sisu.xboxlive.com/client/v33/000000004c20a908/view/index.html",
"Sandbox": "RETAIL",
"UseModernGamertag": true,
"Flow": ""
}至此获得了sisu的全部token,将token保存在本地,后续寻找控制台和串流均需要使用。
本地缓存的token一共包含三个对象:userToken、sisuToken、jwtKeys
{
"userToken": {
"token_type": "bearer",
"expires_in": 86400,
"scope": "service::user.auth.xboxlive.com::MBI_SSL",
"access_token": "EwAIA+pvBAAU...xNnxE0GwI=",
"refresh_token": "M.C507_SN1.0.U.-Chb79D...HB",
"user_id": "8397cf3feb56a78b",
"expires_on": "2024-05-09T08:14:49.715Z"
},
"sisuToken": {
"DeviceToken": "eyJhbGciOiJSU0Et...TXgH8Ox-zA",
"TitleToken": {
"DisplayClaims": {
"xti": {
"tid": "1016898439"
}
},
"IssueInstant": "2024-05-08T08:14:49.4327776Z",
"NotAfter": "2024-05-22T08:14:49.4327776Z",
"Token": "eyJhbGciOiJSU0EtT0F....c3rRN8CJ6GdbaBikPeUJ-g"
},
"UserToken": {
"DisplayClaims": {
"xui": [
{
"uhs": "8920341548513246887"
}
]
},
"IssueInstant": "2024-05-08T08:14:49.4209938Z",
"NotAfter": "2024-05-12T08:14:49.4209938Z",
"Token": "eyJhbGciOiJSU0EtT0FF..."
},
"AuthorizationToken": {
"DisplayClaims": {
"xui": [
{
"gtg": "Geocld",
"xid": "2535466257699046",
"uhs": "8920341548513246887",
"mgt": "Geocld",
"umg": "Geocld",
"agg": "Adult",
"usr": "195 234",
"prv": "184 185 186 187 188 190 191 193 196 198 199 200 201 203 204 205 206 208 211 217 220 224 227 228 235 238 245 247 249 252 254 255"
}
]
},
"IssueInstant": "2024-05-08T08:14:49.561186Z",
"NotAfter": "2024-05-09T00:14:49.561186Z",
"Token": "eyJlbmMiOiJBMTI4Q0JD..."
},
"WebPage": "https://sisu.xboxlive.com/client/v33/000000004c20a908/view/index.html",
"Sandbox": "RETAIL",
"UseModernGamertag": true,
"Flow": ""
},
"jwtKeys": {
"raw": {
"privateKey": {}
},
"jwt": {
"kty": "EC",
"x": "mj-77bl7yPfQFW18kESQzxNR4t0-pRiKjv1n9ZnzYFE",
"y": "0DenZBXJfO7Cb4mcrAejSdjVqirDtM8XugIUb5qRWGo",
"crv": "P-256",
"d": "ywdsGcxoA89rp50R8fqwHusV-0-Ili9bd5jhqpNNd_E"
}
}
}到这里本地保存的token已经准备就绪,用的最多的就是其中的sisuToken,sisuToken可以帮助我们获得串流需要的token及web api的token( 10.1 doXstsAuthorization )。
利用sisuToken可以进一步获得webToken和streamingToken,两个token分别作用:
- webToken: 获取终端列表、成就、好友列表等web UI相关的接口
- streamingToken:串流相关参数和凭证,进行webrtc协商时需要带上相关token(信令服务器协商)
两个token均为请求同一个restful API地址,区别在body的参数不同,均使用到tokenstore的sisuToken,即8.3返回的token
请求地址:https://xsts.auth.xboxlive.com/xsts/authorize
请求请示:POST
headers:
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
Signature: 'AAAAAQHaoSi1...9N2w=='
}body:
{
Properties: {
SandboxId: 'RETAIL', // 固定值
DeviceToken: sisuToken.DeviceToken,
TitleToken: sisuToken.TitleToken.Token,
UserTokens: [sisuToken.UserToken.Token]
},
RelyingParty: 'http://xboxlive.com',// 固定值
TokenType: 'JWT'// 固定值
}body需进行JSON.stringify
response:
{
IssueInstant: '2024-05-08T09:26:59.4788867Z',
NotAfter: '2024-05-09T01:26:59.4788867Z',
Token: 'eyJlbmMiOi...UT5BPqexEDHfhwk2upiNzw', // 这个就是xsts令牌
DisplayClaims: {
"xui": [
{
"gtg": "Geocld",
"xid": "2535466257699046",
"uhs": "8920341548513246887",
"agg": "Adult",
"usr": "195 234",
"prv": "184 185 186 187 188 190 191 193 196 198 199 200 201 203 204 205 206 208 211 217 220 224 227 228 235 238 245 247 249 252 254 255"
}
]
}
}body:
{
Properties: {
SandboxId: 'RETAIL', // 固定值
DeviceToken: sisuToken.DeviceToken,
TitleToken: sisuToken.TitleToken.Token,
UserTokens: [sisuToken.UserToken.Token]
},
RelyingParty: 'http://gssv.xboxlive.com/',// 固定值
TokenType: 'JWT'// 固定值
}body需进行JSON.stringify
response:
{
IssueInstant: '2024-05-08T09:26:59.4788867Z',
NotAfter: '2024-05-09T01:26:59.4788867Z',
Token: 'eyJlbmMiOi...UT5BPqexEDHfhwk2upiNzw', // 这个就是xsts令牌
DisplayClaims: {
"xui": [
{"uhs":"8920341548513246887"}
]
}
}返回值我们称之为xstsToken,用于1.11获取串流令牌。
只是通过1.10.2获取的xsts令牌(streamingToken)还不能进入webrtc的认证流程,还需要通过xsts令牌进一步获得xhome(本地串流)、xcloud(云游戏)的令牌。两个获取方式的接口,headers和body基本相同。
前置依赖:1.10.2获取的xsts返回值
url: https://xhome.gssv-play-prod.xboxlive.com
headers:
{
'Content-Type': 'application/json', // 固定值
'Cache-Control': 'no-store, must-revalidate, no-cache', // 固定值
'x-gssv-client': 'XboxComBrowser', // 固定值
'Content-Length': 1987 // body进行stringify后的长度:JSON.stringify(payload).length
}body:
{
'token': 'eyJhbGc...IsInR5cC', // xsts Token值,即
'offeringId': 'xhome', // 固定值
}body需要进行JSON.stringify
response:
{
"offeringSettings": {
"allowRegionSelection": false,
"regions": [{ // 可以正常提供服务的地区,在后续发起串流请求时需要使用有效的url
"name": "WestUS2",
"baseUri": "https://wus2.core.gssv-play-prodxhome.xboxlive.com",
"networkTestHostname": null,
"isDefault": false,
"systemUpdateGroups": null,
"fallbackPriority": -1
}, {
"name": "EastUs",
"baseUri": "https://eus.core.gssv-play-prodxhome.xboxlive.com",
"networkTestHostname": null,
"isDefault": false,
"systemUpdateGroups": null,
"fallbackPriority": -1
}, {
"name": "UkSouth",
"baseUri": "https://uks.core.gssv-play-prodxhome.xboxlive.com",
"networkTestHostname": null,
"isDefault": true,
"systemUpdateGroups": null,
"fallbackPriority": -1
}],
"selectableServerTypes": null,
"clientCloudSettings": {
"Environments": [{
"Name": "Int",
"AuthBaseUri": "https://gssv-auth-intxhome.xboxlive.com"
}, {
"Name": "Prod",
"AuthBaseUri": null
}, {
"Name": "Test",
"AuthBaseUri": "https://gssv-auth-testxhome.xboxlive.com"
}]
}
},
"market": "CN",
"gsToken": "eyJhbGciO...8TYenzybtQJovSdNFJ25c", // 当前串流凭证,重要字段,后续串流协商接口都需要带上此token
"tokenType": "bearer",
"durationInSeconds": 14400 // 有效时间,可用于有效期验证,单位为秒,返回大概率是14400秒左右,折合4个小时,即微软串流token的有效时间为4小时
}分为XGPU和免费GPU两种类型
url: https://xgpuweb.gssv-play-prod.xboxlive.com
headers:
{
'Content-Type': 'application/json', // 固定值
'Cache-Control': 'no-store, must-revalidate, no-cache', // 固定值
'x-gssv-client': 'XboxComBrowser', // 固定值
'Content-Length': 1987 // body进行stringify后的长度:JSON.stringify(payload).length
}body:
{
'token': 'eyJhbGc...IsInR5cC', // xsts Token值,即
'offeringId': 'xgpuweb', // 固定值
}body需要进行JSON.stringify
response: (跟xhome类似)
{
{
offeringSettings: [Object],
market: 'JP',
gsToken: 'eyJhbGciOiJS...o1k',
tokenType: 'bearer',
durationInSeconds: 14400
}
}
错误返回:
{
statuscode: 403,
headers: {
'content-length': '61',
connection: 'close',
'content-type': 'application/json',
date: 'Thu, 09 May 2024 09:45:28 GMT',
'ms-cv': 'WmSI8f1Xs0yjhvQATrMMtg.1.0',
'x-proxy-success': 'true,true'
},
body: '{"code":"OfferingAccessDenied","statusCode":403,"message":""}',
message: 'Error fetching xgpuweb.gssv-play-prod.xboxlive.com/v2/login/user'
}url: https://xgpuwebf2p.gssv-play-prod.xboxlive.com
header和body同上,只需把body的offeringId字段改为xgpuwebf2p
参数和返回和标准XGPU接口一致。
10.1节返回的串流token,数据结构如下:
{
"offeringSettings": {
// ...
"gsToken": "eyJhbGciO...8TYenzybtQJovSdNFJ25c", // 当前串流凭证,重要字段,后续串流协商接口都需要带上此token
"durationInSeconds": 14400 // 有效时间,可用于有效期验证,单位为秒,返回大概率是14400秒左右,折合4个小时,即微软串流token的有效时间为4小时
}其中的durationInSeconds表示gsToken的有效时间,单位为秒,返回大概率是14400秒左右,折合4个小时,即微软串流token的有效时间为4小时。保存gsToken时也保存对应的durationInSeconds,可以计算出超时时间,再使用当前时间比较即可知道token是否失效,以下为greenlight的token验证机制:
calculateSecondsLeft(date: Date){
const expiresOn = date
const currentDate = new Date()
return Math.floor((expiresOn.getTime() - currentDate.getTime()) / 1000)
}
getSecondsValid(){
// _objectCreateTime看做是生成token时的时间点
// durationInSeconds注意要转为ms
const expireTime = this._objectCreateTime + (this.data.durationInSeconds*1000)
if(expireTime){
return this.calculateSecondsLeft(new Date(expireTime))
}
return 0
}