Skip to content

Commit f33cc04

Browse files
authored
Merge pull request #7 from Daniel-Ric/feature/2026-01-22/list-missing-user-related-tokens-in-callback
Add PlayFab entity token to auth callback responses
2 parents 28336c4 + 605825e commit f33cc04

File tree

5 files changed

+114
-12
lines changed

5 files changed

+114
-12
lines changed

src/routes/auth.routes.js

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import {asyncHandler} from "../utils/async.js";
44
import {jwtMiddleware, signJwt} from "../utils/jwt.js";
55
import {getTokenFromDeviceCode, refreshMsToken, requestDeviceCode} from "../services/microsoft.service.js";
66
import {getXBLToken, getXSTSToken} from "../services/xbox.service.js";
7-
import {loginWithXbox} from "../services/playfab.service.js";
7+
import {getEntityToken, loginWithXbox} from "../services/playfab.service.js";
88
import {getMCToken} from "../services/minecraft.service.js";
99
import {env} from "../config/env.js";
1010
import {authLimiter} from "../middleware/rateLimit.js";
1111
import {badRequest} from "../utils/httpError.js";
12+
import {buildAuthCallbackResponse} from "../utils/authResponse.js";
1213

1314
const router = express.Router();
1415

@@ -100,13 +101,28 @@ router.post("/callback", authLimiter, asyncHandler(async (req, res) => {
100101

101102
const {SessionTicket, PlayFabId} = await loginWithXbox(playfabToken, titleId);
102103
const mcToken = await getMCToken(SessionTicket);
104+
const entityData = await getEntityToken(SessionTicket);
103105
const jwtToken = signJwt({xuid: xid, gamertag: gtg});
104106

105-
res.json({
106-
jwt: jwtToken, xuid: xid, gamertag: gtg, uhs, msAccessToken, msRefreshToken, msExpiresIn, xblToken, xsts: {
107-
xbox: xboxTokenInfo, redeem: redeemTokenInfo, playfab: playfabTokenInfo
108-
}, xboxliveToken, playfabToken, redeemToken, mcToken, sessionTicket: SessionTicket, playFabId: PlayFabId
109-
});
107+
res.json(buildAuthCallbackResponse({
108+
jwtToken,
109+
xuid: xid,
110+
gamertag: gtg,
111+
uhs,
112+
msAccessToken,
113+
msRefreshToken,
114+
msExpiresIn,
115+
xblToken,
116+
xsts: {xbox: xboxTokenInfo, redeem: redeemTokenInfo, playfab: playfabTokenInfo},
117+
xboxliveToken,
118+
playfabToken,
119+
redeemToken,
120+
mcToken,
121+
sessionTicket: SessionTicket,
122+
playFabId: PlayFabId,
123+
entityToken: entityData.EntityToken,
124+
entityTokenExpiresOn: entityData.TokenExpiration
125+
}));
110126
}));
111127

112128
/**
@@ -171,13 +187,28 @@ router.post("/refresh", authLimiter, asyncHandler(async (req, res) => {
171187

172188
const {SessionTicket, PlayFabId} = await loginWithXbox(playfabToken, titleId);
173189
const mcToken = await getMCToken(SessionTicket);
190+
const entityData = await getEntityToken(SessionTicket);
174191
const jwtToken = signJwt({xuid: xid, gamertag: gtg});
175192

176-
res.json({
177-
jwt: jwtToken, xuid: xid, gamertag: gtg, uhs, msAccessToken, msRefreshToken, msExpiresIn, xblToken, xsts: {
178-
xbox: xboxTokenInfo, redeem: redeemTokenInfo, playfab: playfabTokenInfo
179-
}, xboxliveToken, playfabToken, redeemToken, mcToken, sessionTicket: SessionTicket, playFabId: PlayFabId
180-
});
193+
res.json(buildAuthCallbackResponse({
194+
jwtToken,
195+
xuid: xid,
196+
gamertag: gtg,
197+
uhs,
198+
msAccessToken,
199+
msRefreshToken,
200+
msExpiresIn,
201+
xblToken,
202+
xsts: {xbox: xboxTokenInfo, redeem: redeemTokenInfo, playfab: playfabTokenInfo},
203+
xboxliveToken,
204+
playfabToken,
205+
redeemToken,
206+
mcToken,
207+
sessionTicket: SessionTicket,
208+
playFabId: PlayFabId,
209+
entityToken: entityData.EntityToken,
210+
entityTokenExpiresOn: entityData.TokenExpiration
211+
}));
181212
}));
182213

183214
/**

src/routes/debug.routes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ router.post("/decode-callback", jwtMiddleware, asyncHandler(async (req, res) =>
156156
mcToken: bundle.mcToken,
157157
sessionTicket: bundle.sessionTicket,
158158
playFabId: bundle.playFabId,
159+
entityToken: bundle.entityToken,
159160
xstsXbox: bundle.xstsXbox,
160161
xstsRedeem: bundle.xstsRedeem,
161162
xstsPlayFab: bundle.xstsPlayFab
@@ -206,6 +207,7 @@ router.post("/decode-callback", jwtMiddleware, asyncHandler(async (req, res) =>
206207

207208
add("mcToken", extracted.mcToken);
208209
add("sessionTicket", extracted.sessionTicket);
210+
add("entityToken", extracted.entityToken);
209211

210212
const user = {
211213
xuid: extracted.xuid || decoded.jwt?.payload?.xuid || get(bundle, "xsts.xbox.DisplayClaims.xui.0.xid") || null,

src/utils/authResponse.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export function buildAuthCallbackResponse(data) {
2+
return {
3+
jwt: data.jwtToken,
4+
xuid: data.xuid,
5+
gamertag: data.gamertag,
6+
uhs: data.uhs,
7+
msAccessToken: data.msAccessToken,
8+
msRefreshToken: data.msRefreshToken,
9+
msExpiresIn: data.msExpiresIn,
10+
xblToken: data.xblToken,
11+
xsts: data.xsts,
12+
xboxliveToken: data.xboxliveToken,
13+
playfabToken: data.playfabToken,
14+
redeemToken: data.redeemToken,
15+
mcToken: data.mcToken,
16+
sessionTicket: data.sessionTicket,
17+
playFabId: data.playFabId,
18+
entityToken: data.entityToken,
19+
entityTokenExpiresOn: data.entityTokenExpiresOn
20+
};
21+
}

src/utils/swagger.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ const options = {
8080
redeemToken: {type: "string"},
8181
mcToken: {type: "string"},
8282
sessionTicket: {type: "string"},
83-
playFabId: {type: "string"}
83+
playFabId: {type: "string"},
84+
entityToken: {type: "string"},
85+
entityTokenExpiresOn: {type: "string"}
8486
}
8587
}, ProfileOverviewRequest: {
8688
type: "object", properties: {

tests/authResponse.test.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import test from "node:test";
2+
import assert from "node:assert/strict";
3+
4+
import {buildAuthCallbackResponse} from "../src/utils/authResponse.js";
5+
6+
test("buildAuthCallbackResponse maps callback payload", () => {
7+
const result = buildAuthCallbackResponse({
8+
jwtToken: "jwt",
9+
xuid: "xuid",
10+
gamertag: "tag",
11+
uhs: "uhs",
12+
msAccessToken: "ms-access",
13+
msRefreshToken: "ms-refresh",
14+
msExpiresIn: 3600,
15+
xblToken: "xbl",
16+
xsts: {xbox: {Token: "xsts"}},
17+
xboxliveToken: "XBL3.0 x=uhs;xsts",
18+
playfabToken: "XBL3.0 x=uhs;pf",
19+
redeemToken: "XBL3.0 x=uhs;redeem",
20+
mcToken: "MCToken mc",
21+
sessionTicket: "ticket",
22+
playFabId: "pfid",
23+
entityToken: "entity-token",
24+
entityTokenExpiresOn: "2025-01-01T00:00:00Z"
25+
});
26+
27+
assert.deepEqual(result, {
28+
jwt: "jwt",
29+
xuid: "xuid",
30+
gamertag: "tag",
31+
uhs: "uhs",
32+
msAccessToken: "ms-access",
33+
msRefreshToken: "ms-refresh",
34+
msExpiresIn: 3600,
35+
xblToken: "xbl",
36+
xsts: {xbox: {Token: "xsts"}},
37+
xboxliveToken: "XBL3.0 x=uhs;xsts",
38+
playfabToken: "XBL3.0 x=uhs;pf",
39+
redeemToken: "XBL3.0 x=uhs;redeem",
40+
mcToken: "MCToken mc",
41+
sessionTicket: "ticket",
42+
playFabId: "pfid",
43+
entityToken: "entity-token",
44+
entityTokenExpiresOn: "2025-01-01T00:00:00Z"
45+
});
46+
});

0 commit comments

Comments
 (0)