halo-infinite-api 1.2.2 → 2.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.
Files changed (62) hide show
  1. package/README.md +1 -1
  2. package/dist/authentication/halo-authentication-client.d.ts +4 -5
  3. package/dist/authentication/halo-authentication-client.js +23 -69
  4. package/dist/authentication/halo-authentication-client.js.map +1 -1
  5. package/dist/authentication/xbox-authentication-client.d.ts +9 -9
  6. package/dist/authentication/xbox-authentication-client.js +80 -63
  7. package/dist/authentication/xbox-authentication-client.js.map +1 -1
  8. package/dist/core/halo-infinite-client.d.ts +5 -8
  9. package/dist/core/halo-infinite-client.js +5 -35
  10. package/dist/core/halo-infinite-client.js.map +1 -1
  11. package/dist/core/spartan-token-fetchers/auto-xsts-sartan-token-provider.d.ts +11 -0
  12. package/dist/core/spartan-token-fetchers/auto-xsts-sartan-token-provider.js +43 -0
  13. package/dist/core/spartan-token-fetchers/auto-xsts-sartan-token-provider.js.map +1 -0
  14. package/dist/core/spartan-token-fetchers/index.d.ts +3 -0
  15. package/dist/core/spartan-token-fetchers/index.js +2 -0
  16. package/dist/core/spartan-token-fetchers/index.js.map +1 -0
  17. package/dist/core/spartan-token-fetchers/static-xsts-ticket-token-spartan-token-provider.d.ts +12 -0
  18. package/dist/core/spartan-token-fetchers/static-xsts-ticket-token-spartan-token-provider.js +26 -0
  19. package/dist/core/spartan-token-fetchers/static-xsts-ticket-token-spartan-token-provider.js.map +1 -0
  20. package/dist/core/spartan-token-providers/auto-xsts-spartan-token-provider.d.ts +11 -0
  21. package/dist/core/spartan-token-providers/auto-xsts-spartan-token-provider.js +32 -0
  22. package/dist/core/spartan-token-providers/auto-xsts-spartan-token-provider.js.map +1 -0
  23. package/dist/core/spartan-token-providers/index.d.ts +3 -0
  24. package/dist/core/spartan-token-providers/index.js +2 -0
  25. package/dist/core/spartan-token-providers/index.js.map +1 -0
  26. package/dist/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.d.ts +12 -0
  27. package/dist/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js +26 -0
  28. package/dist/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js.map +1 -0
  29. package/dist/core/token-persister.d.ts +4 -0
  30. package/dist/core/token-persister.js +2 -0
  31. package/dist/core/token-persister.js.map +1 -0
  32. package/dist/core/token-persisters/index.d.ts +4 -0
  33. package/dist/core/token-persisters/index.js +2 -0
  34. package/dist/core/token-persisters/index.js.map +1 -0
  35. package/dist/core/token-persisters/local-storage-token-persister.d.ts +2 -0
  36. package/dist/core/token-persisters/local-storage-token-persister.js +15 -0
  37. package/dist/core/token-persisters/local-storage-token-persister.js.map +1 -0
  38. package/dist/core/token-persisters/node-fs-token-persister.d.ts +2 -0
  39. package/dist/core/token-persisters/node-fs-token-persister.js +24 -0
  40. package/dist/core/token-persisters/node-fs-token-persister.js.map +1 -0
  41. package/dist/index.d.ts +5 -0
  42. package/dist/index.js +3 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/util/expiry-token-cache.d.ts +9 -0
  45. package/dist/util/expiry-token-cache.js +48 -0
  46. package/dist/util/expiry-token-cache.js.map +1 -0
  47. package/dist/util/resolvable-promise.d.ts +8 -0
  48. package/dist/util/resolvable-promise.js +31 -0
  49. package/dist/util/resolvable-promise.js.map +1 -0
  50. package/package.json +3 -2
  51. package/src/authentication/halo-authentication-client.ts +30 -72
  52. package/src/authentication/xbox-authentication-client.ts +107 -81
  53. package/src/core/halo-infinite-client.ts +9 -66
  54. package/src/core/spartan-token-providers/auto-xsts-spartan-token-provider.ts +55 -0
  55. package/src/core/spartan-token-providers/index.ts +3 -0
  56. package/src/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.ts +36 -0
  57. package/src/core/token-persisters/index.ts +4 -0
  58. package/src/core/token-persisters/local-storage-token-persister.ts +15 -0
  59. package/src/core/token-persisters/node-fs-token-persister.ts +23 -0
  60. package/src/index.ts +8 -0
  61. package/src/util/expiry-token-cache.ts +51 -0
  62. package/src/util/resolvable-promise.ts +32 -0
@@ -0,0 +1,3 @@
1
+ export interface SpartanTokenProvider {
2
+ getSpartanToken(): Promise<string>;
3
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/spartan-token-fetchers/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import { TokenPersister } from "../token-persister";
2
+ import { SpartanTokenProvider } from ".";
3
+ /**
4
+ * A SpartanTokenProvider that fetches uses a pre-fetched XSTS ticket token.
5
+ * Since requests to the Halo API are subject to CORS restrictions a
6
+ * HaloAuthenticationClient can be instantitated with a pre-fetched XSTS ticket
7
+ * and run on a server (such as one provided by the user).
8
+ */
9
+ export declare class StaticXstsTicketTokenSpartanTokenProvider implements SpartanTokenProvider {
10
+ readonly getSpartanToken: () => Promise<string>;
11
+ constructor(xstsTicketToken: string, tokenPersister?: TokenPersister);
12
+ }
@@ -0,0 +1,26 @@
1
+ import { HaloAuthenticationClient } from "../../authentication/halo-authentication-client";
2
+ /**
3
+ * A SpartanTokenProvider that fetches uses a pre-fetched XSTS ticket token.
4
+ * Since requests to the Halo API are subject to CORS restrictions a
5
+ * HaloAuthenticationClient can be instantitated with a pre-fetched XSTS ticket
6
+ * and run on a server (such as one provided by the user).
7
+ */
8
+ export class StaticXstsTicketTokenSpartanTokenProvider {
9
+ getSpartanToken;
10
+ constructor(xstsTicketToken, tokenPersister) {
11
+ const haloAuthClient = new HaloAuthenticationClient(() => xstsTicketToken, async () => {
12
+ if (tokenPersister) {
13
+ return await tokenPersister.load("halo.authToken");
14
+ }
15
+ else {
16
+ return null;
17
+ }
18
+ }, async (token) => {
19
+ if (tokenPersister) {
20
+ await tokenPersister.save("halo.authToken", token);
21
+ }
22
+ });
23
+ this.getSpartanToken = () => haloAuthClient.getSpartanToken();
24
+ }
25
+ }
26
+ //# sourceMappingURL=static-xsts-ticket-token-spartan-token-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-xsts-ticket-token-spartan-token-provider.js","sourceRoot":"","sources":["../../../src/core/spartan-token-fetchers/static-xsts-ticket-token-spartan-token-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,iDAAiD,CAAC;AAG3F;;;;;GAKG;AAEH,MAAM,OAAO,yCAAyC;IAGpC,eAAe,CAAwB;IAEvD,YAAY,eAAuB,EAAE,cAA+B;QAClE,MAAM,cAAc,GAAG,IAAI,wBAAwB,CACjD,GAAG,EAAE,CAAC,eAAe,EACrB,KAAK,IAAI,EAAE;YACT,IAAI,cAAc,EAAE;gBAClB,OAAO,MAAM,cAAc,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;aACpD;iBAAM;gBACL,OAAO,IAAI,CAAC;aACb;QACH,CAAC,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;YACd,IAAI,cAAc,EAAE;gBAClB,MAAM,cAAc,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;aACpD;QACH,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,GAAG,GAAG,EAAE,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC;IAChE,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ import { TokenPersister } from "../token-persisters";
2
+ import { SpartanTokenProvider } from ".";
3
+ /**
4
+ * A SpartanTokenProvider that fetches both the Xbox and Halo tokens in the same
5
+ * process. This is useful for applications that do not need to contend with
6
+ * CORS restrictions.
7
+ */
8
+ export declare class AutoXstsSpartanTokenProvider implements SpartanTokenProvider {
9
+ readonly getSpartanToken: () => Promise<string>;
10
+ constructor(clientId: string, redirectUri: string, getAuthCode: (authorizeUrl: string) => Promise<string>, tokenPersister?: TokenPersister);
11
+ }
@@ -0,0 +1,32 @@
1
+ import { RelyingParty, XboxAuthenticationClient, } from "../../authentication/xbox-authentication-client";
2
+ import { HaloAuthenticationClient } from "../../authentication/halo-authentication-client";
3
+ /**
4
+ * A SpartanTokenProvider that fetches both the Xbox and Halo tokens in the same
5
+ * process. This is useful for applications that do not need to contend with
6
+ * CORS restrictions.
7
+ */
8
+ export class AutoXstsSpartanTokenProvider {
9
+ getSpartanToken;
10
+ constructor(clientId, redirectUri, getAuthCode, tokenPersister) {
11
+ const xboxAuthClient = new XboxAuthenticationClient(clientId, redirectUri, getAuthCode, tokenPersister);
12
+ const haloAuthClient = new HaloAuthenticationClient(async () => {
13
+ const accessToken = await xboxAuthClient.getAccessToken();
14
+ const userToken = await xboxAuthClient.getUserToken(accessToken);
15
+ const xstsTicket = await xboxAuthClient.getXstsTicket(userToken, RelyingParty.Halo);
16
+ return xstsTicket.Token;
17
+ }, async () => {
18
+ if (tokenPersister) {
19
+ return await tokenPersister.load("halo.authToken");
20
+ }
21
+ else {
22
+ return null;
23
+ }
24
+ }, async (token) => {
25
+ if (tokenPersister) {
26
+ await tokenPersister.save("halo.authToken", token);
27
+ }
28
+ });
29
+ this.getSpartanToken = () => haloAuthClient.getSpartanToken();
30
+ }
31
+ }
32
+ //# sourceMappingURL=auto-xsts-spartan-token-provider.js.map
@@ -0,0 +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;AAG3F;;;;GAIG;AACH,MAAM,OAAO,4BAA4B;IACvB,eAAe,CAAwB;IAEvD,YACE,QAAgB,EAChB,WAAmB,EACnB,WAAsD,EACtD,cAA+B;QAE/B,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;YACT,IAAI,cAAc,EAAE;gBAClB,OAAO,MAAM,cAAc,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;aACpD;iBAAM;gBACL,OAAO,IAAI,CAAC;aACb;QACH,CAAC,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;YACd,IAAI,cAAc,EAAE;gBAClB,MAAM,cAAc,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;aACpD;QACH,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,GAAG,GAAG,EAAE,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC;IAChE,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ export interface SpartanTokenProvider {
2
+ getSpartanToken(): Promise<string>;
3
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/spartan-token-providers/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import { TokenPersister } from "../token-persisters";
2
+ import { SpartanTokenProvider } from ".";
3
+ /**
4
+ * A SpartanTokenProvider that fetches uses a pre-fetched XSTS ticket token.
5
+ * Since requests to the Halo API are subject to CORS restrictions a
6
+ * HaloAuthenticationClient can be instantitated with a pre-fetched XSTS ticket
7
+ * and run on a server (such as one provided by the user).
8
+ */
9
+ export declare class StaticXstsTicketTokenSpartanTokenProvider implements SpartanTokenProvider {
10
+ readonly getSpartanToken: () => Promise<string>;
11
+ constructor(xstsTicketToken: string, tokenPersister?: TokenPersister);
12
+ }
@@ -0,0 +1,26 @@
1
+ import { HaloAuthenticationClient } from "../../authentication/halo-authentication-client";
2
+ /**
3
+ * A SpartanTokenProvider that fetches uses a pre-fetched XSTS ticket token.
4
+ * Since requests to the Halo API are subject to CORS restrictions a
5
+ * HaloAuthenticationClient can be instantitated with a pre-fetched XSTS ticket
6
+ * and run on a server (such as one provided by the user).
7
+ */
8
+ export class StaticXstsTicketTokenSpartanTokenProvider {
9
+ getSpartanToken;
10
+ constructor(xstsTicketToken, tokenPersister) {
11
+ const haloAuthClient = new HaloAuthenticationClient(() => xstsTicketToken, async () => {
12
+ if (tokenPersister) {
13
+ return await tokenPersister.load("halo.authToken");
14
+ }
15
+ else {
16
+ return null;
17
+ }
18
+ }, async (token) => {
19
+ if (tokenPersister) {
20
+ await tokenPersister.save("halo.authToken", token);
21
+ }
22
+ });
23
+ this.getSpartanToken = () => haloAuthClient.getSpartanToken();
24
+ }
25
+ }
26
+ //# sourceMappingURL=static-xsts-ticket-token-spartan-token-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-xsts-ticket-token-spartan-token-provider.js","sourceRoot":"","sources":["../../../src/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,iDAAiD,CAAC;AAG3F;;;;;GAKG;AAEH,MAAM,OAAO,yCAAyC;IAGpC,eAAe,CAAwB;IAEvD,YAAY,eAAuB,EAAE,cAA+B;QAClE,MAAM,cAAc,GAAG,IAAI,wBAAwB,CACjD,GAAG,EAAE,CAAC,eAAe,EACrB,KAAK,IAAI,EAAE;YACT,IAAI,cAAc,EAAE;gBAClB,OAAO,MAAM,cAAc,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;aACpD;iBAAM;gBACL,OAAO,IAAI,CAAC;aACb;QACH,CAAC,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;YACd,IAAI,cAAc,EAAE;gBAClB,MAAM,cAAc,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;aACpD;QACH,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,GAAG,GAAG,EAAE,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC;IAChE,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export interface TokenPersister {
2
+ load: <T>(tokenName: string) => Promise<T> | T;
3
+ save: (tokenName: string, token: unknown) => Promise<void> | void;
4
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=token-persister.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-persister.js","sourceRoot":"","sources":["../../src/core/token-persister.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export interface TokenPersister {
2
+ load: <T>(tokenName: string) => Promise<T> | T;
3
+ save: (tokenName: string, token: unknown) => Promise<void> | void;
4
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/token-persisters/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ import { TokenPersister } from ".";
2
+ export declare const localStorageTokenPersister: TokenPersister;
@@ -0,0 +1,15 @@
1
+ export const localStorageTokenPersister = {
2
+ load: (tokenName) => {
3
+ const json = localStorage.getItem(tokenName);
4
+ if (json) {
5
+ return JSON.parse(json);
6
+ }
7
+ else {
8
+ return null;
9
+ }
10
+ },
11
+ save: (tokenName, token) => {
12
+ localStorage.setItem(tokenName, JSON.stringify(token));
13
+ },
14
+ };
15
+ //# sourceMappingURL=local-storage-token-persister.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-storage-token-persister.js","sourceRoot":"","sources":["../../../src/core/token-persisters/local-storage-token-persister.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,0BAA0B,GAAmB;IACxD,IAAI,EAAE,CAAC,SAAS,EAAE,EAAE;QAClB,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,IAAI,EAAE;YACR,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SACzB;aAAM;YACL,OAAO,IAAI,CAAC;SACb;IACH,CAAC;IACD,IAAI,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE;QACzB,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { TokenPersister } from ".";
2
+ export declare const nodeFsTokenPersister: TokenPersister;
@@ -0,0 +1,24 @@
1
+ import fs from "fs/promises";
2
+ export const nodeFsTokenPersister = {
3
+ load: async (tokenName) => {
4
+ const storageFileName = `./tokens/${tokenName}`;
5
+ try {
6
+ const json = await fs.readFile(storageFileName, { encoding: "utf-8" });
7
+ return JSON.parse(json);
8
+ }
9
+ catch (e) {
10
+ if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
11
+ return null;
12
+ }
13
+ else {
14
+ throw e;
15
+ }
16
+ }
17
+ },
18
+ save: async (tokenName, token) => {
19
+ const storageFileName = `./tokens/${tokenName}`;
20
+ await fs.mkdir(`./tokens/`, { recursive: true });
21
+ await fs.writeFile(storageFileName, JSON.stringify(token));
22
+ },
23
+ };
24
+ //# sourceMappingURL=node-fs-token-persister.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-fs-token-persister.js","sourceRoot":"","sources":["../../../src/core/token-persisters/node-fs-token-persister.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,aAAa,CAAC;AAG7B,MAAM,CAAC,MAAM,oBAAoB,GAAmB;IAClD,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;QACxB,MAAM,eAAe,GAAG,YAAY,SAAS,EAAE,CAAC;QAChD,IAAI;YACF,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YACvE,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SACzB;QAAC,OAAO,CAAC,EAAE;YACV,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE;gBACpE,OAAO,IAAI,CAAC;aACb;iBAAM;gBACL,MAAM,CAAC,CAAC;aACT;SACF;IACH,CAAC;IACD,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE;QAC/B,MAAM,eAAe,GAAG,YAAY,SAAS,EAAE,CAAC;QAChD,MAAM,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,MAAM,EAAE,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7D,CAAC;CACF,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { HaloInfiniteClient, AssetKindTypeMap, } from "./core/halo-infinite-client";
2
+ export { XboxAuthenticationClient, RelyingParty, } from "./authentication/xbox-authentication-client";
2
3
  export { Playlist } from "./models/halo-infinite/playlist";
3
4
  export { PlaylistCsrContainer } from "./models/halo-infinite/playlist-csr-container";
4
5
  export { UserInfo } from "./models/halo-infinite/user-info";
@@ -14,3 +15,7 @@ export { MatchOutcome } from "./models/halo-infinite/match-outcome";
14
15
  export { MatchSkill } from "./models/halo-infinite/match-skill";
15
16
  export { AssetVersionLink } from "./models/halo-infinite/asset-version-link";
16
17
  export { MatchInfo } from "./models/halo-infinite/match-info";
18
+ export { SpartanTokenProvider } from "./core/spartan-token-providers";
19
+ export { AutoXstsSpartanTokenProvider } from "./core/spartan-token-providers/auto-xsts-spartan-token-provider";
20
+ export { StaticXstsTicketTokenSpartanTokenProvider } from "./core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider";
21
+ export { TokenPersister } from "./core/token-persisters";
package/dist/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  export { HaloInfiniteClient, } from "./core/halo-infinite-client";
2
+ export { XboxAuthenticationClient, RelyingParty, } from "./authentication/xbox-authentication-client";
2
3
  export { MatchType } from "./models/halo-infinite/match-type";
3
4
  export { GameVariantCategory } from "./models/halo-infinite/game-variant-category";
4
5
  export { AssetKind } from "./models/halo-infinite/asset-kind";
5
6
  export { MatchOutcome } from "./models/halo-infinite/match-outcome";
7
+ export { AutoXstsSpartanTokenProvider } from "./core/spartan-token-providers/auto-xsts-spartan-token-provider";
8
+ export { StaticXstsTicketTokenSpartanTokenProvider } from "./core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider";
6
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAEnB,MAAM,6BAA6B,CAAC;AAKrC,OAAO,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AAKnF,OAAO,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAEnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,wBAAwB,EACxB,YAAY,GACb,MAAM,6CAA6C,CAAC;AAKrD,OAAO,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AAKnF,OAAO,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAKpE,OAAO,EAAE,4BAA4B,EAAE,MAAM,iEAAiE,CAAC;AAC/G,OAAO,EAAE,yCAAyC,EAAE,MAAM,gFAAgF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { DateTime } from "luxon";
2
+ export declare class ExpiryTokenCache<TToken extends {
3
+ expiresAt: DateTime;
4
+ }, TArgs extends any[]> {
5
+ private readonly tokenFetcher;
6
+ private tokenFetchPromise;
7
+ constructor(tokenFetcher: (...args: TArgs) => Promise<TToken>);
8
+ getToken(...args: TArgs): Promise<TToken>;
9
+ }
@@ -0,0 +1,48 @@
1
+ import { DateTime } from "luxon";
2
+ import { ResolvablePromise } from "./resolvable-promise";
3
+ export class ExpiryTokenCache {
4
+ tokenFetcher;
5
+ tokenFetchPromise = undefined;
6
+ constructor(tokenFetcher) {
7
+ this.tokenFetcher = tokenFetcher;
8
+ }
9
+ // TODO: Compare args and separate cache entries based on input
10
+ async getToken(...args) {
11
+ if (this.tokenFetchPromise) {
12
+ // Someone either already has a token or is in the process of getting one
13
+ // Wait for them to finish, then check for validity
14
+ const currentToken = await this.tokenFetchPromise;
15
+ if (currentToken.expiresAt > DateTime.now()) {
16
+ // Current token is valid, return it
17
+ return currentToken;
18
+ }
19
+ else {
20
+ // Current token expired, start a new promise
21
+ this.tokenFetchPromise = new ResolvablePromise();
22
+ try {
23
+ const newToken = await this.tokenFetcher(...args);
24
+ this.tokenFetchPromise.resolve(newToken);
25
+ return newToken;
26
+ }
27
+ catch (e) {
28
+ this.tokenFetchPromise.reject(e);
29
+ throw e;
30
+ }
31
+ }
32
+ }
33
+ else {
34
+ // No one has a token, start a new promise
35
+ this.tokenFetchPromise = new ResolvablePromise();
36
+ try {
37
+ const newToken = await this.tokenFetcher(...args);
38
+ this.tokenFetchPromise.resolve(newToken);
39
+ return newToken;
40
+ }
41
+ catch (e) {
42
+ this.tokenFetchPromise.reject(e);
43
+ throw e;
44
+ }
45
+ }
46
+ }
47
+ }
48
+ //# sourceMappingURL=expiry-token-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expiry-token-cache.js","sourceRoot":"","sources":["../../src/util/expiry-token-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,OAAO,gBAAgB;IAOR;IAHX,iBAAiB,GAA0C,SAAS,CAAC;IAE7E,YACmB,YAAiD;QAAjD,iBAAY,GAAZ,YAAY,CAAqC;IACjE,CAAC;IAEJ,+DAA+D;IAC/D,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAW;QAC3B,IAAI,IAAI,CAAC,iBAAiB,EAAE;YAC1B,yEAAyE;YACzE,mDAAmD;YACnD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC;YAElD,IAAI,YAAY,CAAC,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE;gBAC3C,oCAAoC;gBACpC,OAAO,YAAY,CAAC;aACrB;iBAAM;gBACL,6CAA6C;gBAC7C,IAAI,CAAC,iBAAiB,GAAG,IAAI,iBAAiB,EAAU,CAAC;gBAEzD,IAAI;oBACF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,CAAC;oBAClD,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;oBACzC,OAAO,QAAQ,CAAC;iBACjB;gBAAC,OAAO,CAAC,EAAE;oBACV,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;oBACjC,MAAM,CAAC,CAAC;iBACT;aACF;SACF;aAAM;YACL,0CAA0C;YAC1C,IAAI,CAAC,iBAAiB,GAAG,IAAI,iBAAiB,EAAU,CAAC;YAEzD,IAAI;gBACF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,CAAC;gBAClD,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACzC,OAAO,QAAQ,CAAC;aACjB;YAAC,OAAO,CAAC,EAAE;gBACV,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACjC,MAAM,CAAC,CAAC;aACT;SACF;IACH,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export declare class ResolvablePromise<TReturn> extends Promise<TReturn> {
2
+ isCompleted: boolean;
3
+ readonly resolve: (value: TReturn | PromiseLike<TReturn>) => void;
4
+ readonly reject: (reason?: unknown) => void;
5
+ constructor();
6
+ static get [Symbol.species](): PromiseConstructor;
7
+ get [Symbol.toStringTag](): string;
8
+ }
@@ -0,0 +1,31 @@
1
+ export class ResolvablePromise extends Promise {
2
+ isCompleted = false;
3
+ resolve;
4
+ reject;
5
+ constructor() {
6
+ let resolve;
7
+ let reject;
8
+ super((res, rej) => {
9
+ resolve = res;
10
+ reject = rej;
11
+ });
12
+ this.resolve = (v) => {
13
+ this.isCompleted = true;
14
+ return resolve(v);
15
+ };
16
+ this.reject = (r) => {
17
+ this.isCompleted = true;
18
+ return reject(r);
19
+ };
20
+ }
21
+ // you can also use Symbol.species in order to
22
+ // return a Promise for then/catch/finally
23
+ static get [Symbol.species]() {
24
+ return Promise;
25
+ }
26
+ // Promise overrides his Symbol.toStringTag
27
+ get [Symbol.toStringTag]() {
28
+ return "ResolvablePromise";
29
+ }
30
+ }
31
+ //# sourceMappingURL=resolvable-promise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvable-promise.js","sourceRoot":"","sources":["../../src/util/resolvable-promise.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,iBAA2B,SAAQ,OAAgB;IAC9D,WAAW,GAAG,KAAK,CAAC;IACX,OAAO,CAAkD;IACzD,MAAM,CAA6B;IAC5C;QACE,IAAI,OAAyD,CAAC;QAC9D,IAAI,MAAmC,CAAC;QACxC,KAAK,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACjB,OAAO,GAAG,GAAG,CAAC;YACd,MAAM,GAAG,GAAG,CAAC;QACf,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE;YACnB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE;YAClB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC;IAED,8CAA8C;IAC9C,0CAA0C;IAC1C,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QACzB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,2CAA2C;IAC3C,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACtB,OAAO,mBAAmB,CAAC;IAC7B,CAAC;CACF"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "halo-infinite-api",
3
3
  "type": "module",
4
- "version": "1.2.2",
4
+ "version": "2.0.0",
5
5
  "description": "An NPM package for accessing the official Halo Infinite API.",
6
6
  "main": "dist/index.js",
7
7
  "scripts": {
@@ -34,8 +34,9 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "axios": "^1.3.5",
37
+ "expiry-map": "^2.0.0",
37
38
  "luxon": "^3.3.0",
38
- "pkce-challenge": "^4.0.1",
39
+ "pkce-challenge": "^3.1.0",
39
40
  "simple-oauth2": "^5.0.0"
40
41
  },
41
42
  "publishConfig": {
@@ -3,6 +3,7 @@ import { DateTime } from "luxon";
3
3
  import type { SpartanToken } from "../models/spartan-token";
4
4
  import type { SpartanTokenRequest } from "../models/spartan-token-request";
5
5
  import { coalesceDateTime } from "../util/date-time";
6
+ import { ExpiryTokenCache } from "../util/expiry-token-cache";
6
7
 
7
8
  export interface Token {
8
9
  token: string;
@@ -10,81 +11,21 @@ export interface Token {
10
11
  }
11
12
 
12
13
  export class HaloAuthenticationClient {
13
- private currentTokenPromise: Promise<Token> | undefined = undefined;
14
+ private spartanTokenCache = new ExpiryTokenCache(async () => {
15
+ const persistedToken = await this.loadToken();
14
16
 
15
- constructor(
16
- private readonly fetchXstsToken: () => Promise<string>,
17
- private readonly loadToken: () => Promise<{
18
- token?: string;
19
- expiresAt?: unknown;
20
- } | null>,
21
- private readonly saveToken: (token: Token) => Promise<void>
22
- ) {}
23
-
24
- public async getSpartanToken() {
25
- if (this.currentTokenPromise) {
26
- // Someone either already has a token or is in the process of getting one
27
- // Wait for them to finish, then check for validity
28
- const currentToken = await this.currentTokenPromise;
29
-
30
- if (currentToken.expiresAt > DateTime.now()) {
31
- // Current token is valid, return it
32
- return currentToken.token;
33
- } else {
34
- // Current token expired, start a new promise
35
- let promiseResolver!: (token: Token) => void;
36
- let promiseRejector!: (error: unknown) => void;
37
- this.currentTokenPromise = new Promise<Token>((resolve, reject) => {
38
- promiseResolver = resolve;
39
- promiseRejector = reject;
40
- });
41
-
42
- try {
43
- const xstsToken = await this.fetchXstsToken();
44
- const newToken = await this.fetchSpartanToken(xstsToken);
45
- promiseResolver(newToken);
46
- await this.saveToken(newToken);
47
- return newToken.token;
48
- } catch (e) {
49
- promiseRejector(e);
50
- throw e;
51
- }
52
- }
53
- } else {
54
- // We are the first caller, create a promise to block subsequent callers
55
- let promiseResolver!: (token: Token) => void;
56
- let promiseRejector!: (error: unknown) => void;
57
- this.currentTokenPromise = new Promise<Token>((resolve, reject) => {
58
- promiseResolver = resolve;
59
- promiseRejector = reject;
60
- });
61
-
62
- try {
63
- const loadedToken = await this.loadToken();
64
- const currentToken = {
65
- token: loadedToken?.token ?? "",
66
- expiresAt: coalesceDateTime(loadedToken?.expiresAt),
67
- };
68
-
69
- if (currentToken.expiresAt && currentToken.expiresAt > DateTime.now()) {
70
- // Current token is valid, return it and alert other callers if applicable
71
- promiseResolver(currentToken as Token);
72
- return currentToken.token;
73
- } else {
74
- const xstsToken = await this.fetchXstsToken();
75
- const newToken = await this.fetchSpartanToken(xstsToken);
76
- promiseResolver(newToken);
77
- await this.saveToken(newToken);
78
- return newToken.token;
79
- }
80
- } catch (e) {
81
- promiseRejector(e);
82
- throw e;
17
+ if (persistedToken?.expiresAt) {
18
+ const currentToken = {
19
+ token: persistedToken.token,
20
+ expiresAt: coalesceDateTime(persistedToken.expiresAt) as DateTime,
21
+ };
22
+ if (currentToken.expiresAt && currentToken.expiresAt > DateTime.now()) {
23
+ return currentToken;
83
24
  }
84
25
  }
85
- }
86
26
 
87
- private async fetchSpartanToken(xstsToken: string) {
27
+ const xstsToken = await this.fetchXstsToken();
28
+
88
29
  const tokenRequest: SpartanTokenRequest = {
89
30
  Audience: "urn:343:s3:services",
90
31
  MinVersion: "4",
@@ -106,9 +47,26 @@ export class HaloAuthenticationClient {
106
47
  },
107
48
  }
108
49
  );
109
- return {
50
+
51
+ const newToken = {
110
52
  token: response.data.SpartanToken,
111
53
  expiresAt: DateTime.fromISO(response.data.ExpiresUtc.ISO8601Date),
112
54
  };
55
+ await this.saveToken(newToken);
56
+ return newToken;
57
+ });
58
+
59
+ constructor(
60
+ private readonly fetchXstsToken: () => Promise<string> | string,
61
+ private readonly loadToken: () => Promise<{
62
+ token: string;
63
+ expiresAt: unknown;
64
+ } | null>,
65
+ private readonly saveToken: (token: Token) => Promise<void>
66
+ ) {}
67
+
68
+ public async getSpartanToken() {
69
+ const { token } = await this.spartanTokenCache.getToken();
70
+ return token;
113
71
  }
114
72
  }