@phalanx-engine/client 0.1.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 +1037 -0
- package/dist/DesyncDetector.d.ts +80 -0
- package/dist/DesyncDetector.d.ts.map +1 -0
- package/dist/DesyncDetector.js +93 -0
- package/dist/DesyncDetector.js.map +1 -0
- package/dist/DeterministicRandom.d.ts +78 -0
- package/dist/DeterministicRandom.d.ts.map +1 -0
- package/dist/DeterministicRandom.js +122 -0
- package/dist/DeterministicRandom.js.map +1 -0
- package/dist/EventEmitter.d.ts +65 -0
- package/dist/EventEmitter.d.ts.map +1 -0
- package/dist/EventEmitter.js +102 -0
- package/dist/EventEmitter.js.map +1 -0
- package/dist/FixedMath.d.ts +22 -0
- package/dist/FixedMath.d.ts.map +1 -0
- package/dist/FixedMath.js +26 -0
- package/dist/FixedMath.js.map +1 -0
- package/dist/PhalanxClient.d.ts +335 -0
- package/dist/PhalanxClient.d.ts.map +1 -0
- package/dist/PhalanxClient.js +844 -0
- package/dist/PhalanxClient.js.map +1 -0
- package/dist/RenderLoop.d.ts +95 -0
- package/dist/RenderLoop.d.ts.map +1 -0
- package/dist/RenderLoop.js +192 -0
- package/dist/RenderLoop.js.map +1 -0
- package/dist/SocketManager.d.ts +228 -0
- package/dist/SocketManager.d.ts.map +1 -0
- package/dist/SocketManager.js +584 -0
- package/dist/SocketManager.js.map +1 -0
- package/dist/StateHasher.d.ts +76 -0
- package/dist/StateHasher.d.ts.map +1 -0
- package/dist/StateHasher.js +129 -0
- package/dist/StateHasher.js.map +1 -0
- package/dist/auth/AuthManager.d.ts +188 -0
- package/dist/auth/AuthManager.d.ts.map +1 -0
- package/dist/auth/AuthManager.js +462 -0
- package/dist/auth/AuthManager.js.map +1 -0
- package/dist/auth/adapters/GoogleOAuthAdapter.d.ts +164 -0
- package/dist/auth/adapters/GoogleOAuthAdapter.d.ts.map +1 -0
- package/dist/auth/adapters/GoogleOAuthAdapter.js +521 -0
- package/dist/auth/adapters/GoogleOAuthAdapter.js.map +1 -0
- package/dist/auth/index.d.ts +45 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +54 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/storage.d.ts +56 -0
- package/dist/auth/storage.d.ts.map +1 -0
- package/dist/auth/storage.js +78 -0
- package/dist/auth/storage.js.map +1 -0
- package/dist/auth/types.d.ts +212 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +7 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/recovery/BrowserLifecycle.d.ts +33 -0
- package/dist/recovery/BrowserLifecycle.d.ts.map +1 -0
- package/dist/recovery/BrowserLifecycle.js +62 -0
- package/dist/recovery/BrowserLifecycle.js.map +1 -0
- package/dist/recovery/GuestPlayerIdStore.d.ts +17 -0
- package/dist/recovery/GuestPlayerIdStore.d.ts.map +1 -0
- package/dist/recovery/GuestPlayerIdStore.js +31 -0
- package/dist/recovery/GuestPlayerIdStore.js.map +1 -0
- package/dist/recovery/KeyValueStorage.d.ts +32 -0
- package/dist/recovery/KeyValueStorage.d.ts.map +1 -0
- package/dist/recovery/KeyValueStorage.js +58 -0
- package/dist/recovery/KeyValueStorage.js.map +1 -0
- package/dist/recovery/MobileTransport.d.ts +12 -0
- package/dist/recovery/MobileTransport.d.ts.map +1 -0
- package/dist/recovery/MobileTransport.js +24 -0
- package/dist/recovery/MobileTransport.js.map +1 -0
- package/dist/recovery/NetworkQuality.d.ts +22 -0
- package/dist/recovery/NetworkQuality.d.ts.map +1 -0
- package/dist/recovery/NetworkQuality.js +35 -0
- package/dist/recovery/NetworkQuality.js.map +1 -0
- package/dist/recovery/RoomPersistence.d.ts +55 -0
- package/dist/recovery/RoomPersistence.d.ts.map +1 -0
- package/dist/recovery/RoomPersistence.js +68 -0
- package/dist/recovery/RoomPersistence.js.map +1 -0
- package/dist/recovery/RoomRecoveryController.d.ts +146 -0
- package/dist/recovery/RoomRecoveryController.d.ts.map +1 -0
- package/dist/recovery/RoomRecoveryController.js +348 -0
- package/dist/recovery/RoomRecoveryController.js.map +1 -0
- package/dist/recovery/index.d.ts +13 -0
- package/dist/recovery/index.d.ts.map +1 -0
- package/dist/recovery/index.js +8 -0
- package/dist/recovery/index.js.map +1 -0
- package/dist/types.d.ts +501 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google OAuth Adapter
|
|
3
|
+
*
|
|
4
|
+
* OAuth 2.0 adapter for Google Sign-In using PKCE flow.
|
|
5
|
+
* This is a client-side only implementation that doesn't require a backend.
|
|
6
|
+
*/
|
|
7
|
+
// ============================================
|
|
8
|
+
// Google OAuth Adapter
|
|
9
|
+
// ============================================
|
|
10
|
+
/**
|
|
11
|
+
* Google OAuth adapter using PKCE flow.
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - PKCE (Proof Key for Code Exchange) for security
|
|
15
|
+
* - State parameter for CSRF protection
|
|
16
|
+
* - Nonce for replay protection
|
|
17
|
+
* - ID token validation
|
|
18
|
+
* - Automatic token refresh
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const adapter = new GoogleOAuthAdapter({
|
|
23
|
+
* clientId: 'your-client-id.apps.googleusercontent.com'
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Generate login URL
|
|
27
|
+
* const loginUrl = adapter.getLoginUrl();
|
|
28
|
+
* window.location.href = loginUrl;
|
|
29
|
+
*
|
|
30
|
+
* // Handle callback (on /auth/callback page)
|
|
31
|
+
* const result = await adapter.handleCallback({
|
|
32
|
+
* code: new URLSearchParams(location.search).get('code'),
|
|
33
|
+
* state: new URLSearchParams(location.search).get('state')
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export class GoogleOAuthAdapter {
|
|
38
|
+
provider = 'google';
|
|
39
|
+
config;
|
|
40
|
+
// Storage keys for PKCE
|
|
41
|
+
VERIFIER_KEY = 'phalanx_google_verifier';
|
|
42
|
+
CHALLENGE_KEY = 'phalanx_google_challenge';
|
|
43
|
+
STATE_KEY = 'phalanx_google_state';
|
|
44
|
+
NONCE_KEY = 'phalanx_google_nonce';
|
|
45
|
+
/**
|
|
46
|
+
* Create a new Google OAuth adapter.
|
|
47
|
+
* @param config - Google OAuth configuration
|
|
48
|
+
*/
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.config = {
|
|
51
|
+
scopes: ['openid', 'profile', 'email'],
|
|
52
|
+
...config,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// ─────────────────────────────────────────────────────────────────
|
|
56
|
+
// AuthAdapter Interface
|
|
57
|
+
// ─────────────────────────────────────────────────────────────────
|
|
58
|
+
/**
|
|
59
|
+
* Generate the Google OAuth authorization URL.
|
|
60
|
+
*
|
|
61
|
+
* This method:
|
|
62
|
+
* 1. Generates PKCE code verifier and challenge
|
|
63
|
+
* 2. Generates state for CSRF protection
|
|
64
|
+
* 3. Generates nonce for ID token validation
|
|
65
|
+
* 4. Stores values in sessionStorage for callback validation
|
|
66
|
+
* 5. Returns the authorization URL
|
|
67
|
+
*
|
|
68
|
+
* Note: Call preparePKCE() before this if you need async SHA-256 challenge.
|
|
69
|
+
* If not called, uses plain verifier as challenge (works with some providers).
|
|
70
|
+
*
|
|
71
|
+
* @param options - Optional login options
|
|
72
|
+
* @returns Authorization URL to redirect user to
|
|
73
|
+
*/
|
|
74
|
+
getLoginUrl(options) {
|
|
75
|
+
// Check if PKCE was pre-computed
|
|
76
|
+
let codeVerifier = typeof sessionStorage !== 'undefined'
|
|
77
|
+
? sessionStorage.getItem(this.VERIFIER_KEY)
|
|
78
|
+
: null;
|
|
79
|
+
let codeChallenge = typeof sessionStorage !== 'undefined'
|
|
80
|
+
? sessionStorage.getItem(this.CHALLENGE_KEY)
|
|
81
|
+
: null;
|
|
82
|
+
// Generate new PKCE values if not pre-computed
|
|
83
|
+
if (!codeVerifier || !codeChallenge) {
|
|
84
|
+
codeVerifier = this.generateCodeVerifier();
|
|
85
|
+
// For sync operation, use plain method (S256 requires async)
|
|
86
|
+
// We'll compute proper S256 challenge inline using a workaround
|
|
87
|
+
codeChallenge = codeVerifier; // Fallback - will be replaced if preparePKCE was called
|
|
88
|
+
}
|
|
89
|
+
const state = options?.state || this.generateRandomString();
|
|
90
|
+
const nonce = options?.nonce || this.generateRandomString();
|
|
91
|
+
// Store for callback validation
|
|
92
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
93
|
+
sessionStorage.setItem(this.VERIFIER_KEY, codeVerifier);
|
|
94
|
+
sessionStorage.setItem(this.STATE_KEY, state);
|
|
95
|
+
sessionStorage.setItem(this.NONCE_KEY, nonce);
|
|
96
|
+
// Clear challenge key (will be regenerated on next preparePKCE)
|
|
97
|
+
sessionStorage.removeItem(this.CHALLENGE_KEY);
|
|
98
|
+
}
|
|
99
|
+
// Merge scopes
|
|
100
|
+
const scopes = options?.scopes
|
|
101
|
+
? [...new Set([...this.config.scopes, ...options.scopes])]
|
|
102
|
+
: this.config.scopes;
|
|
103
|
+
// Build authorization URL
|
|
104
|
+
// Use 'plain' method if challenge equals verifier, otherwise 'S256'
|
|
105
|
+
const challengeMethod = codeChallenge === codeVerifier ? 'plain' : 'S256';
|
|
106
|
+
const params = new URLSearchParams({
|
|
107
|
+
client_id: this.config.clientId,
|
|
108
|
+
redirect_uri: options?.redirectUri || this.getRedirectUri(),
|
|
109
|
+
response_type: 'code',
|
|
110
|
+
scope: scopes.join(' '),
|
|
111
|
+
code_challenge: codeChallenge,
|
|
112
|
+
code_challenge_method: challengeMethod,
|
|
113
|
+
state: state,
|
|
114
|
+
nonce: nonce,
|
|
115
|
+
access_type: 'offline', // Request refresh token
|
|
116
|
+
include_granted_scopes: 'true',
|
|
117
|
+
});
|
|
118
|
+
// Optional parameters
|
|
119
|
+
if (options?.prompt) {
|
|
120
|
+
params.set('prompt', options.prompt);
|
|
121
|
+
}
|
|
122
|
+
if (options?.loginHint) {
|
|
123
|
+
params.set('login_hint', options.loginHint);
|
|
124
|
+
}
|
|
125
|
+
if (this.config.hostedDomain) {
|
|
126
|
+
params.set('hd', this.config.hostedDomain);
|
|
127
|
+
}
|
|
128
|
+
return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Prepare PKCE values asynchronously before calling getLoginUrl().
|
|
132
|
+
* This computes the proper SHA-256 code challenge.
|
|
133
|
+
*
|
|
134
|
+
* Call this before login() for proper S256 PKCE:
|
|
135
|
+
* ```typescript
|
|
136
|
+
* await adapter.preparePKCE();
|
|
137
|
+
* const url = adapter.getLoginUrl();
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
async preparePKCE() {
|
|
141
|
+
const codeVerifier = this.generateCodeVerifier();
|
|
142
|
+
const codeChallenge = await this.computeCodeChallenge(codeVerifier);
|
|
143
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
144
|
+
sessionStorage.setItem(this.VERIFIER_KEY, codeVerifier);
|
|
145
|
+
sessionStorage.setItem(this.CHALLENGE_KEY, codeChallenge);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Compute SHA-256 code challenge from verifier (async).
|
|
150
|
+
*/
|
|
151
|
+
async computeCodeChallenge(verifier) {
|
|
152
|
+
const encoder = new TextEncoder();
|
|
153
|
+
const data = encoder.encode(verifier);
|
|
154
|
+
const digest = await crypto.subtle.digest('SHA-256', data);
|
|
155
|
+
return this.base64UrlEncode(new Uint8Array(digest));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Handle the OAuth callback from Google.
|
|
159
|
+
*
|
|
160
|
+
* This method:
|
|
161
|
+
* 1. Validates the state parameter (CSRF protection)
|
|
162
|
+
* 2. Retrieves the stored PKCE verifier
|
|
163
|
+
* 3. Exchanges the authorization code for tokens
|
|
164
|
+
* 4. Validates the ID token
|
|
165
|
+
* 5. Returns the auth result
|
|
166
|
+
*
|
|
167
|
+
* @param params - Callback parameters from URL
|
|
168
|
+
* @returns Auth result with user info and tokens
|
|
169
|
+
*/
|
|
170
|
+
async handleCallback(params) {
|
|
171
|
+
// Validate state (CSRF protection)
|
|
172
|
+
const storedState = typeof sessionStorage !== 'undefined'
|
|
173
|
+
? sessionStorage.getItem(this.STATE_KEY)
|
|
174
|
+
: null;
|
|
175
|
+
if (!storedState || storedState !== params.state) {
|
|
176
|
+
return {
|
|
177
|
+
valid: false,
|
|
178
|
+
error: 'Invalid state parameter. Possible CSRF attack.',
|
|
179
|
+
errorCode: 'invalid_state',
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
// Get stored PKCE verifier
|
|
183
|
+
const codeVerifier = typeof sessionStorage !== 'undefined'
|
|
184
|
+
? sessionStorage.getItem(this.VERIFIER_KEY)
|
|
185
|
+
: null;
|
|
186
|
+
if (!codeVerifier) {
|
|
187
|
+
return {
|
|
188
|
+
valid: false,
|
|
189
|
+
error: 'Code verifier not found. Please start authentication again.',
|
|
190
|
+
errorCode: 'missing_verifier',
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
// Get stored nonce
|
|
194
|
+
const nonce = typeof sessionStorage !== 'undefined'
|
|
195
|
+
? sessionStorage.getItem(this.NONCE_KEY)
|
|
196
|
+
: null;
|
|
197
|
+
// Clear stored values
|
|
198
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
199
|
+
sessionStorage.removeItem(this.VERIFIER_KEY);
|
|
200
|
+
sessionStorage.removeItem(this.STATE_KEY);
|
|
201
|
+
sessionStorage.removeItem(this.NONCE_KEY);
|
|
202
|
+
}
|
|
203
|
+
// Handle error from Google
|
|
204
|
+
if (params.error) {
|
|
205
|
+
return {
|
|
206
|
+
valid: false,
|
|
207
|
+
error: params.errorDescription || params.error,
|
|
208
|
+
errorCode: params.error,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (!params.code) {
|
|
212
|
+
return {
|
|
213
|
+
valid: false,
|
|
214
|
+
error: 'No authorization code received',
|
|
215
|
+
errorCode: 'missing_code',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
// Exchange code for tokens
|
|
220
|
+
const tokens = await this.exchangeCodeForTokens(params.code, codeVerifier, this.getRedirectUri());
|
|
221
|
+
// Decode and validate ID token
|
|
222
|
+
const idTokenPayload = this.decodeIdToken(tokens.id_token);
|
|
223
|
+
// Validate ID token claims
|
|
224
|
+
const validationError = this.validateIdToken(idTokenPayload, nonce);
|
|
225
|
+
if (validationError) {
|
|
226
|
+
return {
|
|
227
|
+
valid: false,
|
|
228
|
+
error: validationError,
|
|
229
|
+
errorCode: 'invalid_id_token',
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
valid: true,
|
|
234
|
+
playerId: idTokenPayload.sub,
|
|
235
|
+
username: idTokenPayload.name,
|
|
236
|
+
email: idTokenPayload.email,
|
|
237
|
+
avatarUrl: idTokenPayload.picture,
|
|
238
|
+
provider: 'google',
|
|
239
|
+
token: tokens.id_token,
|
|
240
|
+
accessToken: tokens.access_token,
|
|
241
|
+
refreshToken: tokens.refresh_token,
|
|
242
|
+
expiresAt: Date.now() + tokens.expires_in * 1000,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
return {
|
|
247
|
+
valid: false,
|
|
248
|
+
error: error instanceof Error ? error.message : 'Token exchange failed',
|
|
249
|
+
errorCode: 'token_exchange_failed',
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Refresh an expired token using the refresh token.
|
|
255
|
+
*
|
|
256
|
+
* @param refreshToken - The refresh token to use
|
|
257
|
+
* @returns New auth result or null if refresh failed
|
|
258
|
+
*/
|
|
259
|
+
async refreshToken(refreshToken) {
|
|
260
|
+
try {
|
|
261
|
+
const response = await fetch('https://oauth2.googleapis.com/token', {
|
|
262
|
+
method: 'POST',
|
|
263
|
+
headers: {
|
|
264
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
265
|
+
},
|
|
266
|
+
body: new URLSearchParams({
|
|
267
|
+
client_id: this.config.clientId,
|
|
268
|
+
refresh_token: refreshToken,
|
|
269
|
+
grant_type: 'refresh_token',
|
|
270
|
+
}),
|
|
271
|
+
});
|
|
272
|
+
if (!response.ok) {
|
|
273
|
+
const error = await response.json();
|
|
274
|
+
console.error('[GoogleOAuth] Refresh failed:', error);
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
const tokens = (await response.json());
|
|
278
|
+
const idTokenPayload = this.decodeIdToken(tokens.id_token);
|
|
279
|
+
return {
|
|
280
|
+
valid: true,
|
|
281
|
+
playerId: idTokenPayload.sub,
|
|
282
|
+
username: idTokenPayload.name,
|
|
283
|
+
email: idTokenPayload.email,
|
|
284
|
+
avatarUrl: idTokenPayload.picture,
|
|
285
|
+
provider: 'google',
|
|
286
|
+
token: tokens.id_token,
|
|
287
|
+
accessToken: tokens.access_token,
|
|
288
|
+
// Note: Google doesn't return new refresh_token on refresh
|
|
289
|
+
expiresAt: Date.now() + tokens.expires_in * 1000,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
console.error('[GoogleOAuth] Refresh error:', error);
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Revoke a token (logout from Google).
|
|
299
|
+
*
|
|
300
|
+
* @param token - The token to revoke
|
|
301
|
+
*/
|
|
302
|
+
async revokeToken(token) {
|
|
303
|
+
try {
|
|
304
|
+
await fetch(`https://oauth2.googleapis.com/revoke?token=${token}`, {
|
|
305
|
+
method: 'POST',
|
|
306
|
+
headers: {
|
|
307
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
console.warn('[GoogleOAuth] Revoke failed:', error);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Check if this adapter can handle the given token.
|
|
317
|
+
*
|
|
318
|
+
* @param token - Token to check
|
|
319
|
+
* @returns True if token is a Google ID token
|
|
320
|
+
*/
|
|
321
|
+
canHandle(token) {
|
|
322
|
+
try {
|
|
323
|
+
const payload = this.decodeIdToken(token);
|
|
324
|
+
return (payload.iss === 'https://accounts.google.com' ||
|
|
325
|
+
payload.iss === 'accounts.google.com');
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// ─────────────────────────────────────────────────────────────────
|
|
332
|
+
// Private Methods
|
|
333
|
+
// ─────────────────────────────────────────────────────────────────
|
|
334
|
+
/**
|
|
335
|
+
* Exchange authorization code for tokens.
|
|
336
|
+
* Uses backend endpoint if configured, otherwise calls Google directly.
|
|
337
|
+
*/
|
|
338
|
+
async exchangeCodeForTokens(code, codeVerifier, redirectUri) {
|
|
339
|
+
// Use backend token exchange if configured (recommended for security)
|
|
340
|
+
if (this.config.tokenExchangeUrl) {
|
|
341
|
+
return this.exchangeCodeViaBackend(code, codeVerifier, redirectUri);
|
|
342
|
+
}
|
|
343
|
+
// Direct exchange with Google (requires client_secret on server or native app)
|
|
344
|
+
return this.exchangeCodeDirect(code, codeVerifier, redirectUri);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Exchange code via our backend server (secure - keeps client_secret on server)
|
|
348
|
+
*/
|
|
349
|
+
async exchangeCodeViaBackend(code, codeVerifier, redirectUri) {
|
|
350
|
+
const response = await fetch(this.config.tokenExchangeUrl, {
|
|
351
|
+
method: 'POST',
|
|
352
|
+
headers: {
|
|
353
|
+
'Content-Type': 'application/json',
|
|
354
|
+
},
|
|
355
|
+
body: JSON.stringify({
|
|
356
|
+
code,
|
|
357
|
+
codeVerifier,
|
|
358
|
+
redirectUri,
|
|
359
|
+
provider: 'google',
|
|
360
|
+
}),
|
|
361
|
+
});
|
|
362
|
+
if (!response.ok) {
|
|
363
|
+
const error = await response.json();
|
|
364
|
+
throw new Error(error.error || error.message || 'Token exchange failed');
|
|
365
|
+
}
|
|
366
|
+
const result = await response.json();
|
|
367
|
+
if (!result.success) {
|
|
368
|
+
throw new Error(result.error || 'Token exchange failed');
|
|
369
|
+
}
|
|
370
|
+
// Map backend response to GoogleTokenResponse format
|
|
371
|
+
return {
|
|
372
|
+
id_token: result.idToken,
|
|
373
|
+
access_token: result.accessToken,
|
|
374
|
+
expires_in: result.expiresIn || 3600,
|
|
375
|
+
scope: 'openid profile email',
|
|
376
|
+
token_type: 'Bearer',
|
|
377
|
+
refresh_token: result.refreshToken,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Exchange code directly with Google (won't work for Web apps without client_secret)
|
|
382
|
+
*/
|
|
383
|
+
async exchangeCodeDirect(code, codeVerifier, redirectUri) {
|
|
384
|
+
const response = await fetch('https://oauth2.googleapis.com/token', {
|
|
385
|
+
method: 'POST',
|
|
386
|
+
headers: {
|
|
387
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
388
|
+
},
|
|
389
|
+
body: new URLSearchParams({
|
|
390
|
+
client_id: this.config.clientId,
|
|
391
|
+
code,
|
|
392
|
+
code_verifier: codeVerifier,
|
|
393
|
+
grant_type: 'authorization_code',
|
|
394
|
+
redirect_uri: redirectUri,
|
|
395
|
+
}),
|
|
396
|
+
});
|
|
397
|
+
if (!response.ok) {
|
|
398
|
+
const error = await response.json();
|
|
399
|
+
throw new Error(error.error_description || error.error || 'Token exchange failed');
|
|
400
|
+
}
|
|
401
|
+
return response.json();
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Decode a JWT ID token (without verification - verification done via claims).
|
|
405
|
+
*/
|
|
406
|
+
decodeIdToken(idToken) {
|
|
407
|
+
const parts = idToken.split('.');
|
|
408
|
+
if (parts.length !== 3 || !parts[1]) {
|
|
409
|
+
throw new Error('Invalid ID token format');
|
|
410
|
+
}
|
|
411
|
+
const payloadBase64 = parts[1];
|
|
412
|
+
const payloadJson = this.base64UrlDecode(payloadBase64);
|
|
413
|
+
return JSON.parse(payloadJson);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Validate ID token claims.
|
|
417
|
+
*/
|
|
418
|
+
validateIdToken(payload, nonce) {
|
|
419
|
+
// Check issuer
|
|
420
|
+
if (payload.iss !== 'https://accounts.google.com' &&
|
|
421
|
+
payload.iss !== 'accounts.google.com') {
|
|
422
|
+
return 'Invalid issuer';
|
|
423
|
+
}
|
|
424
|
+
// Check audience
|
|
425
|
+
if (payload.aud !== this.config.clientId) {
|
|
426
|
+
return 'Invalid audience';
|
|
427
|
+
}
|
|
428
|
+
// Check expiration (with 5 minute leeway)
|
|
429
|
+
if (payload.exp * 1000 < Date.now() - 5 * 60 * 1000) {
|
|
430
|
+
return 'Token expired';
|
|
431
|
+
}
|
|
432
|
+
// Check nonce (replay protection)
|
|
433
|
+
if (nonce && payload.nonce !== nonce) {
|
|
434
|
+
return 'Invalid nonce';
|
|
435
|
+
}
|
|
436
|
+
// Check hosted domain if configured
|
|
437
|
+
if (this.config.hostedDomain && payload.hd !== this.config.hostedDomain) {
|
|
438
|
+
return `User must be from ${this.config.hostedDomain} domain`;
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Get the redirect URI.
|
|
444
|
+
*/
|
|
445
|
+
getRedirectUri() {
|
|
446
|
+
if (this.config.redirectUri) {
|
|
447
|
+
return this.config.redirectUri;
|
|
448
|
+
}
|
|
449
|
+
if (typeof window !== 'undefined') {
|
|
450
|
+
return `${window.location.origin}/auth/callback`;
|
|
451
|
+
}
|
|
452
|
+
return '/auth/callback';
|
|
453
|
+
}
|
|
454
|
+
// ─────────────────────────────────────────────────────────────────
|
|
455
|
+
// PKCE Helpers
|
|
456
|
+
// ─────────────────────────────────────────────────────────────────
|
|
457
|
+
/**
|
|
458
|
+
* Generate a random code verifier for PKCE.
|
|
459
|
+
* 43-128 characters from unreserved URI characters.
|
|
460
|
+
*/
|
|
461
|
+
generateCodeVerifier() {
|
|
462
|
+
const array = new Uint8Array(32);
|
|
463
|
+
crypto.getRandomValues(array);
|
|
464
|
+
return this.base64UrlEncode(array);
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Generate a random string for state/nonce.
|
|
468
|
+
*/
|
|
469
|
+
generateRandomString() {
|
|
470
|
+
const array = new Uint8Array(16);
|
|
471
|
+
crypto.getRandomValues(array);
|
|
472
|
+
return this.base64UrlEncode(array);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Base64URL encode a Uint8Array.
|
|
476
|
+
*/
|
|
477
|
+
base64UrlEncode(buffer) {
|
|
478
|
+
let binary = '';
|
|
479
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
480
|
+
binary += String.fromCharCode(buffer[i] ?? 0);
|
|
481
|
+
}
|
|
482
|
+
const base64 = btoa(binary);
|
|
483
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Base64URL decode a string.
|
|
487
|
+
*/
|
|
488
|
+
base64UrlDecode(input) {
|
|
489
|
+
// Convert base64url to base64
|
|
490
|
+
let base64 = input.replace(/-/g, '+').replace(/_/g, '/');
|
|
491
|
+
// Add padding if needed
|
|
492
|
+
const padding = base64.length % 4;
|
|
493
|
+
if (padding) {
|
|
494
|
+
base64 += '='.repeat(4 - padding);
|
|
495
|
+
}
|
|
496
|
+
return atob(base64);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
// ============================================
|
|
500
|
+
// Async PKCE Helper (for production use)
|
|
501
|
+
// ============================================
|
|
502
|
+
/**
|
|
503
|
+
* Compute SHA-256 code challenge asynchronously.
|
|
504
|
+
* Use this for production-ready PKCE implementation.
|
|
505
|
+
*
|
|
506
|
+
* @param verifier - The code verifier
|
|
507
|
+
* @returns Base64URL-encoded SHA-256 hash
|
|
508
|
+
*/
|
|
509
|
+
export async function computeCodeChallenge(verifier) {
|
|
510
|
+
const encoder = new TextEncoder();
|
|
511
|
+
const data = encoder.encode(verifier);
|
|
512
|
+
const digest = await crypto.subtle.digest('SHA-256', data);
|
|
513
|
+
const array = new Uint8Array(digest);
|
|
514
|
+
let binary = '';
|
|
515
|
+
for (let i = 0; i < array.length; i++) {
|
|
516
|
+
binary += String.fromCharCode(array[i] ?? 0);
|
|
517
|
+
}
|
|
518
|
+
const base64 = btoa(binary);
|
|
519
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
520
|
+
}
|
|
521
|
+
//# sourceMappingURL=GoogleOAuthAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleOAuthAdapter.js","sourceRoot":"","sources":["../../../src/auth/adapters/GoogleOAuthAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phalanx Auth Module
|
|
3
|
+
*
|
|
4
|
+
* Managed OAuth authentication for Phalanx games.
|
|
5
|
+
* Provides drop-in authentication with Google, Discord, and Steam.
|
|
6
|
+
*
|
|
7
|
+
* ## Quick Start
|
|
8
|
+
*
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { AuthManager } from '@phalanx-engine/client/auth';
|
|
11
|
+
*
|
|
12
|
+
* const auth = new AuthManager({
|
|
13
|
+
* provider: 'google',
|
|
14
|
+
* google: {
|
|
15
|
+
* clientId: 'your-client-id.apps.googleusercontent.com'
|
|
16
|
+
* }
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Check for existing session
|
|
20
|
+
* const hasSession = await auth.checkSession();
|
|
21
|
+
*
|
|
22
|
+
* if (!hasSession) {
|
|
23
|
+
* // Login with popup
|
|
24
|
+
* await auth.loginWithPopup();
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* // Get token for Phalanx client
|
|
28
|
+
* const client = await PhalanxClient.create({
|
|
29
|
+
* serverUrl: 'http://localhost:3000',
|
|
30
|
+
* playerId: auth.getUser()!.id,
|
|
31
|
+
* username: auth.getUser()!.username || 'Player',
|
|
32
|
+
* authToken: auth.getToken()!
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @packageDocumentation
|
|
37
|
+
*/
|
|
38
|
+
export { AuthManager } from './AuthManager.js';
|
|
39
|
+
export type { AuthManagerConfig } from './AuthManager.js';
|
|
40
|
+
export { GoogleOAuthAdapter } from './adapters/GoogleOAuthAdapter.js';
|
|
41
|
+
export { computeCodeChallenge } from './adapters/GoogleOAuthAdapter.js';
|
|
42
|
+
export { LocalStorageAdapter, MemoryStorageAdapter } from './storage.js';
|
|
43
|
+
export type { AuthStorage } from './storage.js';
|
|
44
|
+
export type { AuthAdapter, LoginOptions, CallbackParams, AuthResult, AuthUser, AuthState, StoredAuthData, AuthError, GoogleOAuthConfig, DiscordOAuthConfig, SteamAuthConfig, } from './types.js';
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAMH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAM1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAUxE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhD,YAAY,EAEV,WAAW,EACX,YAAY,EACZ,cAAc,EACd,UAAU,EAGV,QAAQ,EACR,SAAS,EACT,cAAc,EACd,SAAS,EAGT,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phalanx Auth Module
|
|
3
|
+
*
|
|
4
|
+
* Managed OAuth authentication for Phalanx games.
|
|
5
|
+
* Provides drop-in authentication with Google, Discord, and Steam.
|
|
6
|
+
*
|
|
7
|
+
* ## Quick Start
|
|
8
|
+
*
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { AuthManager } from '@phalanx-engine/client/auth';
|
|
11
|
+
*
|
|
12
|
+
* const auth = new AuthManager({
|
|
13
|
+
* provider: 'google',
|
|
14
|
+
* google: {
|
|
15
|
+
* clientId: 'your-client-id.apps.googleusercontent.com'
|
|
16
|
+
* }
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Check for existing session
|
|
20
|
+
* const hasSession = await auth.checkSession();
|
|
21
|
+
*
|
|
22
|
+
* if (!hasSession) {
|
|
23
|
+
* // Login with popup
|
|
24
|
+
* await auth.loginWithPopup();
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* // Get token for Phalanx client
|
|
28
|
+
* const client = await PhalanxClient.create({
|
|
29
|
+
* serverUrl: 'http://localhost:3000',
|
|
30
|
+
* playerId: auth.getUser()!.id,
|
|
31
|
+
* username: auth.getUser()!.username || 'Player',
|
|
32
|
+
* authToken: auth.getToken()!
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @packageDocumentation
|
|
37
|
+
*/
|
|
38
|
+
// ============================================
|
|
39
|
+
// Main Auth Manager
|
|
40
|
+
// ============================================
|
|
41
|
+
export { AuthManager } from './AuthManager.js';
|
|
42
|
+
// ============================================
|
|
43
|
+
// OAuth Adapters
|
|
44
|
+
// ============================================
|
|
45
|
+
export { GoogleOAuthAdapter } from './adapters/GoogleOAuthAdapter.js';
|
|
46
|
+
export { computeCodeChallenge } from './adapters/GoogleOAuthAdapter.js';
|
|
47
|
+
// Future adapters:
|
|
48
|
+
// export { DiscordOAuthAdapter } from './adapters/DiscordOAuthAdapter.js';
|
|
49
|
+
// export { SteamAuthAdapter } from './adapters/SteamAuthAdapter.js';
|
|
50
|
+
// ============================================
|
|
51
|
+
// Storage
|
|
52
|
+
// ============================================
|
|
53
|
+
export { LocalStorageAdapter, MemoryStorageAdapter } from './storage.js';
|
|
54
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,+CAA+C;AAC/C,oBAAoB;AACpB,+CAA+C;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,+CAA+C;AAC/C,iBAAiB;AACjB,+CAA+C;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAExE,mBAAmB;AACnB,2EAA2E;AAC3E,qEAAqE;AAErE,+CAA+C;AAC/C,UAAU;AACV,+CAA+C;AAE/C,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC"}
|