Skip to content

Latest commit

 

History

History
850 lines (713 loc) · 24.4 KB

1.Auth.md

File metadata and controls

850 lines (713 loc) · 24.4 KB

Login Authentication and Obtaining Streaming Credentials

Basic

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.

1.1 JWT

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'
  }
}

1.2 Digital Signature (signature)

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
}

1.3 Device Token

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' 
	  } 
  }
}

1.4 Code Challenge

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.

1.5 Random State

Generate a 64-byte random state using the crypto.randomBytes(64) method, returning a base64 string, such as:

lyzqu_x9HwhdP1udrPnJkLPZOQFe1VenZPMoqJkXwtrSxqFbrG9ZawTwc-A4mgvlPM5YpGzacVEKn7XmChvdWA

1.6 Sisu Authentication (SisuAuthentication)

Prerequisites:

  1. deviceToken (1.3)
  2. codeChallenge (1.4)
  3. 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

gamepass

1.7 authenticateUserUsingCode

Parameters:

  1. code and state from the redirect URL in 1.6
  2. verifier 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.


Midway Break

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.


1.8 Refresh Tokens

1.8.1 Refresh userToken

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"
  }

1.8.2 Re-obtain Device Token

Re-execute step three to get the deviceToken, which will be used in 1.8.3.

1.8.3 Obtain Sisu Authorization (SisuAuthorization, different from 1.6 Sisu Authentication)

Prepare parameters:

  1. Updated userToken from 1.8.1
  2. 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.

1.9 Local tokenStore Overview

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).

1.10 Obtain XSTS (Xbox Secure Token Service) Token (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=='
}

1.10.1 webToken

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"
		}
	  ]
	}
}

1.10.2 streamingToken

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.

1.11 Obtain Streaming Token (getStreamToken)

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

1.11.1 xhome

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 
}

1.11.2 xcloud

Divided into XGPU and free GPU types.

1.11.2.1 Game Pass Ultimate users (xgpuweb)

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'
}
1.11.2.2 Game Pass Free to Play users (xgpuwebf2p)

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.

Others

Token Validity Verification

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
}