@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,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phalanx Auth Manager
|
|
3
|
+
*
|
|
4
|
+
* Central manager for OAuth authentication.
|
|
5
|
+
* Handles auth state lifecycle, coordinates adapters, manages token refresh,
|
|
6
|
+
* and persists sessions to storage.
|
|
7
|
+
*/
|
|
8
|
+
import { LocalStorageAdapter } from './storage.js';
|
|
9
|
+
import { GoogleOAuthAdapter } from './adapters/GoogleOAuthAdapter.js';
|
|
10
|
+
// ============================================
|
|
11
|
+
// Auth Manager
|
|
12
|
+
// ============================================
|
|
13
|
+
/**
|
|
14
|
+
* Main authentication manager.
|
|
15
|
+
*
|
|
16
|
+
* Provides a simple API for OAuth authentication with automatic
|
|
17
|
+
* token refresh, session persistence, and state management.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const auth = new AuthManager({
|
|
22
|
+
* provider: 'google',
|
|
23
|
+
* google: {
|
|
24
|
+
* clientId: 'your-client-id.apps.googleusercontent.com'
|
|
25
|
+
* },
|
|
26
|
+
* onAuthStateChange: (state) => {
|
|
27
|
+
* console.log('Auth state changed:', state);
|
|
28
|
+
* }
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Check for existing session on app startup
|
|
32
|
+
* const hasSession = await auth.checkSession();
|
|
33
|
+
*
|
|
34
|
+
* if (!hasSession) {
|
|
35
|
+
* // Redirect to login
|
|
36
|
+
* auth.login();
|
|
37
|
+
* // Or use popup
|
|
38
|
+
* const result = await auth.loginWithPopup();
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* // Get token for Phalanx client
|
|
42
|
+
* const token = auth.getToken();
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export class AuthManager {
|
|
46
|
+
adapter;
|
|
47
|
+
storage;
|
|
48
|
+
config;
|
|
49
|
+
state;
|
|
50
|
+
refreshTimer;
|
|
51
|
+
/**
|
|
52
|
+
* Create a new AuthManager.
|
|
53
|
+
* @param config - Auth manager configuration
|
|
54
|
+
*/
|
|
55
|
+
constructor(config) {
|
|
56
|
+
this.config = {
|
|
57
|
+
autoRefresh: true,
|
|
58
|
+
refreshBeforeExpiryMs: 60000,
|
|
59
|
+
storageKey: 'phalanx_auth',
|
|
60
|
+
...config,
|
|
61
|
+
};
|
|
62
|
+
this.storage =
|
|
63
|
+
config.storage || new LocalStorageAdapter(this.config.storageKey);
|
|
64
|
+
this.adapter = this.createAdapter(config);
|
|
65
|
+
this.state = this.getInitialState();
|
|
66
|
+
this.log('AuthManager initialized', { provider: config.provider });
|
|
67
|
+
}
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────
|
|
69
|
+
// Public API
|
|
70
|
+
// ─────────────────────────────────────────────────────────────────
|
|
71
|
+
/**
|
|
72
|
+
* Check for existing session and restore it.
|
|
73
|
+
* Call this on app startup to restore user session.
|
|
74
|
+
*
|
|
75
|
+
* @returns True if a valid session was restored
|
|
76
|
+
*/
|
|
77
|
+
async checkSession() {
|
|
78
|
+
this.log('Checking for existing session...');
|
|
79
|
+
const stored = await this.storage.get();
|
|
80
|
+
if (!stored || !stored.token) {
|
|
81
|
+
this.log('No stored session found');
|
|
82
|
+
this.updateState({
|
|
83
|
+
...this.state,
|
|
84
|
+
isLoading: false,
|
|
85
|
+
});
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
// Check if token is expired
|
|
89
|
+
if (stored.expiresAt && Date.now() >= stored.expiresAt) {
|
|
90
|
+
this.log('Stored token is expired, attempting refresh...');
|
|
91
|
+
// Try to refresh
|
|
92
|
+
if (stored.refreshToken) {
|
|
93
|
+
const result = await this.adapter.refreshToken(stored.refreshToken);
|
|
94
|
+
if (result?.valid) {
|
|
95
|
+
await this.handleAuthSuccess(result, stored.refreshToken);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Refresh failed, clear session
|
|
100
|
+
this.log('Token refresh failed, clearing session');
|
|
101
|
+
await this.logout();
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
// Restore session
|
|
105
|
+
this.log('Restoring session for user:', stored.user.id);
|
|
106
|
+
this.updateState({
|
|
107
|
+
isAuthenticated: true,
|
|
108
|
+
isLoading: false,
|
|
109
|
+
user: stored.user,
|
|
110
|
+
token: stored.token,
|
|
111
|
+
expiresAt: stored.expiresAt || null,
|
|
112
|
+
provider: stored.provider,
|
|
113
|
+
});
|
|
114
|
+
this.scheduleTokenRefresh();
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Start login flow using redirect.
|
|
119
|
+
* User will be redirected to the OAuth provider.
|
|
120
|
+
*
|
|
121
|
+
* @param options - Optional login options
|
|
122
|
+
*/
|
|
123
|
+
login(options) {
|
|
124
|
+
this.log('Starting login redirect flow...');
|
|
125
|
+
// Prepare PKCE asynchronously, then redirect
|
|
126
|
+
void this.prepareAndRedirect(options);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Prepare PKCE and redirect to OAuth provider.
|
|
130
|
+
*/
|
|
131
|
+
async prepareAndRedirect(options) {
|
|
132
|
+
// Prepare PKCE if the adapter supports it
|
|
133
|
+
if ('preparePKCE' in this.adapter && typeof this.adapter.preparePKCE === 'function') {
|
|
134
|
+
await this.adapter.preparePKCE();
|
|
135
|
+
}
|
|
136
|
+
const url = this.adapter.getLoginUrl(options);
|
|
137
|
+
if (typeof window !== 'undefined') {
|
|
138
|
+
window.location.href = url;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
throw new Error('login() requires a browser environment');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Start login flow using popup.
|
|
146
|
+
* Better UX as user stays on the game page.
|
|
147
|
+
*
|
|
148
|
+
* @param options - Optional login options
|
|
149
|
+
* @returns Auth result after popup flow completes
|
|
150
|
+
*/
|
|
151
|
+
async loginWithPopup(options) {
|
|
152
|
+
this.log('Starting login popup flow...');
|
|
153
|
+
if (typeof window === 'undefined') {
|
|
154
|
+
throw new Error('loginWithPopup() requires a browser environment');
|
|
155
|
+
}
|
|
156
|
+
// Prepare PKCE if the adapter supports it
|
|
157
|
+
if ('preparePKCE' in this.adapter && typeof this.adapter.preparePKCE === 'function') {
|
|
158
|
+
await this.adapter.preparePKCE();
|
|
159
|
+
}
|
|
160
|
+
return new Promise((resolve, reject) => {
|
|
161
|
+
const url = this.adapter.getLoginUrl(options);
|
|
162
|
+
const width = 500;
|
|
163
|
+
const height = 600;
|
|
164
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
165
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
166
|
+
const popup = window.open(url, 'phalanx-auth', `width=${width},height=${height},left=${left},top=${top},scrollbars=yes`);
|
|
167
|
+
if (!popup) {
|
|
168
|
+
const error = new Error('Popup blocked. Please allow popups for this site.');
|
|
169
|
+
this.config.onAuthError?.(error);
|
|
170
|
+
reject(error);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// Listen for callback message from popup
|
|
174
|
+
const handleMessage = async (event) => {
|
|
175
|
+
// Validate origin
|
|
176
|
+
if (event.origin !== window.location.origin)
|
|
177
|
+
return;
|
|
178
|
+
if (event.data?.type !== 'phalanx-auth-callback')
|
|
179
|
+
return;
|
|
180
|
+
this.log('Received auth callback from popup');
|
|
181
|
+
window.removeEventListener('message', handleMessage);
|
|
182
|
+
popup.close();
|
|
183
|
+
try {
|
|
184
|
+
const result = await this.adapter.handleCallback(event.data.params);
|
|
185
|
+
if (result.valid) {
|
|
186
|
+
await this.handleAuthSuccess(result);
|
|
187
|
+
}
|
|
188
|
+
resolve(result);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
this.config.onAuthError?.(error);
|
|
192
|
+
reject(error);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
window.addEventListener('message', handleMessage);
|
|
196
|
+
// Check if popup was closed without completing auth
|
|
197
|
+
const checkClosed = setInterval(() => {
|
|
198
|
+
if (popup.closed) {
|
|
199
|
+
clearInterval(checkClosed);
|
|
200
|
+
window.removeEventListener('message', handleMessage);
|
|
201
|
+
const error = new Error('Authentication cancelled');
|
|
202
|
+
this.config.onAuthError?.(error);
|
|
203
|
+
reject(error);
|
|
204
|
+
}
|
|
205
|
+
}, 500);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Handle OAuth callback.
|
|
210
|
+
* Call this on your callback page.
|
|
211
|
+
*
|
|
212
|
+
* @param params - Callback parameters (parsed from URL if not provided)
|
|
213
|
+
* @returns Auth result
|
|
214
|
+
*/
|
|
215
|
+
async handleCallback(params) {
|
|
216
|
+
this.log('Handling OAuth callback...');
|
|
217
|
+
const callbackParams = params || this.parseCallbackFromUrl();
|
|
218
|
+
if (callbackParams.error) {
|
|
219
|
+
this.log('Callback error:', callbackParams.error);
|
|
220
|
+
return {
|
|
221
|
+
valid: false,
|
|
222
|
+
error: callbackParams.errorDescription || callbackParams.error,
|
|
223
|
+
errorCode: callbackParams.error,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const result = await this.adapter.handleCallback(callbackParams);
|
|
227
|
+
if (result.valid) {
|
|
228
|
+
await this.handleAuthSuccess(result);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
this.config.onAuthError?.(new Error(result.error || 'Auth failed'));
|
|
232
|
+
}
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Logout and clear session.
|
|
237
|
+
* Optionally revokes the token with the provider.
|
|
238
|
+
*/
|
|
239
|
+
async logout() {
|
|
240
|
+
this.log('Logging out...');
|
|
241
|
+
if (this.refreshTimer) {
|
|
242
|
+
clearTimeout(this.refreshTimer);
|
|
243
|
+
this.refreshTimer = undefined;
|
|
244
|
+
}
|
|
245
|
+
// Get stored data to retrieve access token for revocation
|
|
246
|
+
const stored = await this.storage.get();
|
|
247
|
+
const accessToken = stored?.accessToken;
|
|
248
|
+
// Clear state first
|
|
249
|
+
await this.storage.clear();
|
|
250
|
+
this.updateState(this.getInitialState());
|
|
251
|
+
this.state.isLoading = false;
|
|
252
|
+
this.updateState(this.state);
|
|
253
|
+
// Revoke access token if available (Google requires access token, not ID token)
|
|
254
|
+
if (accessToken && this.adapter.revokeToken) {
|
|
255
|
+
try {
|
|
256
|
+
await this.adapter.revokeToken(accessToken);
|
|
257
|
+
this.log('Token revoked successfully');
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
console.warn('[AuthManager] Failed to revoke token:', error);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Get current auth state.
|
|
266
|
+
* @returns Copy of current auth state
|
|
267
|
+
*/
|
|
268
|
+
getState() {
|
|
269
|
+
return { ...this.state };
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get current token (for Phalanx client).
|
|
273
|
+
* @returns Current auth token or null
|
|
274
|
+
*/
|
|
275
|
+
getToken() {
|
|
276
|
+
return this.state.token;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Check if user is authenticated.
|
|
280
|
+
* @returns True if user is authenticated
|
|
281
|
+
*/
|
|
282
|
+
isAuthenticated() {
|
|
283
|
+
return this.state.isAuthenticated;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get current user info.
|
|
287
|
+
* @returns User info or null
|
|
288
|
+
*/
|
|
289
|
+
getUser() {
|
|
290
|
+
return this.state.user;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Get the OAuth provider name.
|
|
294
|
+
* @returns Provider name
|
|
295
|
+
*/
|
|
296
|
+
getProvider() {
|
|
297
|
+
return this.adapter.provider;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Manually refresh the current token.
|
|
301
|
+
* @returns True if refresh was successful
|
|
302
|
+
*/
|
|
303
|
+
async refreshToken() {
|
|
304
|
+
this.log('Manually refreshing token...');
|
|
305
|
+
const stored = await this.storage.get();
|
|
306
|
+
if (!stored?.refreshToken) {
|
|
307
|
+
this.log('No refresh token available');
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
const result = await this.adapter.refreshToken(stored.refreshToken);
|
|
311
|
+
if (result?.valid) {
|
|
312
|
+
await this.handleAuthSuccess(result, stored.refreshToken);
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
this.log('Token refresh failed');
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Get the login URL without redirecting.
|
|
320
|
+
* Useful for custom login UI.
|
|
321
|
+
*
|
|
322
|
+
* @param options - Optional login options
|
|
323
|
+
* @returns Authorization URL
|
|
324
|
+
*/
|
|
325
|
+
getLoginUrl(options) {
|
|
326
|
+
return this.adapter.getLoginUrl(options);
|
|
327
|
+
}
|
|
328
|
+
// ─────────────────────────────────────────────────────────────────
|
|
329
|
+
// Private Methods
|
|
330
|
+
// ─────────────────────────────────────────────────────────────────
|
|
331
|
+
/**
|
|
332
|
+
* Create the appropriate adapter based on configuration.
|
|
333
|
+
*/
|
|
334
|
+
createAdapter(config) {
|
|
335
|
+
switch (config.provider) {
|
|
336
|
+
case 'google':
|
|
337
|
+
if (!config.google) {
|
|
338
|
+
throw new Error('Google config required when provider is "google"');
|
|
339
|
+
}
|
|
340
|
+
return new GoogleOAuthAdapter(config.google);
|
|
341
|
+
case 'discord':
|
|
342
|
+
if (!config.discord) {
|
|
343
|
+
throw new Error('Discord config required when provider is "discord"');
|
|
344
|
+
}
|
|
345
|
+
// DiscordOAuthAdapter not implemented yet
|
|
346
|
+
throw new Error('Discord OAuth adapter not implemented yet');
|
|
347
|
+
case 'steam':
|
|
348
|
+
if (!config.steam) {
|
|
349
|
+
throw new Error('Steam config required when provider is "steam"');
|
|
350
|
+
}
|
|
351
|
+
// SteamAuthAdapter not implemented yet
|
|
352
|
+
throw new Error('Steam auth adapter not implemented yet');
|
|
353
|
+
default:
|
|
354
|
+
throw new Error(`Unknown provider: ${config.provider}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Handle successful authentication.
|
|
359
|
+
*/
|
|
360
|
+
async handleAuthSuccess(result, existingRefreshToken) {
|
|
361
|
+
this.log('Auth success for user:', result.playerId);
|
|
362
|
+
const user = {
|
|
363
|
+
id: result.playerId,
|
|
364
|
+
username: result.username,
|
|
365
|
+
email: result.email,
|
|
366
|
+
avatarUrl: result.avatarUrl,
|
|
367
|
+
provider: result.provider,
|
|
368
|
+
};
|
|
369
|
+
const storedData = {
|
|
370
|
+
user,
|
|
371
|
+
token: result.token,
|
|
372
|
+
accessToken: result.accessToken,
|
|
373
|
+
refreshToken: result.refreshToken || existingRefreshToken,
|
|
374
|
+
expiresAt: result.expiresAt,
|
|
375
|
+
provider: result.provider,
|
|
376
|
+
};
|
|
377
|
+
await this.storage.set(storedData);
|
|
378
|
+
this.updateState({
|
|
379
|
+
isAuthenticated: true,
|
|
380
|
+
isLoading: false,
|
|
381
|
+
user,
|
|
382
|
+
token: result.token,
|
|
383
|
+
expiresAt: result.expiresAt || null,
|
|
384
|
+
provider: result.provider,
|
|
385
|
+
});
|
|
386
|
+
this.scheduleTokenRefresh();
|
|
387
|
+
this.config.onTokenRefresh?.(result.token);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Schedule automatic token refresh.
|
|
391
|
+
*/
|
|
392
|
+
scheduleTokenRefresh() {
|
|
393
|
+
if (this.config.autoRefresh === false) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (!this.state.expiresAt) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (this.refreshTimer) {
|
|
400
|
+
clearTimeout(this.refreshTimer);
|
|
401
|
+
}
|
|
402
|
+
const refreshBeforeMs = this.config.refreshBeforeExpiryMs || 60000;
|
|
403
|
+
const refreshAt = this.state.expiresAt - refreshBeforeMs;
|
|
404
|
+
const delay = refreshAt - Date.now();
|
|
405
|
+
if (delay <= 0) {
|
|
406
|
+
// Token already needs refresh
|
|
407
|
+
this.log('Token needs immediate refresh');
|
|
408
|
+
this.refreshToken();
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
this.log(`Scheduling token refresh in ${Math.round(delay / 1000)}s`);
|
|
412
|
+
this.refreshTimer = setTimeout(() => {
|
|
413
|
+
this.log('Auto-refreshing token...');
|
|
414
|
+
this.refreshToken();
|
|
415
|
+
}, delay);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Update state and notify listeners.
|
|
419
|
+
*/
|
|
420
|
+
updateState(newState) {
|
|
421
|
+
this.state = newState;
|
|
422
|
+
this.config.onAuthStateChange?.(newState);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get initial auth state.
|
|
426
|
+
*/
|
|
427
|
+
getInitialState() {
|
|
428
|
+
return {
|
|
429
|
+
isAuthenticated: false,
|
|
430
|
+
isLoading: true,
|
|
431
|
+
user: null,
|
|
432
|
+
token: null,
|
|
433
|
+
expiresAt: null,
|
|
434
|
+
provider: null,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Parse OAuth callback parameters from current URL.
|
|
439
|
+
*/
|
|
440
|
+
parseCallbackFromUrl() {
|
|
441
|
+
if (typeof window === 'undefined') {
|
|
442
|
+
return {};
|
|
443
|
+
}
|
|
444
|
+
const params = new URLSearchParams(window.location.search);
|
|
445
|
+
return {
|
|
446
|
+
code: params.get('code') || undefined,
|
|
447
|
+
state: params.get('state') || undefined,
|
|
448
|
+
error: params.get('error') || undefined,
|
|
449
|
+
errorDescription: params.get('error_description') || undefined,
|
|
450
|
+
url: window.location.href,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Log debug message if debug mode is enabled.
|
|
455
|
+
*/
|
|
456
|
+
log(...args) {
|
|
457
|
+
if (this.config.debug) {
|
|
458
|
+
console.log('[AuthManager]', ...args);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
//# sourceMappingURL=AuthManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthManager.js","sourceRoot":"","sources":["../../src/auth/AuthManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,164 @@
|
|
|
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
|
+
import type { AuthAdapter, AuthResult, CallbackParams, GoogleOAuthConfig, LoginOptions } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Google OAuth adapter using PKCE flow.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - PKCE (Proof Key for Code Exchange) for security
|
|
13
|
+
* - State parameter for CSRF protection
|
|
14
|
+
* - Nonce for replay protection
|
|
15
|
+
* - ID token validation
|
|
16
|
+
* - Automatic token refresh
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const adapter = new GoogleOAuthAdapter({
|
|
21
|
+
* clientId: 'your-client-id.apps.googleusercontent.com'
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Generate login URL
|
|
25
|
+
* const loginUrl = adapter.getLoginUrl();
|
|
26
|
+
* window.location.href = loginUrl;
|
|
27
|
+
*
|
|
28
|
+
* // Handle callback (on /auth/callback page)
|
|
29
|
+
* const result = await adapter.handleCallback({
|
|
30
|
+
* code: new URLSearchParams(location.search).get('code'),
|
|
31
|
+
* state: new URLSearchParams(location.search).get('state')
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare class GoogleOAuthAdapter implements AuthAdapter {
|
|
36
|
+
readonly provider = "google";
|
|
37
|
+
private config;
|
|
38
|
+
private readonly VERIFIER_KEY;
|
|
39
|
+
private readonly CHALLENGE_KEY;
|
|
40
|
+
private readonly STATE_KEY;
|
|
41
|
+
private readonly NONCE_KEY;
|
|
42
|
+
/**
|
|
43
|
+
* Create a new Google OAuth adapter.
|
|
44
|
+
* @param config - Google OAuth configuration
|
|
45
|
+
*/
|
|
46
|
+
constructor(config: GoogleOAuthConfig);
|
|
47
|
+
/**
|
|
48
|
+
* Generate the Google OAuth authorization URL.
|
|
49
|
+
*
|
|
50
|
+
* This method:
|
|
51
|
+
* 1. Generates PKCE code verifier and challenge
|
|
52
|
+
* 2. Generates state for CSRF protection
|
|
53
|
+
* 3. Generates nonce for ID token validation
|
|
54
|
+
* 4. Stores values in sessionStorage for callback validation
|
|
55
|
+
* 5. Returns the authorization URL
|
|
56
|
+
*
|
|
57
|
+
* Note: Call preparePKCE() before this if you need async SHA-256 challenge.
|
|
58
|
+
* If not called, uses plain verifier as challenge (works with some providers).
|
|
59
|
+
*
|
|
60
|
+
* @param options - Optional login options
|
|
61
|
+
* @returns Authorization URL to redirect user to
|
|
62
|
+
*/
|
|
63
|
+
getLoginUrl(options?: LoginOptions): string;
|
|
64
|
+
/**
|
|
65
|
+
* Prepare PKCE values asynchronously before calling getLoginUrl().
|
|
66
|
+
* This computes the proper SHA-256 code challenge.
|
|
67
|
+
*
|
|
68
|
+
* Call this before login() for proper S256 PKCE:
|
|
69
|
+
* ```typescript
|
|
70
|
+
* await adapter.preparePKCE();
|
|
71
|
+
* const url = adapter.getLoginUrl();
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
preparePKCE(): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Compute SHA-256 code challenge from verifier (async).
|
|
77
|
+
*/
|
|
78
|
+
private computeCodeChallenge;
|
|
79
|
+
/**
|
|
80
|
+
* Handle the OAuth callback from Google.
|
|
81
|
+
*
|
|
82
|
+
* This method:
|
|
83
|
+
* 1. Validates the state parameter (CSRF protection)
|
|
84
|
+
* 2. Retrieves the stored PKCE verifier
|
|
85
|
+
* 3. Exchanges the authorization code for tokens
|
|
86
|
+
* 4. Validates the ID token
|
|
87
|
+
* 5. Returns the auth result
|
|
88
|
+
*
|
|
89
|
+
* @param params - Callback parameters from URL
|
|
90
|
+
* @returns Auth result with user info and tokens
|
|
91
|
+
*/
|
|
92
|
+
handleCallback(params: CallbackParams): Promise<AuthResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Refresh an expired token using the refresh token.
|
|
95
|
+
*
|
|
96
|
+
* @param refreshToken - The refresh token to use
|
|
97
|
+
* @returns New auth result or null if refresh failed
|
|
98
|
+
*/
|
|
99
|
+
refreshToken(refreshToken: string): Promise<AuthResult | null>;
|
|
100
|
+
/**
|
|
101
|
+
* Revoke a token (logout from Google).
|
|
102
|
+
*
|
|
103
|
+
* @param token - The token to revoke
|
|
104
|
+
*/
|
|
105
|
+
revokeToken(token: string): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Check if this adapter can handle the given token.
|
|
108
|
+
*
|
|
109
|
+
* @param token - Token to check
|
|
110
|
+
* @returns True if token is a Google ID token
|
|
111
|
+
*/
|
|
112
|
+
canHandle(token: string): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Exchange authorization code for tokens.
|
|
115
|
+
* Uses backend endpoint if configured, otherwise calls Google directly.
|
|
116
|
+
*/
|
|
117
|
+
private exchangeCodeForTokens;
|
|
118
|
+
/**
|
|
119
|
+
* Exchange code via our backend server (secure - keeps client_secret on server)
|
|
120
|
+
*/
|
|
121
|
+
private exchangeCodeViaBackend;
|
|
122
|
+
/**
|
|
123
|
+
* Exchange code directly with Google (won't work for Web apps without client_secret)
|
|
124
|
+
*/
|
|
125
|
+
private exchangeCodeDirect;
|
|
126
|
+
/**
|
|
127
|
+
* Decode a JWT ID token (without verification - verification done via claims).
|
|
128
|
+
*/
|
|
129
|
+
private decodeIdToken;
|
|
130
|
+
/**
|
|
131
|
+
* Validate ID token claims.
|
|
132
|
+
*/
|
|
133
|
+
private validateIdToken;
|
|
134
|
+
/**
|
|
135
|
+
* Get the redirect URI.
|
|
136
|
+
*/
|
|
137
|
+
private getRedirectUri;
|
|
138
|
+
/**
|
|
139
|
+
* Generate a random code verifier for PKCE.
|
|
140
|
+
* 43-128 characters from unreserved URI characters.
|
|
141
|
+
*/
|
|
142
|
+
private generateCodeVerifier;
|
|
143
|
+
/**
|
|
144
|
+
* Generate a random string for state/nonce.
|
|
145
|
+
*/
|
|
146
|
+
private generateRandomString;
|
|
147
|
+
/**
|
|
148
|
+
* Base64URL encode a Uint8Array.
|
|
149
|
+
*/
|
|
150
|
+
private base64UrlEncode;
|
|
151
|
+
/**
|
|
152
|
+
* Base64URL decode a string.
|
|
153
|
+
*/
|
|
154
|
+
private base64UrlDecode;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Compute SHA-256 code challenge asynchronously.
|
|
158
|
+
* Use this for production-ready PKCE implementation.
|
|
159
|
+
*
|
|
160
|
+
* @param verifier - The code verifier
|
|
161
|
+
* @returns Base64URL-encoded SHA-256 hash
|
|
162
|
+
*/
|
|
163
|
+
export declare function computeCodeChallenge(verifier: string): Promise<string>;
|
|
164
|
+
//# sourceMappingURL=GoogleOAuthAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleOAuthAdapter.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/GoogleOAuthAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,YAAY,EACb,MAAM,aAAa,CAAC;AA4CrB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,kBAAmB,YAAW,WAAW;IACpD,QAAQ,CAAC,QAAQ,YAAY;IAE7B,OAAO,CAAC,MAAM,CACM;IAGpB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IAEpD;;;OAGG;gBACS,MAAM,EAAE,iBAAiB;IAWrC;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM;IAmE3C;;;;;;;;;OASG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAUlC;;OAEG;YACW,oBAAoB;IAOlC;;;;;;;;;;;;OAYG;IACG,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAsGjE;;;;;OAKG;IACG,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAyCpE;;;;OAIG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/C;;;;;OAKG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAgBjC;;;OAGG;YACW,qBAAqB;IAcnC;;OAEG;YACW,sBAAsB;IA0CpC;;OAEG;YACW,kBAAkB;IA6BhC;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAmCvB;;OAEG;IACH,OAAO,CAAC,cAAc;IActB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAO5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,eAAe;CAYxB;AAMD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAY5E"}
|