halo-infinite-api 2.0.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 +34 -34
- package/dist/authentication/xbox-authentication-client.d.ts +16 -11
- package/dist/authentication/xbox-authentication-client.js +11 -118
- package/dist/authentication/xbox-authentication-client.js.map +1 -1
- package/dist/core/halo-infinite-client.d.ts +4 -0
- package/dist/core/halo-infinite-client.js +15 -0
- package/dist/core/halo-infinite-client.js.map +1 -1
- package/dist/core/spartan-token-providers/auto-xsts-spartan-token-provider.d.ts +1 -1
- package/dist/core/spartan-token-providers/auto-xsts-spartan-token-provider.js +13 -15
- package/dist/core/spartan-token-providers/auto-xsts-spartan-token-provider.js.map +1 -1
- package/dist/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js +10 -11
- package/dist/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js.map +1 -1
- package/dist/core/token-persisters/in-memory-token-persister.d.ts +2 -0
- package/dist/core/token-persisters/in-memory-token-persister.js +10 -0
- package/dist/core/token-persisters/in-memory-token-persister.js.map +1 -0
- package/package.json +2 -1
- package/src/authentication/xbox-authentication-client.ts +13 -163
- package/src/core/halo-infinite-client.ts +21 -0
- package/src/core/spartan-token-providers/auto-xsts-spartan-token-provider.ts +14 -21
- package/src/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.ts +10 -11
- package/src/core/token-persisters/in-memory-token-persister.ts +12 -0
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
async (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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");
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DateTime } from "luxon";
|
|
2
|
-
import { XboxTicket } from "../models/xbox-ticket";
|
|
3
2
|
import { TokenPersister } from "../core/token-persisters";
|
|
4
3
|
export declare enum RelyingParty {
|
|
5
4
|
Xbox = "http://xboxlive.com",
|
|
@@ -11,22 +10,28 @@ export interface XboxAuthenticationToken {
|
|
|
11
10
|
refreshToken: string;
|
|
12
11
|
}
|
|
13
12
|
export declare class XboxAuthenticationClient {
|
|
14
|
-
private readonly clientId;
|
|
15
|
-
private readonly redirectUri;
|
|
16
|
-
private readonly getAuthCode;
|
|
17
13
|
private readonly tokenPersister?;
|
|
18
|
-
private accessTokenPromise;
|
|
19
14
|
private userTokenCache;
|
|
20
15
|
private xstsTicketCache;
|
|
21
16
|
private readonly httpClient;
|
|
22
|
-
constructor(
|
|
23
|
-
private getPkce;
|
|
24
|
-
getAccessToken(): Promise<string>;
|
|
25
|
-
private fetchOauth2Token;
|
|
26
|
-
private refreshOAuth2Token;
|
|
17
|
+
constructor(tokenPersister?: TokenPersister | undefined);
|
|
27
18
|
getUserToken(accessToken: string): Promise<string>;
|
|
28
|
-
getXstsTicket(userToken: string, relyingParty: RelyingParty): Promise<
|
|
19
|
+
getXstsTicket(userToken: string, relyingParty: RelyingParty): Promise<{
|
|
29
20
|
expiresAt: DateTime;
|
|
21
|
+
IssueInstant: string;
|
|
22
|
+
NotAfter: string;
|
|
23
|
+
Token: string;
|
|
24
|
+
DisplayClaims: {
|
|
25
|
+
xui: [{
|
|
26
|
+
uhs: string;
|
|
27
|
+
gtg: string;
|
|
28
|
+
xid: string;
|
|
29
|
+
agg: string;
|
|
30
|
+
usr: string;
|
|
31
|
+
utr: string;
|
|
32
|
+
prv: string;
|
|
33
|
+
}];
|
|
34
|
+
};
|
|
30
35
|
}>;
|
|
31
36
|
getXboxLiveV3Token: (userHash: string, userToken: string) => string;
|
|
32
37
|
}
|
|
@@ -1,25 +1,21 @@
|
|
|
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
|
-
if (persistedToken
|
|
22
|
-
|
|
14
|
+
if (persistedToken?.expiresAt) {
|
|
15
|
+
const expiresAt = coalesceDateTime(persistedToken.expiresAt);
|
|
16
|
+
if (expiresAt && expiresAt > DateTime.now()) {
|
|
17
|
+
return { ...persistedToken, expiresAt };
|
|
18
|
+
}
|
|
23
19
|
}
|
|
24
20
|
const response = await this.httpClient.post("https://user.auth.xboxlive.com/user/authenticate", {
|
|
25
21
|
RelyingParty: "http://auth.xboxlive.com",
|
|
@@ -45,8 +41,11 @@ export class XboxAuthenticationClient {
|
|
|
45
41
|
});
|
|
46
42
|
xstsTicketCache = new ExpiryTokenCache(async (userToken, relyingParty) => {
|
|
47
43
|
const persistedToken = await this.tokenPersister?.load("xbox.xstsTicket");
|
|
48
|
-
if (persistedToken
|
|
49
|
-
|
|
44
|
+
if (persistedToken?.expiresAt) {
|
|
45
|
+
const expiresAt = coalesceDateTime(persistedToken.expiresAt);
|
|
46
|
+
if (expiresAt && expiresAt > DateTime.now()) {
|
|
47
|
+
return { ...persistedToken, expiresAt };
|
|
48
|
+
}
|
|
50
49
|
}
|
|
51
50
|
const response = await this.httpClient.post("https://xsts.auth.xboxlive.com/xsts/authorize", {
|
|
52
51
|
RelyingParty: relyingParty,
|
|
@@ -70,116 +69,10 @@ export class XboxAuthenticationClient {
|
|
|
70
69
|
return result;
|
|
71
70
|
});
|
|
72
71
|
httpClient;
|
|
73
|
-
constructor(
|
|
74
|
-
this.clientId = clientId;
|
|
75
|
-
this.redirectUri = redirectUri;
|
|
76
|
-
this.getAuthCode = getAuthCode;
|
|
72
|
+
constructor(tokenPersister) {
|
|
77
73
|
this.tokenPersister = tokenPersister;
|
|
78
74
|
this.httpClient = axios.create();
|
|
79
75
|
}
|
|
80
|
-
getPkce() {
|
|
81
|
-
return pkceChallenge(43);
|
|
82
|
-
}
|
|
83
|
-
async getAccessToken() {
|
|
84
|
-
if (this.accessTokenPromise) {
|
|
85
|
-
// Someone either already has a token or is in the process of getting one
|
|
86
|
-
// Wait for them to finish, then check for validity
|
|
87
|
-
const currentToken = await this.accessTokenPromise;
|
|
88
|
-
if (currentToken.expiresAt > DateTime.now()) {
|
|
89
|
-
// Current token is valid, return it
|
|
90
|
-
return currentToken.token;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
// Current token expired, start a new promise
|
|
94
|
-
this.accessTokenPromise =
|
|
95
|
-
new ResolvablePromise();
|
|
96
|
-
try {
|
|
97
|
-
const newToken = await this.refreshOAuth2Token(currentToken.refreshToken);
|
|
98
|
-
this.accessTokenPromise.resolve(newToken);
|
|
99
|
-
await this.tokenPersister?.save("xbox.accessToken", newToken);
|
|
100
|
-
return newToken.token;
|
|
101
|
-
}
|
|
102
|
-
catch (e) {
|
|
103
|
-
this.accessTokenPromise.reject(e);
|
|
104
|
-
throw e;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
// We are the first caller, create a promise to block subsequent callers
|
|
110
|
-
this.accessTokenPromise =
|
|
111
|
-
new ResolvablePromise();
|
|
112
|
-
try {
|
|
113
|
-
const loadedToken = await this.tokenPersister?.load("xbox.accessToken");
|
|
114
|
-
const currentToken = {
|
|
115
|
-
...loadedToken,
|
|
116
|
-
token: loadedToken?.token ?? "",
|
|
117
|
-
expiresAt: coalesceDateTime(loadedToken?.expiresAt),
|
|
118
|
-
};
|
|
119
|
-
if (currentToken.expiresAt && currentToken.expiresAt > DateTime.now()) {
|
|
120
|
-
// Current token is valid, return it and alert other callers if applicable
|
|
121
|
-
this.accessTokenPromise.resolve(currentToken);
|
|
122
|
-
return currentToken.token;
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
const newToken = await this.fetchOauth2Token();
|
|
126
|
-
this.accessTokenPromise.resolve(newToken);
|
|
127
|
-
await this.tokenPersister?.save("xbox.accessToken", newToken);
|
|
128
|
-
return newToken.token;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
catch (e) {
|
|
132
|
-
this.accessTokenPromise.reject(e);
|
|
133
|
-
throw e;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
async fetchOauth2Token() {
|
|
138
|
-
const { code_verifier, code_challenge } = this.getPkce();
|
|
139
|
-
const authorizeUrl = `https://login.live.com/oauth20_authorize.srf?${new URLSearchParams({
|
|
140
|
-
client_id: this.clientId,
|
|
141
|
-
response_type: "code",
|
|
142
|
-
redirect_uri: this.redirectUri,
|
|
143
|
-
scope: SCOPES.join(" "),
|
|
144
|
-
code_challenge_method: "S256",
|
|
145
|
-
code_challenge,
|
|
146
|
-
})}`;
|
|
147
|
-
const code = await this.getAuthCode(authorizeUrl);
|
|
148
|
-
const requestStart = DateTime.now();
|
|
149
|
-
const response = await this.httpClient.post("https://login.live.com/oauth20_token.srf", new URLSearchParams({
|
|
150
|
-
grant_type: "authorization_code",
|
|
151
|
-
code,
|
|
152
|
-
approval_prompt: "auto",
|
|
153
|
-
scope: SCOPES.join(" "),
|
|
154
|
-
redirect_uri: this.redirectUri,
|
|
155
|
-
client_id: this.clientId,
|
|
156
|
-
code_verifier,
|
|
157
|
-
}), {
|
|
158
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
159
|
-
});
|
|
160
|
-
return {
|
|
161
|
-
token: response.data.access_token,
|
|
162
|
-
expiresAt: requestStart.plus({ seconds: response.data.expires_in }),
|
|
163
|
-
refreshToken: response.data.refresh_token,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
async refreshOAuth2Token(refreshToken) {
|
|
167
|
-
const response = await this.httpClient.post("https://login.live.com/oauth20_token.srf", new URLSearchParams({
|
|
168
|
-
grant_type: "refresh_token",
|
|
169
|
-
refresh_token: refreshToken,
|
|
170
|
-
scope: SCOPES.join(" "),
|
|
171
|
-
redirect_uri: this.redirectUri,
|
|
172
|
-
client_id: this.clientId,
|
|
173
|
-
}), {
|
|
174
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
175
|
-
});
|
|
176
|
-
const responseDate = DateTime.fromRFC2822(response.headers["date"]);
|
|
177
|
-
return {
|
|
178
|
-
token: response.data.access_token,
|
|
179
|
-
expiresAt: responseDate.plus({ seconds: response.data.expires_in }),
|
|
180
|
-
refreshToken: response.data.refresh_token,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
76
|
async getUserToken(accessToken) {
|
|
184
77
|
const { Token } = await this.userTokenCache.getToken(accessToken);
|
|
185
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,
|
|
1
|
+
{"version":3,"file":"xbox-authentication-client.js","sourceRoot":"","sources":["../../src/authentication/xbox-authentication-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,10 @@ export declare class HaloInfiniteClient {
|
|
|
34
34
|
* @param gamerTag - Gamertag to lookup.
|
|
35
35
|
*/
|
|
36
36
|
getUser: (gamerTag: string) => Promise<UserInfo>;
|
|
37
|
+
/** Get gamertag info for several players.
|
|
38
|
+
* @param xuids - Xuids to lookup.
|
|
39
|
+
*/
|
|
40
|
+
getUsers: (xuids: string[]) => Promise<UserInfo[]>;
|
|
37
41
|
/** Get service record for a player.
|
|
38
42
|
* @param gamerTag - Gamertag to lookup.
|
|
39
43
|
*/
|
|
@@ -17,6 +17,15 @@ function wrapPlayerId(playerId) {
|
|
|
17
17
|
return `xuid(${playerId})`;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
function unwrapPlayerId(playerId) {
|
|
21
|
+
const match = /^\w+\((\d+)\)$/.exec(playerId);
|
|
22
|
+
if (match) {
|
|
23
|
+
return match[1];
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return playerId;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
20
29
|
export class HaloInfiniteClient {
|
|
21
30
|
spartanTokenProvider;
|
|
22
31
|
constructor(spartanTokenProvider) {
|
|
@@ -64,6 +73,12 @@ export class HaloInfiniteClient {
|
|
|
64
73
|
* @param gamerTag - Gamertag to lookup.
|
|
65
74
|
*/
|
|
66
75
|
getUser = (gamerTag) => this.executeRequest(`https://${HaloCoreEndpoints.Profile}.${HaloCoreEndpoints.ServiceDomain}/users/gt(${gamerTag})`, "get");
|
|
76
|
+
/** Get gamertag info for several players.
|
|
77
|
+
* @param xuids - Xuids to lookup.
|
|
78
|
+
*/
|
|
79
|
+
getUsers = (xuids) => {
|
|
80
|
+
return this.executeRequest(`https://${HaloCoreEndpoints.Profile}.${HaloCoreEndpoints.ServiceDomain}/users?xuids=${xuids.map((x) => unwrapPlayerId(x)).join(",")}`, "get");
|
|
81
|
+
};
|
|
67
82
|
/** Get service record for a player.
|
|
68
83
|
* @param gamerTag - Gamertag to lookup.
|
|
69
84
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"halo-infinite-client.js","sourceRoot":"","sources":["../../src/core/halo-infinite-client.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"halo-infinite-client.js","sourceRoot":"","sources":["../../src/core/halo-infinite-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -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(
|
|
10
|
+
constructor(getOauth2AccessToken: () => Promise<string>, tokenPersister?: TokenPersister);
|
|
11
11
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RelyingParty, XboxAuthenticationClient, } from "../../authentication/xbox-authentication-client";
|
|
2
2
|
import { HaloAuthenticationClient } from "../../authentication/halo-authentication-client";
|
|
3
|
+
import { inMemoryTokenPersister } from "../token-persisters/in-memory-token-persister";
|
|
3
4
|
/**
|
|
4
5
|
* A SpartanTokenProvider that fetches both the Xbox and Halo tokens in the same
|
|
5
6
|
* process. This is useful for applications that do not need to contend with
|
|
@@ -7,24 +8,21 @@ import { HaloAuthenticationClient } from "../../authentication/halo-authenticati
|
|
|
7
8
|
*/
|
|
8
9
|
export class AutoXstsSpartanTokenProvider {
|
|
9
10
|
getSpartanToken;
|
|
10
|
-
constructor(
|
|
11
|
-
|
|
11
|
+
constructor(getOauth2AccessToken, tokenPersister) {
|
|
12
|
+
let actualTokenPersister;
|
|
13
|
+
if (tokenPersister) {
|
|
14
|
+
actualTokenPersister = tokenPersister;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
actualTokenPersister = inMemoryTokenPersister;
|
|
18
|
+
}
|
|
19
|
+
const xboxAuthClient = new XboxAuthenticationClient(tokenPersister);
|
|
12
20
|
const haloAuthClient = new HaloAuthenticationClient(async () => {
|
|
13
|
-
const
|
|
14
|
-
const userToken = await xboxAuthClient.getUserToken(accessToken);
|
|
21
|
+
const userToken = await xboxAuthClient.getUserToken(await getOauth2AccessToken());
|
|
15
22
|
const xstsTicket = await xboxAuthClient.getXstsTicket(userToken, RelyingParty.Halo);
|
|
16
23
|
return xstsTicket.Token;
|
|
17
|
-
}, async () => {
|
|
18
|
-
|
|
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
|
-
}
|
|
24
|
+
}, async () => await actualTokenPersister.load("halo.authToken"), async (token) => {
|
|
25
|
+
await actualTokenPersister.save("halo.authToken", token);
|
|
28
26
|
});
|
|
29
27
|
this.getSpartanToken = () => haloAuthClient.getSpartanToken();
|
|
30
28
|
}
|
|
@@ -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;
|
|
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/dist/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HaloAuthenticationClient } from "../../authentication/halo-authentication-client";
|
|
2
|
+
import { inMemoryTokenPersister } from "../token-persisters/in-memory-token-persister";
|
|
2
3
|
/**
|
|
3
4
|
* A SpartanTokenProvider that fetches uses a pre-fetched XSTS ticket token.
|
|
4
5
|
* Since requests to the Halo API are subject to CORS restrictions a
|
|
@@ -8,17 +9,15 @@ import { HaloAuthenticationClient } from "../../authentication/halo-authenticati
|
|
|
8
9
|
export class StaticXstsTicketTokenSpartanTokenProvider {
|
|
9
10
|
getSpartanToken;
|
|
10
11
|
constructor(xstsTicketToken, tokenPersister) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
await tokenPersister.save("halo.authToken", token);
|
|
21
|
-
}
|
|
12
|
+
let actualTokenPersister;
|
|
13
|
+
if (tokenPersister) {
|
|
14
|
+
actualTokenPersister = tokenPersister;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
actualTokenPersister = inMemoryTokenPersister;
|
|
18
|
+
}
|
|
19
|
+
const haloAuthClient = new HaloAuthenticationClient(() => xstsTicketToken, async () => await actualTokenPersister.load("halo.authToken"), async (token) => {
|
|
20
|
+
await actualTokenPersister.save("halo.authToken", token);
|
|
22
21
|
});
|
|
23
22
|
this.getSpartanToken = () => haloAuthClient.getSpartanToken();
|
|
24
23
|
}
|
package/dist/core/spartan-token-providers/static-xsts-ticket-token-spartan-token-provider.js.map
CHANGED
|
@@ -1 +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;
|
|
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;AAE3F,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAEvF;;;;;GAKG;AACH,MAAM,OAAO,yCAAyC;IAGpC,eAAe,CAAwB;IAEvD,YAAY,eAAuB,EAAE,cAA+B;QAClE,IAAI,oBAAoC,CAAC;QACzC,IAAI,cAAc,EAAE;YAClB,oBAAoB,GAAG,cAAc,CAAC;SACvC;aAAM;YACL,oBAAoB,GAAG,sBAAsB,CAAC;SAC/C;QAED,MAAM,cAAc,GAAG,IAAI,wBAAwB,CACjD,GAAG,EAAE,CAAC,eAAe,EACrB,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-memory-token-persister.js","sourceRoot":"","sources":["../../../src/core/token-persisters/in-memory-token-persister.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAG,IAAI,GAAG,EAAe,CAAC;AAEtC,MAAM,CAAC,MAAM,sBAAsB,GAAmB;IACpD,IAAI,EAAE,CAAC,SAAS,EAAE,EAAE;QAClB,OAAO,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE;QACzB,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "halo-infinite-api",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
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,16 +17,16 @@ 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
|
-
XboxTicket & { expiresAt
|
|
22
|
+
XboxTicket & { expiresAt?: unknown }
|
|
30
23
|
>("xbox.userToken");
|
|
31
24
|
|
|
32
|
-
if (persistedToken
|
|
33
|
-
|
|
25
|
+
if (persistedToken?.expiresAt) {
|
|
26
|
+
const expiresAt = coalesceDateTime(persistedToken.expiresAt);
|
|
27
|
+
if (expiresAt && expiresAt > DateTime.now()) {
|
|
28
|
+
return { ...persistedToken, expiresAt };
|
|
29
|
+
}
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
const response = await this.httpClient.post<XboxTicket>(
|
|
@@ -66,8 +62,11 @@ export class XboxAuthenticationClient {
|
|
|
66
62
|
XboxTicket & { expiresAt: DateTime }
|
|
67
63
|
>("xbox.xstsTicket");
|
|
68
64
|
|
|
69
|
-
if (persistedToken
|
|
70
|
-
|
|
65
|
+
if (persistedToken?.expiresAt) {
|
|
66
|
+
const expiresAt = coalesceDateTime(persistedToken.expiresAt);
|
|
67
|
+
if (expiresAt && expiresAt > DateTime.now()) {
|
|
68
|
+
return { ...persistedToken, expiresAt };
|
|
69
|
+
}
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
const response = await this.httpClient.post<XboxTicket>(
|
|
@@ -100,159 +99,10 @@ export class XboxAuthenticationClient {
|
|
|
100
99
|
|
|
101
100
|
private readonly httpClient: AxiosInstance;
|
|
102
101
|
|
|
103
|
-
constructor(
|
|
104
|
-
private readonly clientId: string,
|
|
105
|
-
private readonly redirectUri: string,
|
|
106
|
-
private readonly getAuthCode: (authorizeUrl: string) => Promise<string>,
|
|
107
|
-
private readonly tokenPersister?: TokenPersister
|
|
108
|
-
) {
|
|
102
|
+
constructor(private readonly tokenPersister?: TokenPersister) {
|
|
109
103
|
this.httpClient = axios.create();
|
|
110
104
|
}
|
|
111
105
|
|
|
112
|
-
private getPkce() {
|
|
113
|
-
return pkceChallenge(43);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
public async getAccessToken() {
|
|
117
|
-
if (this.accessTokenPromise) {
|
|
118
|
-
// Someone either already has a token or is in the process of getting one
|
|
119
|
-
// Wait for them to finish, then check for validity
|
|
120
|
-
const currentToken = await this.accessTokenPromise;
|
|
121
|
-
|
|
122
|
-
if (currentToken.expiresAt > DateTime.now()) {
|
|
123
|
-
// Current token is valid, return it
|
|
124
|
-
return currentToken.token;
|
|
125
|
-
} else {
|
|
126
|
-
// Current token expired, start a new promise
|
|
127
|
-
this.accessTokenPromise =
|
|
128
|
-
new ResolvablePromise<XboxAuthenticationToken>();
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
const newToken = await this.refreshOAuth2Token(
|
|
132
|
-
currentToken.refreshToken
|
|
133
|
-
);
|
|
134
|
-
this.accessTokenPromise.resolve(newToken);
|
|
135
|
-
await this.tokenPersister?.save("xbox.accessToken", newToken);
|
|
136
|
-
return newToken.token;
|
|
137
|
-
} catch (e) {
|
|
138
|
-
this.accessTokenPromise.reject(e);
|
|
139
|
-
throw e;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} else {
|
|
143
|
-
// We are the first caller, create a promise to block subsequent callers
|
|
144
|
-
this.accessTokenPromise =
|
|
145
|
-
new ResolvablePromise<XboxAuthenticationToken>();
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const loadedToken =
|
|
149
|
-
await this.tokenPersister?.load<XboxAuthenticationToken>(
|
|
150
|
-
"xbox.accessToken"
|
|
151
|
-
);
|
|
152
|
-
const currentToken = {
|
|
153
|
-
...loadedToken,
|
|
154
|
-
token: loadedToken?.token ?? "",
|
|
155
|
-
expiresAt: coalesceDateTime(loadedToken?.expiresAt),
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
if (currentToken.expiresAt && currentToken.expiresAt > DateTime.now()) {
|
|
159
|
-
// Current token is valid, return it and alert other callers if applicable
|
|
160
|
-
this.accessTokenPromise.resolve(
|
|
161
|
-
currentToken as XboxAuthenticationToken
|
|
162
|
-
);
|
|
163
|
-
return currentToken.token;
|
|
164
|
-
} else {
|
|
165
|
-
const newToken = await this.fetchOauth2Token();
|
|
166
|
-
this.accessTokenPromise.resolve(newToken);
|
|
167
|
-
await this.tokenPersister?.save("xbox.accessToken", newToken);
|
|
168
|
-
return newToken.token;
|
|
169
|
-
}
|
|
170
|
-
} catch (e) {
|
|
171
|
-
this.accessTokenPromise.reject(e);
|
|
172
|
-
throw e;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
private async fetchOauth2Token(): Promise<XboxAuthenticationToken> {
|
|
178
|
-
const { code_verifier, code_challenge } = this.getPkce();
|
|
179
|
-
|
|
180
|
-
const authorizeUrl = `https://login.live.com/oauth20_authorize.srf?${new URLSearchParams(
|
|
181
|
-
{
|
|
182
|
-
client_id: this.clientId,
|
|
183
|
-
response_type: "code",
|
|
184
|
-
redirect_uri: this.redirectUri,
|
|
185
|
-
scope: SCOPES.join(" "),
|
|
186
|
-
code_challenge_method: "S256",
|
|
187
|
-
code_challenge,
|
|
188
|
-
}
|
|
189
|
-
)}`;
|
|
190
|
-
|
|
191
|
-
const code = await this.getAuthCode(authorizeUrl);
|
|
192
|
-
|
|
193
|
-
const requestStart = DateTime.now();
|
|
194
|
-
const response = await this.httpClient.post<{
|
|
195
|
-
access_token: string;
|
|
196
|
-
expires_in: number;
|
|
197
|
-
scope: string;
|
|
198
|
-
token_type: string;
|
|
199
|
-
user_id: string;
|
|
200
|
-
refresh_token: string;
|
|
201
|
-
}>(
|
|
202
|
-
"https://login.live.com/oauth20_token.srf",
|
|
203
|
-
new URLSearchParams({
|
|
204
|
-
grant_type: "authorization_code",
|
|
205
|
-
code,
|
|
206
|
-
approval_prompt: "auto",
|
|
207
|
-
scope: SCOPES.join(" "),
|
|
208
|
-
redirect_uri: this.redirectUri,
|
|
209
|
-
client_id: this.clientId,
|
|
210
|
-
code_verifier,
|
|
211
|
-
}),
|
|
212
|
-
{
|
|
213
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
214
|
-
}
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
token: response.data.access_token,
|
|
219
|
-
expiresAt: requestStart.plus({ seconds: response.data.expires_in }),
|
|
220
|
-
refreshToken: response.data.refresh_token,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
private async refreshOAuth2Token(
|
|
225
|
-
refreshToken: string
|
|
226
|
-
): Promise<XboxAuthenticationToken> {
|
|
227
|
-
const response = await this.httpClient.post<{
|
|
228
|
-
access_token: string;
|
|
229
|
-
expires_in: number;
|
|
230
|
-
scope: string;
|
|
231
|
-
token_type: string;
|
|
232
|
-
user_id: string;
|
|
233
|
-
refresh_token: string;
|
|
234
|
-
}>(
|
|
235
|
-
"https://login.live.com/oauth20_token.srf",
|
|
236
|
-
new URLSearchParams({
|
|
237
|
-
grant_type: "refresh_token",
|
|
238
|
-
refresh_token: refreshToken,
|
|
239
|
-
scope: SCOPES.join(" "),
|
|
240
|
-
redirect_uri: this.redirectUri,
|
|
241
|
-
client_id: this.clientId,
|
|
242
|
-
}),
|
|
243
|
-
{
|
|
244
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
245
|
-
}
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
const responseDate = DateTime.fromRFC2822(response.headers["date"]);
|
|
249
|
-
return {
|
|
250
|
-
token: response.data.access_token,
|
|
251
|
-
expiresAt: responseDate.plus({ seconds: response.data.expires_in }),
|
|
252
|
-
refreshToken: response.data.refresh_token,
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
106
|
public async getUserToken(accessToken: string) {
|
|
257
107
|
const { Token } = await this.userTokenCache.getToken(accessToken);
|
|
258
108
|
return Token;
|
|
@@ -57,6 +57,15 @@ function wrapPlayerId(playerId: string) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function unwrapPlayerId(playerId: string) {
|
|
61
|
+
const match = /^\w+\((\d+)\)$/.exec(playerId);
|
|
62
|
+
if (match) {
|
|
63
|
+
return match[1];
|
|
64
|
+
} else {
|
|
65
|
+
return playerId;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
export class HaloInfiniteClient {
|
|
61
70
|
constructor(private spartanTokenProvider: SpartanTokenProvider) {}
|
|
62
71
|
|
|
@@ -142,6 +151,18 @@ export class HaloInfiniteClient {
|
|
|
142
151
|
"get"
|
|
143
152
|
);
|
|
144
153
|
|
|
154
|
+
/** Get gamertag info for several players.
|
|
155
|
+
* @param xuids - Xuids to lookup.
|
|
156
|
+
*/
|
|
157
|
+
public getUsers = (xuids: string[]) => {
|
|
158
|
+
return this.executeRequest<UserInfo[]>(
|
|
159
|
+
`https://${HaloCoreEndpoints.Profile}.${
|
|
160
|
+
HaloCoreEndpoints.ServiceDomain
|
|
161
|
+
}/users?xuids=${xuids.map((x) => unwrapPlayerId(x)).join(",")}`,
|
|
162
|
+
"get"
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
145
166
|
/** Get service record for a player.
|
|
146
167
|
* @param gamerTag - Gamertag to lookup.
|
|
147
168
|
*/
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
import { TokenPersister } from "../token-persisters";
|
|
6
6
|
import { HaloAuthenticationClient } from "../../authentication/halo-authentication-client";
|
|
7
7
|
import { SpartanTokenProvider } from ".";
|
|
8
|
+
import { inMemoryTokenPersister } from "../token-persisters/in-memory-token-persister";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* A SpartanTokenProvider that fetches both the Xbox and Halo tokens in the same
|
|
@@ -15,38 +16,30 @@ export class AutoXstsSpartanTokenProvider implements SpartanTokenProvider {
|
|
|
15
16
|
public readonly getSpartanToken: () => Promise<string>;
|
|
16
17
|
|
|
17
18
|
constructor(
|
|
18
|
-
|
|
19
|
-
redirectUri: string,
|
|
20
|
-
getAuthCode: (authorizeUrl: string) => Promise<string>,
|
|
19
|
+
getOauth2AccessToken: () => Promise<string>,
|
|
21
20
|
tokenPersister?: TokenPersister
|
|
22
21
|
) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
let actualTokenPersister: TokenPersister;
|
|
23
|
+
if (tokenPersister) {
|
|
24
|
+
actualTokenPersister = tokenPersister;
|
|
25
|
+
} else {
|
|
26
|
+
actualTokenPersister = inMemoryTokenPersister;
|
|
27
|
+
}
|
|
28
|
+
const xboxAuthClient = new XboxAuthenticationClient(tokenPersister);
|
|
29
29
|
const haloAuthClient = new HaloAuthenticationClient(
|
|
30
30
|
async () => {
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const userToken = await xboxAuthClient.getUserToken(
|
|
32
|
+
await getOauth2AccessToken()
|
|
33
|
+
);
|
|
33
34
|
const xstsTicket = await xboxAuthClient.getXstsTicket(
|
|
34
35
|
userToken,
|
|
35
36
|
RelyingParty.Halo
|
|
36
37
|
);
|
|
37
38
|
return xstsTicket.Token;
|
|
38
39
|
},
|
|
39
|
-
async () =>
|
|
40
|
-
if (tokenPersister) {
|
|
41
|
-
return await tokenPersister.load("halo.authToken");
|
|
42
|
-
} else {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
},
|
|
40
|
+
async () => await actualTokenPersister.load("halo.authToken"),
|
|
46
41
|
async (token) => {
|
|
47
|
-
|
|
48
|
-
await tokenPersister.save("halo.authToken", token);
|
|
49
|
-
}
|
|
42
|
+
await actualTokenPersister.save("halo.authToken", token);
|
|
50
43
|
}
|
|
51
44
|
);
|
|
52
45
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TokenPersister } from "../token-persisters";
|
|
2
2
|
import { HaloAuthenticationClient } from "../../authentication/halo-authentication-client";
|
|
3
3
|
import { SpartanTokenProvider } from ".";
|
|
4
|
+
import { inMemoryTokenPersister } from "../token-persisters/in-memory-token-persister";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* A SpartanTokenProvider that fetches uses a pre-fetched XSTS ticket token.
|
|
@@ -8,26 +9,24 @@ import { SpartanTokenProvider } from ".";
|
|
|
8
9
|
* HaloAuthenticationClient can be instantitated with a pre-fetched XSTS ticket
|
|
9
10
|
* and run on a server (such as one provided by the user).
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
12
|
export class StaticXstsTicketTokenSpartanTokenProvider
|
|
13
13
|
implements SpartanTokenProvider
|
|
14
14
|
{
|
|
15
15
|
public readonly getSpartanToken: () => Promise<string>;
|
|
16
16
|
|
|
17
17
|
constructor(xstsTicketToken: string, tokenPersister?: TokenPersister) {
|
|
18
|
+
let actualTokenPersister: TokenPersister;
|
|
19
|
+
if (tokenPersister) {
|
|
20
|
+
actualTokenPersister = tokenPersister;
|
|
21
|
+
} else {
|
|
22
|
+
actualTokenPersister = inMemoryTokenPersister;
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
const haloAuthClient = new HaloAuthenticationClient(
|
|
19
26
|
() => xstsTicketToken,
|
|
20
|
-
async () =>
|
|
21
|
-
if (tokenPersister) {
|
|
22
|
-
return await tokenPersister.load("halo.authToken");
|
|
23
|
-
} else {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
+
async () => await actualTokenPersister.load("halo.authToken"),
|
|
27
28
|
async (token) => {
|
|
28
|
-
|
|
29
|
-
await tokenPersister.save("halo.authToken", token);
|
|
30
|
-
}
|
|
29
|
+
await actualTokenPersister.save("halo.authToken", token);
|
|
31
30
|
}
|
|
32
31
|
);
|
|
33
32
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TokenPersister } from ".";
|
|
2
|
+
|
|
3
|
+
const tokens = new Map<string, any>();
|
|
4
|
+
|
|
5
|
+
export const inMemoryTokenPersister: TokenPersister = {
|
|
6
|
+
load: (tokenName) => {
|
|
7
|
+
return tokens.get(tokenName);
|
|
8
|
+
},
|
|
9
|
+
save: (tokenName, token) => {
|
|
10
|
+
tokens.set(tokenName, token);
|
|
11
|
+
},
|
|
12
|
+
};
|