Obtain the login URL, open this official Microsoft URL, and after a successful login, you will receive a redirect address starting with ms-xal-
. The URL contains the user's code
and state
. Then, use the challenge code mechanism to obtain the final login token.
Generate a JWT locally in the following format:
{
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'
}
}
Generate a signature for the URL you are about to request. This signature is used to create the HTTP request signature header. You need to generate a unique signature for the request URL, request body, and JWT.
The request header is unified as:
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
signature: ''
}
The signature is a base64 string like this: AAAAAQHaoOqdI6AAk7sE2qAqJ2gqR4p0mFfeoI2V6H8mHJOHPZB9Q6FYscBkjPijiXkasR0rVGUom4AhpY8giaWPzkKKiIQtxoscAQ==
Example method call:
const signature = this.sign('https://device.auth.xboxlive.com/device/authenticate', '', body, jwtKeys).toString('base64')
The algorithm based on NODEJS is as follows. The encryption library used may differ across platforms, but the general idea remains the same:
sign(url: string, authorizationToken: string, payload: string, jwtKeys: string) {
// calculate windowsTimestamp
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
}
Request URL: https://device.auth.xboxlive.com/device/authenticate
Request Method: POST
Headers:
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
Signature: ''
}
Body:
{
Properties: {
AuthMethod: 'ProofOfPossession',
Id: '{UUID}', // dynamic UUID,Must use {}wrap up
DeviceType: 'Android',
SerialNumber: '{UUID}', // Above
Version: '15.0',
ProofKey: {
use: 'sig',
alg: 'ES256',
kty: 'EC',
crv: 'P-256',
x: '', // Obtain 1.1Generated jwtKeys.jwt.x
y: '' // Obtain 1.1Generated jwtKeys.jwt.y
}
},
RelyingParty: 'http://auth.xboxlive.com',
TokenType: 'JWT'
}
Note 1: There are 4 variables in total, two UUIDs, and two JWT key values; the rest are fixed values.
Note 2: The body of the POST request needs to be JSON.stringify(payload)
.
Response example:
{
IssueInstant: '2024-05-08T03:24:04.3321393Z',
NotAfter: '2024-05-22T03:24:04.3321393Z',
Token: '...',
DisplayClaims: {
xdi: {
did: 'F700EB21352DC10A',
dcs: '1'
}
}
}
Algorithm as follows:
// Generate a length to 32Byte pseudo -random byte sequence ,Then convert it to base64url coding
const code_verifier = Buffer.from(crypto.pseudoRandomBytes(32)).toString('base64url')
// use SHA-256Hash algorithm pair code_verifier Has operation ,get code_challenge
const code_challenge = crypto
.createHash("sha256")
.update(code_verifier)
.digest();
Returns an object structured as:
{
value: code_challenge.toString('base64url'),
method: 'S256',
verifier: code_verifier,
}
Example:
{
value: 'b0MZRUnZ12KEyT_hiVIbMEuArvWq8lwKx6pOCYheQ7Y',
method: 'S256',
verifier: 'aP-cHR9T0zxbXQXB3eUn-jQBBDd3IEP1GtzFluK7Q9E'
}
Note: The Code Challenge can be reused in a single login process.
Generate a 64-byte random state using the crypto.randomBytes(64)
method, returning a base64 string, such as:
lyzqu_x9HwhdP1udrPnJkLPZOQFe1VenZPMoqJkXwtrSxqFbrG9ZawTwc-A4mgvlPM5YpGzacVEKn7XmChvdWA
Prerequisites:
- deviceToken (1.3)
- codeChallenge (1.4)
- Random state (1.5)
Request URL:
https://sisu.xboxlive.com/authenticate
Request Method: POST
Headers (same as 1.3 device token request headers):
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
Signature: ''
}
Body:
{
AppId: '000000004c20a908', // Fixed value
TitleId: '328178078', // Fixed value
RedirectUri: 'ms-xal-000000004c20a908://auth', // Fixed value
DeviceToken: '...', // Value deviceToken.Token
Sandbox: 'RETAIL', // Fixed value
TokenType: 'code', // Fixed value
Offers: [ 'service::user.auth.xboxlive.com::MBI_SSL' ], // Fixed value
Query: {
display: 'android_phone', // Fixed value
code_challenge: codeChallange.value, // Value codeChallangeof value
code_challenge_method: codeChallange.method, // Value codeChallangeof method
state: '...' // Value random state
}
}
Note: The body of the POST request needs to be
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', // Can log in of url,After logging in on this page ,Will redirection to one 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', // important ,this sessionIdCan be saved for later use
date: 'Wed, 08 May 2024 05:54:25 GMT',
connection: 'close'
}
}
The https://login.live.com/oauth20_authorize.srf?*
login redirect illustration will redirect to a URL starting with ms-xal-000000004c20a908:
, which contains an important code
and state
field. The content of the state should match the random state from 1.5.
Example:
ms-xal-000000004c20a908://auth/?code=M.C507_BAY.2.U.fb5422cc-f95b-e6e4-27f8-187001998353&state=Yk6s-4K1YnotDJ3Ngw4ArfBYY1Hoqq2L8VfzNtLiwUi7uyMc7ohmDaI8JpHQfBjFxLbmsXVcBjFRA8d5OXhrYA
Parameters:
code
andstate
from the redirect URL in 1.6verifier
from 1.4 Code Challenge
The content of the state should match the random state from 1.5. If it does not, the login is considered to have failed.
Request URL:
https://login.live.com/oauth20_token.srf
Request Method: POST
Headers:
{
'Content-Type': 'application/x-www-form-urlencoded',
'Cache-Control': 'no-store, must-revalidate, no-cache'
}
Body:
{
client_id: '000000004c20a908', // Fixed value ,and 1.6of appidConsistent
code: 'M.C507_BAY.2.U.0ca196df-601a-8f91-e228-d1cebb6949e7', // 1.6Redirect urlinside of code
code_verifier: 'vFAUzX5900dqSjX0mEwKxScEemm_Iqjh5QkTpoursYc', // 1.4of challengeCode.verifier
grant_type: 'authorization_code', // Fixed value
redirect_uri: 'ms-xal-000000004c20a908://auth', // Fixed value
scope: 'service::user.auth.xboxlive.com::MBI_SSL' // Fixed value
}
Note: The body needs to be converted to the following format using
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...', // Used to refresh userToken
user_id: '8397cf3feb56a78b'
}
The content returned at this step should be saved as a userToken
in local storage.
At this point, the local storage should contain the following tokens, which are required for subsequent terminal searches and streaming authentication.
{
"userToken": { // 1.7generate
"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 generate
"raw": {
"privateKey": {}
},
"jwt": {
"kty": "EC",
"x": "xMNk-xsKxVpCL4Zg10WvWhaprY5qqqyQ6nGwTaMMNiw",
"y": "MpJyurT3Q10H-nfUeYUcwqicXunD-njwuFrJjMMOZ1A",
"crv": "P-256",
"d": "66sH6uHyjA6Q8mGq7olbLW8nVTQpDV3x1h-_ib8XCEw"
}
}
}
However, this information is still insufficient. We still need the sisuToken and a token refresh step.
This process is similar to the process of exchanging the code for the userToken in 1.7. The difference lies in the body parameters: refreshing the userToken requires using the refresh_token returned in 1.7 to make another request to refresh the userToken.
Request URL:
https://login.live.com/oauth20_token.srf
Request Method: POST
Headers:
{
'Content-Type': 'application/x-www-form-urlencoded',
'Cache-Control': 'no-store, must-revalidate, no-cache',
}
Body:
{
'client_id': '000000004c20a908', // Fixed value ,and Section 6 of appidConsistent
'grant_type': 'refresh_token', // Fixed value
'refresh_token': userToken.data.refresh_token, // Step 7 of refresh_token
'scope': 'service::user.auth.xboxlive.com::MBI_SSL' // Fixed value
}
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"
}
Re-execute step three to get the deviceToken, which will be used in 1.8.3.
Prepare parameters:
- Updated userToken from 1.8.1
- Re-obtained deviceToken from 1.8.2
Request URL:
https://sisu.xboxlive.com/authorize
Request Method: POST
Headers (same as device token request headers):
{
'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.1Step -by -step userToken.access_token)
AppId: '000000004c20a908', // Fixed value
DeviceToken: 'eyJhbGciOiJSU0...', // Pick 8.2of deviceToken.token
Sandbox: 'RETAIL', // Fixed value
SiteName: 'user.auth.xboxlive.com', // Fixed value
UseModernGamertag: true, // Fixed value
ProofKey: {
use: 'sig',
alg: 'ES256',
kty: 'EC',
crv: 'P-256',
x: 'wCUvXHBXMu6pkwR96IFyxJ--V6CGVzLvsx462ABTK7o', // Pick jwtKeys.jwt.x,and SisuAuthenticationuse same of jwtKeys
y: 'H5IjTzffUbuAyJO63hnlJBejicqrZniJhunqkjwigqY' // Pick jwtKeys.jwt.y
}
}
The body needs to be JSON.stringify before making the POST request.
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": ""
}
At this point, all sisu tokens have been obtained. Save the tokens locally; they will be needed for subsequent console searches and streaming.
The local storage should contain three objects: userToken, sisuToken, and 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"
}
}
}
At this point, the local tokens are ready. The most frequently used token is the sisuToken
, which can help obtain the tokens needed for streaming and web API tokens (10.1 doXstsAuthorization).
Using the sisuToken, you can further obtain the webToken and streamingToken. These two tokens serve different purposes:
- webToken: Used for web UI-related interfaces such as obtaining terminal lists, achievements, and friend lists.
- streamingToken: Used for streaming-related parameters and credentials, required during WebRTC negotiation (signaling server negotiation).
Both tokens are requested from the same RESTful API address, with the difference being in the body parameters. Both use the sisuToken from the tokenstore, i.e., the token returned in 8.3.
Request URL: https://xsts.auth.xboxlive.com/xsts/authorize
Request Method: POST
Headers:
{
'x-xbl-contract-version': '1',
'Cache-Control': 'no-store, must-revalidate, no-cache',
Signature: 'AAAAAQHaoSi1...9N2w=='
}
Body:
{
Properties: {
SandboxId: 'RETAIL', // Fixed value
DeviceToken: sisuToken.DeviceToken,
TitleToken: sisuToken.TitleToken.Token,
UserTokens: [sisuToken.UserToken.Token]
},
RelyingParty: 'http://xboxlive.com',// Fixed value
TokenType: 'JWT'// Fixed value
}
The body needs to be JSON.stringify.
Response:
{
IssueInstant: '2024-05-08T09:26:59.4788867Z',
NotAfter: '2024-05-09T01:26:59.4788867Z',
Token: 'eyJlbmMiOi...UT5BPqexEDHfhwk2upiNzw', // this that is xstsToken
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', // Fixed value
DeviceToken: sisuToken.DeviceToken,
TitleToken: sisuToken.TitleToken.Token,
UserTokens: [sisuToken.UserToken.Token]
},
RelyingParty: 'http://gssv.xboxlive.com/',// Fixed value
TokenType: 'JWT'// Fixed value
}
The body needs to be JSON.stringify.
Response:
{
IssueInstant: '2024-05-08T09:26:59.4788867Z',
NotAfter: '2024-05-09T01:26:59.4788867Z',
Token: 'eyJlbmMiOi...UT5BPqexEDHfhwk2upiNzw', // this that is xstsToken
DisplayClaims: {
"xui": [
{"uhs":"8920341548513246887"}
]
}
}
The returned value is referred to as the xstsToken, used in 1.11 to obtain the streaming token.
The xstsToken (streamingToken) obtained in 1.10.2 is not sufficient for entering the WebRTC authentication process. You need to use the xstsToken to further obtain the tokens for xhome (local streaming) and xcloud (cloud gaming). The interfaces, headers, and body for obtaining these tokens are essentially the same.
Prerequisite: xstsToken obtained in 1.10.2
URL: https://xhome.gssv-play-prod.xboxlive.com
Headers:
{
'Content-Type': 'application/json', // Fixed value
'Cache-Control': 'no-store, must-revalidate, no-cache', // Fixed value
'x-gssv-client': 'XboxComBrowser', // Fixed value
'Content-Length': 1987 // bodyconduct stringifyback of length :JSON.stringify(payload).length
}
Body:
{
'token': 'eyJhbGc...IsInR5cC', // xsts Tokenvalue ,Right now
'offeringId': 'xhome', // Fixed value
}
The body needs to be JSON.stringify.
Response:
{
"offeringSettings": {
"allowRegionSelection": false,
"regions": [{ // Can provide services normally of area ,exist back Continue to send streaming requests hour need use efficient of 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", // Current streaming voucher ,important Field ,Subsequent streaming negotiation interfaces need to be brought here token
"tokenType": "bearer",
"durationInSeconds": 14400 // Effective time ,Can be used for validity verification ,The unit is second ,The probability of returning is 14400About seconds ,Discount 4Hours ,Right now Microsoft Streaming tokenof Effective time for 4Hour
}
Divided into XGPU and free GPU types.
URL: https://xgpuweb.gssv-play-prod.xboxlive.com
Headers:
{
'Content-Type': 'application/json', // Fixed value
'Cache-Control': 'no-store, must-revalidate, no-cache', // Fixed value
'x-gssv-client': 'XboxComBrowser', // Fixed value
'Content-Length': 1987 // bodyconduct stringifyback of length :JSON.stringify(payload).length
}
Body:
{
'token': 'eyJhbGc...IsInR5cC', // xsts Tokenvalue ,Right now
'offeringId': 'xgpuweb', // Fixed value
}
The body needs to be JSON.stringify.
Response: (similar to xhome)
{
offeringSettings: [Object],
market: 'JP',
gsToken: 'eyJhbGciOiJS...o1k',
tokenType: 'bearer',
durationInSeconds: 14400
}
}
Error response:
{
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
Headers and body are the same as above, only the offeringId
field in the body needs to be changed to xgpuwebf2p
.
Parameters and responses are consistent with the standard XGPU interface.
The streaming token returned in section 10.1 has the following data structure:
{
"offeringSettings": {
// ...
"gsToken": "eyJhbGciO...8TYenzybtQJovSdNFJ25c", // Current streaming voucher ,important Field ,Subsequent streaming negotiation interfaces need to be brought here token
"durationInSeconds": 14400 // Effective time ,Can be used for validity verification ,The unit is second ,The probability of returning is 14400About seconds ,Discount 4Hours ,Right now Microsoft Streaming tokenof Effective time for 4Hour
}
The durationInSeconds
field indicates the validity period of the gsToken in seconds. The returned value is most likely around 14400 seconds, equivalent to 4 hours, meaning the Microsoft streaming token is valid for 4 hours. When saving the gsToken, also save the corresponding durationInSeconds. You can then calculate the expiration time and compare it with the current time to determine if the token has expired. Below is the token validation mechanism for greenlight:
calculateSecondsLeft(date: Date){
const expiresOn = date
const currentDate = new Date()
return Math.floor((expiresOn.getTime() - currentDate.getTime()) / 1000)
}
getSecondsValid(){
// _objectCreateTimeLook at generate tokenhour of hour Interval point
// durationInSecondsPay attention to ms
const expireTime = this._objectCreateTime + (this.data.durationInSeconds*1000)
if(expireTime){
return this.calculateSecondsLeft(new Date(expireTime))
}
return 0
}