halo-infinite-api 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,21 @@
1
1
  # Halo Infinite API
2
2
 
3
- This is a simple typescript wrapper around 343's official Halo Infinite API (the same API that powers both the game and [halowaypoint.com](https://www.halowaypoint.com/)).I based it off the work of the now mysteriously deleted C# Grunt API (a defunct fork of which remains [here](https://github.com/seth-skocelas/grunt/)).
3
+ This is a simple typescript wrapper around 343's official Halo Infinite API (the same API that powers both the game and [halowaypoint.com](https://www.halowaypoint.com/)). I based it off the work of the now mysteriously deleted C# Grunt API (a defunct fork of which remains [here](https://github.com/seth-skocelas/grunt/)).
4
4
 
5
5
  The package is currently limited to the endpoints I've needed to use in other projects, however I do take requests (create an [issue](/issues)) and I welcome [PRs to extend the functionality](/pulls).
6
6
 
7
7
  ### Currently Supported Endpoints
8
8
 
9
9
  - GET https://profile.svc.halowaypoint.com/users/{gamerTag}
10
+ - GET https://profile.svc.halowaypoint.com/users?xuids={xuids}
10
11
  - GET https://skill.svc.halowaypoint.com/hi/playlist/{playlistId}/csrs?players={playerIds}
11
12
  - GET https://gamecms-hacs.svc.halowaypoint.com/hi/multiplayer/file/playlists/assets/{playlistId}.json
12
13
  - GET https://halostats.svc.halowaypoint.com/hi/playlist/{playlistId}/csrs?players={playerIds}
13
14
  - GET https://halostats.svc.halowaypoint.com/hi/players/xuid({playerId})/matches
14
15
  - GET https://skill.svc.halowaypoint.com/hi/matches/{matchId}/skill
16
+ - GET https://halostats.svc.halowaypoint.com/hi/matches/{matchId}/stats
17
+ - GET https://discovery-infiniteugc.svc.halowaypoint.com/hi/{assetType}/{assetId}
18
+ - GET https://discovery-infiniteugc.svc.halowaypoint.com/hi/{assetType}/{assetId}/versions/{versionId}
15
19
 
16
20
  ### Getting Started
17
21
 
@@ -19,46 +23,42 @@ The core requirement to use the endpoints in the library is to have a Spartan to
19
23
 
20
24
  > **⚠️ WARNING**
21
25
  >
22
- > The Spartan token is associated with _your identity_ and _your account_. **Do not share it** with anyone, under any circumstances. The API wrapper does not explicitly store it anywhere. It's your responsibility to make sure that it's secure and not available to anyone else.
26
+ > The Spartan token is associated with _your identity_ and _your account_. **Do not share it** with anyone, under any circumstances. The API wrapper does not explicitly store it anywhere (though you can configure it to store it somewhere of your choosing). It's your responsibility to make sure that it's secure and not available to anyone else.
23
27
 
24
- If you want to automatically generate the Spartan token, you can do so with the help of this package without having to worry about doing any of the REST API calls yourself. Before you get started, make sure that you [register an Azure Active Directory application](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app). You will need it in order to log in with your Microsoft account, that will be used to generate the token. Because this is just for you, you can use `https://localhost` as the redirect URI when you create the application, unless you're thinking of productizing whatever you're building.
28
+ This library does not provide a way to perform the first step of generating a spartan token, which is to get an Oauth2 access token from Microsoft. Microsoft provides a great set of npm packages for this purpose, [@azure/msal-node](https://www.npmjs.com/package/@azure/msal-node), [@azure/msal-browser](https://www.npmjs.com/package/@azure/msal-browser), [@azure/msal-react](https://www.npmjs.com/package/@azure/msal-react), etc. depending on the flavor of your application.
25
29
 
26
- Once you have the application registered, you can use the following code to automatically generate the token and call endpoints:
30
+ Make sure that you [register an Azure Active Directory application](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app), as that is how you will make use of the @azure/msal packages.
31
+
32
+ Below is a simple example of how this library might be used in a console application to get a user's profile:
27
33
 
28
34
  ```typescript
29
- import { HaloInfiniteClient } from "halo-infinite-api";
35
+ import open from "open"; // An npm package that opens a browser window
36
+ import * as msal from "@azure/msal-node";
37
+ import { HaloInfiniteClient, AutoXstsSpartanTokenProvider } from "halo-infinite-api";
38
+
39
+ const oauthApplication = new msal.PublicClientApplication({
40
+ auth: {
41
+ clientId: "42081d3d-4465-4c86-89ba-ea546f825335",
42
+ authority: "https://login.live.com", // Override the default authority with the xbox one
43
+ knownAuthorities: ["login.live.com"],
44
+ protocolMode: "OIDC", // Shit, I actually have no idea what this does, but Microsoft says I need it
45
+ },
46
+ });
30
47
 
31
48
  const client = new HaloInfiniteClient(
32
- "<YOUR_CLIENT_ID_FROM_AAD>",
33
- "<YOUR_REDIRECT_URI_FROM_AAD>",
34
- async (authorizeUrl: string) => {
35
- // A function that transforms the authorization URL into an authorization code.
36
- // The implementation will vary depending on your use case. This example is how
37
- // a firefox addon would accomplish the task
38
- const activeAuthUrl = await browser.identity.launchWebAuthFlow({
39
- url: authorizeUrl,
40
- interactive: true,
49
+ // Other choice for token providers is the StaticXstsTicketTokenSpartanTokenProvider,
50
+ // which uses a preset spartan token
51
+ new AutoXstsSpartanTokenProvider(async () => {
52
+ const token = await oauthApplication.acquireTokenInteractive({
53
+ // offline_access gives us a refresh token which we can use to continually
54
+ // get new credentials from Microsoft as the old ones expire.
55
+ scopes: ["Xboxlive.signin", "Xboxlive.offline_access"],
56
+ openBrowser: async (url) => {
57
+ await open(url);
58
+ },
41
59
  });
42
-
43
- return new URL(activeAuthUrl).searchParams.get("code")!;
44
- },
45
- // This final parameter is optional, but it allows your HaloInfiniteClient
46
- // to save tokens somewhere so that you don't need to refetch them every time
47
- // you restart your application.
48
- // Again, this is an example for a firefox addon using browser storage.
49
- {
50
- load: async (tokenName) => {
51
- const storageResponse = await browser.storage.local.get(tokenName);
52
- if (!storageResponse[tokenName]) {
53
- return null;
54
- } else {
55
- return storageResponse[tokenName];
56
- }
57
- },
58
- save: async (tokenName, token) => {
59
- await browser.storage.local.set({ [tokenName]: token });
60
- },
61
- }
60
+ return token.accessToken;
61
+ })
62
62
  );
63
63
 
64
64
  const user = await client.getUser("GravlLift");
@@ -10,19 +10,11 @@ export interface XboxAuthenticationToken {
10
10
  refreshToken: string;
11
11
  }
12
12
  export declare class XboxAuthenticationClient {
13
- private readonly clientId;
14
- private readonly redirectUri;
15
- private readonly getAuthCode;
16
13
  private readonly tokenPersister?;
17
- private accessTokenPromise;
18
14
  private userTokenCache;
19
15
  private xstsTicketCache;
20
16
  private readonly httpClient;
21
- constructor(clientId: string, redirectUri: string, getAuthCode: (authorizeUrl: string) => Promise<string>, tokenPersister?: TokenPersister | undefined);
22
- private getPkce;
23
- getAccessToken(): Promise<string>;
24
- private fetchOauth2Token;
25
- private refreshOAuth2Token;
17
+ constructor(tokenPersister?: TokenPersister | undefined);
26
18
  getUserToken(accessToken: string): Promise<string>;
27
19
  getXstsTicket(userToken: string, relyingParty: RelyingParty): Promise<{
28
20
  expiresAt: DateTime;
@@ -1,21 +1,14 @@
1
1
  import axios from "axios";
2
- import pkceChallenge from "pkce-challenge";
3
2
  import { DateTime } from "luxon";
4
3
  import { coalesceDateTime } from "../util/date-time";
5
- import { ResolvablePromise } from "../util/resolvable-promise";
6
4
  import { ExpiryTokenCache } from "../util/expiry-token-cache";
7
- const SCOPES = ["Xboxlive.signin", "Xboxlive.offline_access"];
8
5
  export var RelyingParty;
9
6
  (function (RelyingParty) {
10
7
  RelyingParty["Xbox"] = "http://xboxlive.com";
11
8
  RelyingParty["Halo"] = "https://prod.xsts.halowaypoint.com/";
12
9
  })(RelyingParty || (RelyingParty = {}));
13
10
  export class XboxAuthenticationClient {
14
- clientId;
15
- redirectUri;
16
- getAuthCode;
17
11
  tokenPersister;
18
- accessTokenPromise = undefined;
19
12
  userTokenCache = new ExpiryTokenCache(async (accessToken) => {
20
13
  const persistedToken = await this.tokenPersister?.load("xbox.userToken");
21
14
  if (persistedToken?.expiresAt) {
@@ -76,121 +69,10 @@ export class XboxAuthenticationClient {
76
69
  return result;
77
70
  });
78
71
  httpClient;
79
- constructor(clientId, redirectUri, getAuthCode, tokenPersister) {
80
- this.clientId = clientId;
81
- this.redirectUri = redirectUri;
82
- this.getAuthCode = getAuthCode;
72
+ constructor(tokenPersister) {
83
73
  this.tokenPersister = tokenPersister;
84
74
  this.httpClient = axios.create();
85
75
  }
86
- getPkce() {
87
- if (typeof pkceChallenge === "function") {
88
- return pkceChallenge(43);
89
- }
90
- else {
91
- return pkceChallenge.default(43);
92
- }
93
- }
94
- async getAccessToken() {
95
- if (this.accessTokenPromise) {
96
- // Someone either already has a token or is in the process of getting one
97
- // Wait for them to finish, then check for validity
98
- const currentToken = await this.accessTokenPromise;
99
- if (currentToken.expiresAt > DateTime.now()) {
100
- // Current token is valid, return it
101
- return currentToken.token;
102
- }
103
- else {
104
- // Current token expired, start a new promise
105
- this.accessTokenPromise =
106
- new ResolvablePromise();
107
- try {
108
- const newToken = await this.refreshOAuth2Token(currentToken.refreshToken);
109
- this.accessTokenPromise.resolve(newToken);
110
- await this.tokenPersister?.save("xbox.accessToken", newToken);
111
- return newToken.token;
112
- }
113
- catch (e) {
114
- this.accessTokenPromise.reject(e);
115
- throw e;
116
- }
117
- }
118
- }
119
- else {
120
- // We are the first caller, create a promise to block subsequent callers
121
- this.accessTokenPromise =
122
- new ResolvablePromise();
123
- try {
124
- const loadedToken = await this.tokenPersister?.load("xbox.accessToken");
125
- const currentToken = {
126
- ...loadedToken,
127
- token: loadedToken?.token ?? "",
128
- expiresAt: coalesceDateTime(loadedToken?.expiresAt),
129
- };
130
- if (currentToken.expiresAt && currentToken.expiresAt > DateTime.now()) {
131
- // Current token is valid, return it and alert other callers if applicable
132
- this.accessTokenPromise.resolve(currentToken);
133
- return currentToken.token;
134
- }
135
- else {
136
- const newToken = await this.fetchOauth2Token();
137
- this.accessTokenPromise.resolve(newToken);
138
- await this.tokenPersister?.save("xbox.accessToken", newToken);
139
- return newToken.token;
140
- }
141
- }
142
- catch (e) {
143
- this.accessTokenPromise.reject(e);
144
- throw e;
145
- }
146
- }
147
- }
148
- async fetchOauth2Token() {
149
- const { code_verifier, code_challenge } = this.getPkce();
150
- const authorizeUrl = `https://login.live.com/oauth20_authorize.srf?${new URLSearchParams({
151
- client_id: this.clientId,
152
- response_type: "code",
153
- redirect_uri: this.redirectUri,
154
- scope: SCOPES.join(" "),
155
- code_challenge_method: "S256",
156
- code_challenge,
157
- })}`;
158
- const code = await this.getAuthCode(authorizeUrl);
159
- const requestStart = DateTime.now();
160
- const response = await this.httpClient.post("https://login.live.com/oauth20_token.srf", new URLSearchParams({
161
- grant_type: "authorization_code",
162
- code,
163
- approval_prompt: "auto",
164
- scope: SCOPES.join(" "),
165
- redirect_uri: this.redirectUri,
166
- client_id: this.clientId,
167
- code_verifier,
168
- }), {
169
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
170
- });
171
- return {
172
- token: response.data.access_token,
173
- expiresAt: requestStart.plus({ seconds: response.data.expires_in }),
174
- refreshToken: response.data.refresh_token,
175
- };
176
- }
177
- async refreshOAuth2Token(refreshToken) {
178
- const requestStart = DateTime.now();
179
- const response = await this.httpClient.post("https://login.live.com/oauth20_token.srf", new URLSearchParams({
180
- grant_type: "refresh_token",
181
- refresh_token: refreshToken,
182
- scope: SCOPES.join(" "),
183
- redirect_uri: this.redirectUri,
184
- client_id: this.clientId,
185
- }), {
186
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
187
- });
188
- return {
189
- token: response.data.access_token,
190
- expiresAt: requestStart.plus({ seconds: response.data.expires_in }),
191
- refreshToken: response.data.refresh_token,
192
- };
193
- }
194
76
  async getUserToken(accessToken) {
195
77
  const { Token } = await this.userTokenCache.getToken(accessToken);
196
78
  return Token;
@@ -1 +1 @@
1
- {"version":3,"file":"xbox-authentication-client.js","sourceRoot":"","sources":["../../src/authentication/xbox-authentication-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;AAC7C,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG9D,MAAM,MAAM,GAAG,CAAC,iBAAiB,EAAE,yBAAyB,CAAC,CAAC;AAE9D,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,4CAA4B,CAAA;IAC5B,4DAA4C,CAAA;AAC9C,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB;AAQD,MAAM,OAAO,wBAAwB;IAuFhB;IACA;IACA;IACA;IAzFX,kBAAkB,GAEV,SAAS,CAAC;IAClB,cAAc,GAAG,IAAI,gBAAgB,CAAC,KAAK,EAAE,WAAmB,EAAE,EAAE;QAC1E,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAEpD,gBAAgB,CAAC,CAAC;QAEpB,IAAI,cAAc,EAAE,SAAS,EAAE;YAC7B,MAAM,SAAS,GAAG,gBAAgB,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YAC7D,IAAI,SAAS,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE;gBAC3C,OAAO,EAAE,GAAG,cAAc,EAAE,SAAS,EAAE,CAAC;aACzC;SACF;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CACzC,kDAAkD,EAClD;YACE,YAAY,EAAE,0BAA0B;YACxC,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE;gBACV,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,wBAAwB;gBAClC,SAAS,EAAE,KAAK,WAAW,EAAE;aAC9B;SACF,EACD;YACE,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;gBAC1B,wBAAwB,EAAE,GAAG;aAC9B;SACF,CACF,CAAC;QAEF,MAAM,MAAM,GAAG;YACb,GAAG,QAAQ,CAAC,IAAI;YAChB,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;SACpD,CAAC;QACF,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;QAC1D,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IACK,eAAe,GAAG,IAAI,gBAAgB,CAC5C,KAAK,EAAE,SAAiB,EAAE,YAA0B,EAAE,EAAE;QACtD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAEpD,iBAAiB,CAAC,CAAC;QAErB,IAAI,cAAc,EAAE,SAAS,EAAE;YAC7B,MAAM,SAAS,GAAG,gBAAgB,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YAC7D,IAAI,SAAS,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE;gBAC3C,OAAO,EAAE,GAAG,cAAc,EAAE,SAAS,EAAE,CAAC;aACzC;SACF;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CACzC,+CAA+C,EAC/C;YACE,YAAY,EAAE,YAAY;YAC1B,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE;gBACV,SAAS,EAAE,QAAQ;gBACnB,UAAU,EAAE,CAAC,SAAS,CAAC;aACxB;SACF,EACD;YACE,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;gBAC1B,wBAAwB,EAAE,GAAG;aAC9B;SACF,CACF,CAAC;QAEF,MAAM,MAAM,GAAG;YACb,GAAG,QAAQ,CAAC,IAAI;YAChB,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;SACpD,CAAC;QACF,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAC3D,OAAO,MAAM,CAAC;IAChB,CAAC,CACF,CAAC;IAEe,UAAU,CAAgB;IAE3C,YACmB,QAAgB,EAChB,WAAmB,EACnB,WAAsD,EACtD,cAA+B;QAH/B,aAAQ,GAAR,QAAQ,CAAQ;QAChB,gBAAW,GAAX,WAAW,CAAQ;QACnB,gBAAW,GAAX,WAAW,CAA2C;QACtD,mBAAc,GAAd,cAAc,CAAiB;QAEhD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IACnC,CAAC;IAEO,OAAO;QAGb,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE;YACvC,OAAO,aAAa,CAAC,EAAE,CAAC,CAAC;SAC1B;aAAM;YACL,OAAQ,aAA4C,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SAClE;IACH,CAAC;IAEM,KAAK,CAAC,cAAc;QACzB,IAAI,IAAI,CAAC,kBAAkB,EAAE;YAC3B,yEAAyE;YACzE,mDAAmD;YACnD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC;YAEnD,IAAI,YAAY,CAAC,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE;gBAC3C,oCAAoC;gBACpC,OAAO,YAAY,CAAC,KAAK,CAAC;aAC3B;iBAAM;gBACL,6CAA6C;gBAC7C,IAAI,CAAC,kBAAkB;oBACrB,IAAI,iBAAiB,EAA2B,CAAC;gBAEnD,IAAI;oBACF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAC5C,YAAY,CAAC,YAAY,CAC1B,CAAC;oBACF,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;oBAC1C,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;oBAC9D,OAAO,QAAQ,CAAC,KAAK,CAAC;iBACvB;gBAAC,OAAO,CAAC,EAAE;oBACV,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;oBAClC,MAAM,CAAC,CAAC;iBACT;aACF;SACF;aAAM;YACL,wEAAwE;YACxE,IAAI,CAAC,kBAAkB;gBACrB,IAAI,iBAAiB,EAA2B,CAAC;YAEnD,IAAI;gBACF,MAAM,WAAW,GACf,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAC7B,kBAAkB,CACnB,CAAC;gBACJ,MAAM,YAAY,GAAG;oBACnB,GAAG,WAAW;oBACd,KAAK,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE;oBAC/B,SAAS,EAAE,gBAAgB,CAAC,WAAW,EAAE,SAAS,CAAC;iBACpD,CAAC;gBAEF,IAAI,YAAY,CAAC,SAAS,IAAI,YAAY,CAAC,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE;oBACrE,0EAA0E;oBAC1E,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAC7B,YAAuC,CACxC,CAAC;oBACF,OAAO,YAAY,CAAC,KAAK,CAAC;iBAC3B;qBAAM;oBACL,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBAC/C,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;oBAC1C,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;oBAC9D,OAAO,QAAQ,CAAC,KAAK,CAAC;iBACvB;aACF;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAClC,MAAM,CAAC,CAAC;aACT;SACF;IACH,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAEzD,MAAM,YAAY,GAAG,gDAAgD,IAAI,eAAe,CACtF;YACE,SAAS,EAAE,IAAI,CAAC,QAAQ;YACxB,aAAa,EAAE,MAAM;YACrB,YAAY,EAAE,IAAI,CAAC,WAAW;YAC9B,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;YACvB,qBAAqB,EAAE,MAAM;YAC7B,cAAc;SACf,CACF,EAAE,CAAC;QAEJ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAElD,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAQzC,0CAA0C,EAC1C,IAAI,eAAe,CAAC;YAClB,UAAU,EAAE,oBAAoB;YAChC,IAAI;YACJ,eAAe,EAAE,MAAM;YACvB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;YACvB,YAAY,EAAE,IAAI,CAAC,WAAW;YAC9B,SAAS,EAAE,IAAI,CAAC,QAAQ;YACxB,aAAa;SACd,CAAC,EACF;YACE,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;SACjE,CACF,CAAC;QAEF,OAAO;YACL,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,YAAY;YACjC,SAAS,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACnE,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,aAAa;SAC1C,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAC9B,YAAoB;QAEpB,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAQzC,0CAA0C,EAC1C,IAAI,eAAe,CAAC;YAClB,UAAU,EAAE,eAAe;YAC3B,aAAa,EAAE,YAAY;YAC3B,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;YACvB,YAAY,EAAE,IAAI,CAAC,WAAW;YAC9B,SAAS,EAAE,IAAI,CAAC,QAAQ;SACzB,CAAC,EACF;YACE,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;SACjE,CACF,CAAC;QACF,OAAO;YACL,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,YAAY;YACjC,SAAS,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACnE,YAAY,EAAE,QAAQ,CAAC,IAAI,CAAC,aAAa;SAC1C,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,YAAY,CAAC,WAAmB;QAC3C,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAClE,OAAO,KAAK,CAAC;IACf,CAAC;IAEM,aAAa,CAAC,SAAiB,EAAE,YAA0B;QAChE,OAAO,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAChE,CAAC;IAEM,kBAAkB,GAAG,CAAC,QAAgB,EAAE,SAAiB,EAAE,EAAE,CAClE,YAAY,QAAQ,IAAI,SAAS,EAAE,CAAC;CACvC"}
1
+ {"version":3,"file":"xbox-authentication-client.js","sourceRoot":"","sources":["../../src/authentication/xbox-authentication-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAE9D,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,4CAA4B,CAAA;IAC5B,4DAA4C,CAAA;AAC9C,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB;AAQD,MAAM,OAAO,wBAAwB;IAmFN;IAlFrB,cAAc,GAAG,IAAI,gBAAgB,CAAC,KAAK,EAAE,WAAmB,EAAE,EAAE;QAC1E,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAEpD,gBAAgB,CAAC,CAAC;QAEpB,IAAI,cAAc,EAAE,SAAS,EAAE;YAC7B,MAAM,SAAS,GAAG,gBAAgB,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YAC7D,IAAI,SAAS,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE;gBAC3C,OAAO,EAAE,GAAG,cAAc,EAAE,SAAS,EAAE,CAAC;aACzC;SACF;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CACzC,kDAAkD,EAClD;YACE,YAAY,EAAE,0BAA0B;YACxC,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE;gBACV,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,wBAAwB;gBAClC,SAAS,EAAE,KAAK,WAAW,EAAE;aAC9B;SACF,EACD;YACE,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;gBAC1B,wBAAwB,EAAE,GAAG;aAC9B;SACF,CACF,CAAC;QAEF,MAAM,MAAM,GAAG;YACb,GAAG,QAAQ,CAAC,IAAI;YAChB,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;SACpD,CAAC;QACF,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;QAC1D,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IACK,eAAe,GAAG,IAAI,gBAAgB,CAC5C,KAAK,EAAE,SAAiB,EAAE,YAA0B,EAAE,EAAE;QACtD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAEpD,iBAAiB,CAAC,CAAC;QAErB,IAAI,cAAc,EAAE,SAAS,EAAE;YAC7B,MAAM,SAAS,GAAG,gBAAgB,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YAC7D,IAAI,SAAS,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE;gBAC3C,OAAO,EAAE,GAAG,cAAc,EAAE,SAAS,EAAE,CAAC;aACzC;SACF;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CACzC,+CAA+C,EAC/C;YACE,YAAY,EAAE,YAAY;YAC1B,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE;gBACV,SAAS,EAAE,QAAQ;gBACnB,UAAU,EAAE,CAAC,SAAS,CAAC;aACxB;SACF,EACD;YACE,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;gBAC1B,wBAAwB,EAAE,GAAG;aAC9B;SACF,CACF,CAAC;QAEF,MAAM,MAAM,GAAG;YACb,GAAG,QAAQ,CAAC,IAAI;YAChB,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;SACpD,CAAC;QACF,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAC3D,OAAO,MAAM,CAAC;IAChB,CAAC,CACF,CAAC;IAEe,UAAU,CAAgB;IAE3C,YAA6B,cAA+B;QAA/B,mBAAc,GAAd,cAAc,CAAiB;QAC1D,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IACnC,CAAC;IAEM,KAAK,CAAC,YAAY,CAAC,WAAmB;QAC3C,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAClE,OAAO,KAAK,CAAC;IACf,CAAC;IAEM,aAAa,CAAC,SAAiB,EAAE,YAA0B;QAChE,OAAO,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAChE,CAAC;IAEM,kBAAkB,GAAG,CAAC,QAAgB,EAAE,SAAiB,EAAE,EAAE,CAClE,YAAY,QAAQ,IAAI,SAAS,EAAE,CAAC;CACvC"}
@@ -7,5 +7,5 @@ import { SpartanTokenProvider } from ".";
7
7
  */
8
8
  export declare class AutoXstsSpartanTokenProvider implements SpartanTokenProvider {
9
9
  readonly getSpartanToken: () => Promise<string>;
10
- constructor(clientId: string, redirectUri: string, getAuthCode: (authorizeUrl: string) => Promise<string>, tokenPersister?: TokenPersister);
10
+ constructor(getOauth2AccessToken: () => Promise<string>, tokenPersister?: TokenPersister);
11
11
  }
@@ -8,7 +8,7 @@ import { inMemoryTokenPersister } from "../token-persisters/in-memory-token-pers
8
8
  */
9
9
  export class AutoXstsSpartanTokenProvider {
10
10
  getSpartanToken;
11
- constructor(clientId, redirectUri, getAuthCode, tokenPersister) {
11
+ constructor(getOauth2AccessToken, tokenPersister) {
12
12
  let actualTokenPersister;
13
13
  if (tokenPersister) {
14
14
  actualTokenPersister = tokenPersister;
@@ -16,10 +16,9 @@ export class AutoXstsSpartanTokenProvider {
16
16
  else {
17
17
  actualTokenPersister = inMemoryTokenPersister;
18
18
  }
19
- const xboxAuthClient = new XboxAuthenticationClient(clientId, redirectUri, getAuthCode, tokenPersister);
19
+ const xboxAuthClient = new XboxAuthenticationClient(tokenPersister);
20
20
  const haloAuthClient = new HaloAuthenticationClient(async () => {
21
- const accessToken = await xboxAuthClient.getAccessToken();
22
- const userToken = await xboxAuthClient.getUserToken(accessToken);
21
+ const userToken = await xboxAuthClient.getUserToken(await getOauth2AccessToken());
23
22
  const xstsTicket = await xboxAuthClient.getXstsTicket(userToken, RelyingParty.Halo);
24
23
  return xstsTicket.Token;
25
24
  }, async () => await actualTokenPersister.load("halo.authToken"), async (token) => {
@@ -1 +1 @@
1
- {"version":3,"file":"auto-xsts-spartan-token-provider.js","sourceRoot":"","sources":["../../../src/core/spartan-token-providers/auto-xsts-spartan-token-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,wBAAwB,GACzB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iDAAiD,CAAC;AAE3F,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAEvF;;;;GAIG;AACH,MAAM,OAAO,4BAA4B;IACvB,eAAe,CAAwB;IAEvD,YACE,QAAgB,EAChB,WAAmB,EACnB,WAAsD,EACtD,cAA+B;QAE/B,IAAI,oBAAoC,CAAC;QACzC,IAAI,cAAc,EAAE;YAClB,oBAAoB,GAAG,cAAc,CAAC;SACvC;aAAM;YACL,oBAAoB,GAAG,sBAAsB,CAAC;SAC/C;QACD,MAAM,cAAc,GAAG,IAAI,wBAAwB,CACjD,QAAQ,EACR,WAAW,EACX,WAAW,EACX,cAAc,CACf,CAAC;QACF,MAAM,cAAc,GAAG,IAAI,wBAAwB,CACjD,KAAK,IAAI,EAAE;YACT,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC,cAAc,EAAE,CAAC;YAC1D,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YACjE,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,CACnD,SAAS,EACT,YAAY,CAAC,IAAI,CAClB,CAAC;YACF,OAAO,UAAU,CAAC,KAAK,CAAC;QAC1B,CAAC,EACD,KAAK,IAAI,EAAE,CAAC,MAAM,oBAAoB,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAC7D,KAAK,EAAE,KAAK,EAAE,EAAE;YACd,MAAM,oBAAoB,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QAC3D,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,GAAG,GAAG,EAAE,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC;IAChE,CAAC;CACF"}
1
+ {"version":3,"file":"auto-xsts-spartan-token-provider.js","sourceRoot":"","sources":["../../../src/core/spartan-token-providers/auto-xsts-spartan-token-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,wBAAwB,GACzB,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iDAAiD,CAAC;AAE3F,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAEvF;;;;GAIG;AACH,MAAM,OAAO,4BAA4B;IACvB,eAAe,CAAwB;IAEvD,YACE,oBAA2C,EAC3C,cAA+B;QAE/B,IAAI,oBAAoC,CAAC;QACzC,IAAI,cAAc,EAAE;YAClB,oBAAoB,GAAG,cAAc,CAAC;SACvC;aAAM;YACL,oBAAoB,GAAG,sBAAsB,CAAC;SAC/C;QACD,MAAM,cAAc,GAAG,IAAI,wBAAwB,CAAC,cAAc,CAAC,CAAC;QACpE,MAAM,cAAc,GAAG,IAAI,wBAAwB,CACjD,KAAK,IAAI,EAAE;YACT,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,YAAY,CACjD,MAAM,oBAAoB,EAAE,CAC7B,CAAC;YACF,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,CACnD,SAAS,EACT,YAAY,CAAC,IAAI,CAClB,CAAC;YACF,OAAO,UAAU,CAAC,KAAK,CAAC;QAC1B,CAAC,EACD,KAAK,IAAI,EAAE,CAAC,MAAM,oBAAoB,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAC7D,KAAK,EAAE,KAAK,EAAE,EAAE;YACd,MAAM,oBAAoB,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QAC3D,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,GAAG,GAAG,EAAE,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC;IAChE,CAAC;CACF"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "halo-infinite-api",
3
3
  "type": "module",
4
- "version": "2.1.0",
4
+ "version": "3.0.0",
5
5
  "description": "An NPM package for accessing the official Halo Infinite API.",
6
6
  "main": "dist/index.js",
7
7
  "scripts": {
@@ -33,6 +33,7 @@
33
33
  "yargs": "^17.7.1"
34
34
  },
35
35
  "dependencies": {
36
+ "@azure/msal-node": "^2.2.0",
36
37
  "axios": "^1.3.5",
37
38
  "expiry-map": "^2.0.0",
38
39
  "luxon": "^3.3.0",
@@ -1,13 +1,9 @@
1
1
  import axios, { AxiosInstance } from "axios";
2
- import pkceChallenge from "pkce-challenge";
3
2
  import { DateTime } from "luxon";
3
+ import { TokenPersister } from "../core/token-persisters";
4
4
  import { XboxTicket } from "../models/xbox-ticket";
5
5
  import { coalesceDateTime } from "../util/date-time";
6
- import { ResolvablePromise } from "../util/resolvable-promise";
7
6
  import { ExpiryTokenCache } from "../util/expiry-token-cache";
8
- import { TokenPersister } from "../core/token-persisters";
9
-
10
- const SCOPES = ["Xboxlive.signin", "Xboxlive.offline_access"];
11
7
 
12
8
  export enum RelyingParty {
13
9
  Xbox = "http://xboxlive.com",
@@ -21,9 +17,6 @@ export interface XboxAuthenticationToken {
21
17
  }
22
18
 
23
19
  export class XboxAuthenticationClient {
24
- private accessTokenPromise:
25
- | ResolvablePromise<XboxAuthenticationToken>
26
- | undefined = undefined;
27
20
  private userTokenCache = new ExpiryTokenCache(async (accessToken: string) => {
28
21
  const persistedToken = await this.tokenPersister?.load<
29
22
  XboxTicket & { expiresAt?: unknown }
@@ -106,164 +99,10 @@ export class XboxAuthenticationClient {
106
99
 
107
100
  private readonly httpClient: AxiosInstance;
108
101
 
109
- constructor(
110
- private readonly clientId: string,
111
- private readonly redirectUri: string,
112
- private readonly getAuthCode: (authorizeUrl: string) => Promise<string>,
113
- private readonly tokenPersister?: TokenPersister
114
- ) {
102
+ constructor(private readonly tokenPersister?: TokenPersister) {
115
103
  this.httpClient = axios.create();
116
104
  }
117
105
 
118
- private getPkce() {
119
- // Some sort of module issue here, we work around it
120
- type PkceChallenge = typeof pkceChallenge;
121
- if (typeof pkceChallenge === "function") {
122
- return pkceChallenge(43);
123
- } else {
124
- return (pkceChallenge as { default: PkceChallenge }).default(43);
125
- }
126
- }
127
-
128
- public async getAccessToken() {
129
- if (this.accessTokenPromise) {
130
- // Someone either already has a token or is in the process of getting one
131
- // Wait for them to finish, then check for validity
132
- const currentToken = await this.accessTokenPromise;
133
-
134
- if (currentToken.expiresAt > DateTime.now()) {
135
- // Current token is valid, return it
136
- return currentToken.token;
137
- } else {
138
- // Current token expired, start a new promise
139
- this.accessTokenPromise =
140
- new ResolvablePromise<XboxAuthenticationToken>();
141
-
142
- try {
143
- const newToken = await this.refreshOAuth2Token(
144
- currentToken.refreshToken
145
- );
146
- this.accessTokenPromise.resolve(newToken);
147
- await this.tokenPersister?.save("xbox.accessToken", newToken);
148
- return newToken.token;
149
- } catch (e) {
150
- this.accessTokenPromise.reject(e);
151
- throw e;
152
- }
153
- }
154
- } else {
155
- // We are the first caller, create a promise to block subsequent callers
156
- this.accessTokenPromise =
157
- new ResolvablePromise<XboxAuthenticationToken>();
158
-
159
- try {
160
- const loadedToken =
161
- await this.tokenPersister?.load<XboxAuthenticationToken>(
162
- "xbox.accessToken"
163
- );
164
- const currentToken = {
165
- ...loadedToken,
166
- token: loadedToken?.token ?? "",
167
- expiresAt: coalesceDateTime(loadedToken?.expiresAt),
168
- };
169
-
170
- if (currentToken.expiresAt && currentToken.expiresAt > DateTime.now()) {
171
- // Current token is valid, return it and alert other callers if applicable
172
- this.accessTokenPromise.resolve(
173
- currentToken as XboxAuthenticationToken
174
- );
175
- return currentToken.token;
176
- } else {
177
- const newToken = await this.fetchOauth2Token();
178
- this.accessTokenPromise.resolve(newToken);
179
- await this.tokenPersister?.save("xbox.accessToken", newToken);
180
- return newToken.token;
181
- }
182
- } catch (e) {
183
- this.accessTokenPromise.reject(e);
184
- throw e;
185
- }
186
- }
187
- }
188
-
189
- private async fetchOauth2Token(): Promise<XboxAuthenticationToken> {
190
- const { code_verifier, code_challenge } = this.getPkce();
191
-
192
- const authorizeUrl = `https://login.live.com/oauth20_authorize.srf?${new URLSearchParams(
193
- {
194
- client_id: this.clientId,
195
- response_type: "code",
196
- redirect_uri: this.redirectUri,
197
- scope: SCOPES.join(" "),
198
- code_challenge_method: "S256",
199
- code_challenge,
200
- }
201
- )}`;
202
-
203
- const code = await this.getAuthCode(authorizeUrl);
204
-
205
- const requestStart = DateTime.now();
206
- const response = await this.httpClient.post<{
207
- access_token: string;
208
- expires_in: number;
209
- scope: string;
210
- token_type: string;
211
- user_id: string;
212
- refresh_token: string;
213
- }>(
214
- "https://login.live.com/oauth20_token.srf",
215
- new URLSearchParams({
216
- grant_type: "authorization_code",
217
- code,
218
- approval_prompt: "auto",
219
- scope: SCOPES.join(" "),
220
- redirect_uri: this.redirectUri,
221
- client_id: this.clientId,
222
- code_verifier,
223
- }),
224
- {
225
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
226
- }
227
- );
228
-
229
- return {
230
- token: response.data.access_token,
231
- expiresAt: requestStart.plus({ seconds: response.data.expires_in }),
232
- refreshToken: response.data.refresh_token,
233
- };
234
- }
235
-
236
- private async refreshOAuth2Token(
237
- refreshToken: string
238
- ): Promise<XboxAuthenticationToken> {
239
- const requestStart = DateTime.now();
240
- const response = await this.httpClient.post<{
241
- access_token: string;
242
- expires_in: number;
243
- scope: string;
244
- token_type: string;
245
- user_id: string;
246
- refresh_token: string;
247
- }>(
248
- "https://login.live.com/oauth20_token.srf",
249
- new URLSearchParams({
250
- grant_type: "refresh_token",
251
- refresh_token: refreshToken,
252
- scope: SCOPES.join(" "),
253
- redirect_uri: this.redirectUri,
254
- client_id: this.clientId,
255
- }),
256
- {
257
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
258
- }
259
- );
260
- return {
261
- token: response.data.access_token,
262
- expiresAt: requestStart.plus({ seconds: response.data.expires_in }),
263
- refreshToken: response.data.refresh_token,
264
- };
265
- }
266
-
267
106
  public async getUserToken(accessToken: string) {
268
107
  const { Token } = await this.userTokenCache.getToken(accessToken);
269
108
  return Token;
@@ -16,9 +16,7 @@ export class AutoXstsSpartanTokenProvider implements SpartanTokenProvider {
16
16
  public readonly getSpartanToken: () => Promise<string>;
17
17
 
18
18
  constructor(
19
- clientId: string,
20
- redirectUri: string,
21
- getAuthCode: (authorizeUrl: string) => Promise<string>,
19
+ getOauth2AccessToken: () => Promise<string>,
22
20
  tokenPersister?: TokenPersister
23
21
  ) {
24
22
  let actualTokenPersister: TokenPersister;
@@ -27,16 +25,12 @@ export class AutoXstsSpartanTokenProvider implements SpartanTokenProvider {
27
25
  } else {
28
26
  actualTokenPersister = inMemoryTokenPersister;
29
27
  }
30
- const xboxAuthClient = new XboxAuthenticationClient(
31
- clientId,
32
- redirectUri,
33
- getAuthCode,
34
- tokenPersister
35
- );
28
+ const xboxAuthClient = new XboxAuthenticationClient(tokenPersister);
36
29
  const haloAuthClient = new HaloAuthenticationClient(
37
30
  async () => {
38
- const accessToken = await xboxAuthClient.getAccessToken();
39
- const userToken = await xboxAuthClient.getUserToken(accessToken);
31
+ const userToken = await xboxAuthClient.getUserToken(
32
+ await getOauth2AccessToken()
33
+ );
40
34
  const xstsTicket = await xboxAuthClient.getXstsTicket(
41
35
  userToken,
42
36
  RelyingParty.Halo