godgpt-web-auth 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 +203 -0
- package/dist/index.cjs +1089 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +376 -0
- package/dist/index.d.ts +376 -0
- package/dist/index.js +1073 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
// src/auth/components/AuthProvider.tsx
|
|
7
|
+
|
|
8
|
+
// src/auth/config.ts
|
|
9
|
+
function validateConfig(config) {
|
|
10
|
+
if (!config.backendUrl) {
|
|
11
|
+
throw new Error("[Auth] backendUrl is required in AuthConfig");
|
|
12
|
+
}
|
|
13
|
+
if (!config.google && !config.apple && !config.email?.enabled) {
|
|
14
|
+
console.warn(
|
|
15
|
+
"[Auth] No auth strategies configured. Enable at least one: google, apple, or email."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
if (config.google && !config.google.clientId) {
|
|
19
|
+
throw new Error("[Auth] google.clientId is required when google is enabled");
|
|
20
|
+
}
|
|
21
|
+
if (config.apple && !config.apple.clientId) {
|
|
22
|
+
throw new Error("[Auth] apple.clientId is required when apple is enabled");
|
|
23
|
+
}
|
|
24
|
+
console.log("[Auth] Config validated:", {
|
|
25
|
+
backendUrl: config.backendUrl,
|
|
26
|
+
hasGoogle: !!config.google,
|
|
27
|
+
hasApple: !!config.apple,
|
|
28
|
+
hasEmail: !!config.email?.enabled
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/auth/services/storageService.ts
|
|
33
|
+
var localStorageAdapter = {
|
|
34
|
+
getItem: (key) => localStorage.getItem(key),
|
|
35
|
+
setItem: (key, value) => localStorage.setItem(key, value),
|
|
36
|
+
removeItem: (key) => localStorage.removeItem(key)
|
|
37
|
+
};
|
|
38
|
+
var memoryStorageAdapter = () => {
|
|
39
|
+
const store = /* @__PURE__ */ new Map();
|
|
40
|
+
return {
|
|
41
|
+
getItem: (key) => store.get(key) ?? null,
|
|
42
|
+
setItem: (key, value) => {
|
|
43
|
+
store.set(key, value);
|
|
44
|
+
},
|
|
45
|
+
removeItem: (key) => {
|
|
46
|
+
store.delete(key);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
var TOKEN_KEY = "auth_token_data";
|
|
51
|
+
function createStorageService(adapter) {
|
|
52
|
+
return {
|
|
53
|
+
/**
|
|
54
|
+
* Save tokens to storage
|
|
55
|
+
*/
|
|
56
|
+
async saveTokens(tokens) {
|
|
57
|
+
try {
|
|
58
|
+
const dataToStore = {
|
|
59
|
+
...tokens,
|
|
60
|
+
stored_at: Date.now()
|
|
61
|
+
};
|
|
62
|
+
await adapter.setItem(TOKEN_KEY, JSON.stringify(dataToStore));
|
|
63
|
+
console.log("[Auth Storage] Tokens saved successfully");
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error("[Auth Storage] Failed to save tokens:", error);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Get tokens from storage
|
|
71
|
+
* Returns null if no tokens exist or if they're invalid
|
|
72
|
+
*/
|
|
73
|
+
async getTokens() {
|
|
74
|
+
try {
|
|
75
|
+
const data = await adapter.getItem(TOKEN_KEY);
|
|
76
|
+
if (!data) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const parsed = JSON.parse(data);
|
|
80
|
+
const { stored_at: _storedAt, ...tokens } = parsed;
|
|
81
|
+
return tokens;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error("[Auth Storage] Failed to get tokens:", error);
|
|
84
|
+
await this.clearTokens();
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
/**
|
|
89
|
+
* Get raw stored data (including metadata like stored_at)
|
|
90
|
+
*/
|
|
91
|
+
async getRawData() {
|
|
92
|
+
try {
|
|
93
|
+
const data = await adapter.getItem(TOKEN_KEY);
|
|
94
|
+
if (!data) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return JSON.parse(data);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error("[Auth Storage] Failed to get raw data:", error);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
/**
|
|
104
|
+
* Clear tokens from storage
|
|
105
|
+
*/
|
|
106
|
+
async clearTokens() {
|
|
107
|
+
try {
|
|
108
|
+
await adapter.removeItem(TOKEN_KEY);
|
|
109
|
+
console.log("[Auth Storage] Tokens cleared");
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("[Auth Storage] Failed to clear tokens:", error);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
/**
|
|
115
|
+
* Check if tokens exist in storage
|
|
116
|
+
*/
|
|
117
|
+
async hasTokens() {
|
|
118
|
+
const data = await adapter.getItem(TOKEN_KEY);
|
|
119
|
+
return data !== null;
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* Check if stored tokens are expired
|
|
123
|
+
* Uses expires_in from the token data
|
|
124
|
+
*/
|
|
125
|
+
async isTokenExpired() {
|
|
126
|
+
try {
|
|
127
|
+
const rawData = await this.getRawData();
|
|
128
|
+
if (!rawData) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
const { stored_at, tokens } = rawData;
|
|
132
|
+
const expiresIn = tokens.expires_in;
|
|
133
|
+
if (!stored_at || !expiresIn) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
const expirationTime = stored_at + expiresIn * 1e3;
|
|
137
|
+
const isExpired = Date.now() >= expirationTime;
|
|
138
|
+
if (isExpired) {
|
|
139
|
+
console.log("[Auth Storage] Token is expired");
|
|
140
|
+
}
|
|
141
|
+
return isExpired;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error(
|
|
144
|
+
"[Auth Storage] Failed to check token expiration:",
|
|
145
|
+
error
|
|
146
|
+
);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
/**
|
|
151
|
+
* Get the time until token expires (in milliseconds)
|
|
152
|
+
* Returns 0 if token is expired or doesn't exist
|
|
153
|
+
*/
|
|
154
|
+
async getTimeUntilExpiry() {
|
|
155
|
+
try {
|
|
156
|
+
const rawData = await this.getRawData();
|
|
157
|
+
if (!rawData) {
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
const { stored_at, tokens } = rawData;
|
|
161
|
+
const expiresIn = tokens.expires_in;
|
|
162
|
+
if (!stored_at || !expiresIn) {
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
const expirationTime = stored_at + expiresIn * 1e3;
|
|
166
|
+
const timeRemaining = expirationTime - Date.now();
|
|
167
|
+
return Math.max(0, timeRemaining);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error("[Auth Storage] Failed to get time until expiry:", error);
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
var storageService = createStorageService(localStorageAdapter);
|
|
176
|
+
var AuthInternalContext = react.createContext(null);
|
|
177
|
+
function AuthProvider({
|
|
178
|
+
children,
|
|
179
|
+
config,
|
|
180
|
+
onLogout
|
|
181
|
+
}) {
|
|
182
|
+
const [tokens, setTokens] = react.useState(null);
|
|
183
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
184
|
+
const [isAuthLoaded, setIsAuthLoaded] = react.useState(false);
|
|
185
|
+
react.useEffect(() => {
|
|
186
|
+
validateConfig(config);
|
|
187
|
+
const initializeAuth = async () => {
|
|
188
|
+
console.log("[AuthProvider] Initializing auth state");
|
|
189
|
+
try {
|
|
190
|
+
const storedTokens = await storageService.getTokens();
|
|
191
|
+
if (storedTokens) {
|
|
192
|
+
const isExpired = await storageService.isTokenExpired();
|
|
193
|
+
if (isExpired) {
|
|
194
|
+
console.log("[AuthProvider] Stored tokens are expired, clearing");
|
|
195
|
+
await storageService.clearTokens();
|
|
196
|
+
onLogout?.();
|
|
197
|
+
} else {
|
|
198
|
+
console.log("[AuthProvider] Valid tokens found, restoring session");
|
|
199
|
+
setTokens(storedTokens);
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
console.log("[AuthProvider] No stored tokens found");
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error("[AuthProvider] Error initializing auth:", error);
|
|
206
|
+
await storageService.clearTokens();
|
|
207
|
+
} finally {
|
|
208
|
+
setIsAuthLoaded(true);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
initializeAuth();
|
|
212
|
+
}, [config, onLogout]);
|
|
213
|
+
const _setTokens = react.useCallback(async (newTokens) => {
|
|
214
|
+
console.log("[AuthProvider] Setting tokens");
|
|
215
|
+
await storageService.saveTokens(newTokens);
|
|
216
|
+
setTokens(newTokens);
|
|
217
|
+
}, []);
|
|
218
|
+
const _setLoading = react.useCallback((loading) => {
|
|
219
|
+
setIsLoading(loading);
|
|
220
|
+
}, []);
|
|
221
|
+
const logout = react.useCallback(async () => {
|
|
222
|
+
console.log("[AuthProvider] Logging out");
|
|
223
|
+
await storageService.clearTokens();
|
|
224
|
+
setTokens(null);
|
|
225
|
+
onLogout?.();
|
|
226
|
+
}, [onLogout]);
|
|
227
|
+
const contextValue = react.useMemo(
|
|
228
|
+
() => ({
|
|
229
|
+
// Public API
|
|
230
|
+
tokens,
|
|
231
|
+
logout,
|
|
232
|
+
isLoading,
|
|
233
|
+
isAuthLoaded,
|
|
234
|
+
// Internal API (for LoginPage)
|
|
235
|
+
config,
|
|
236
|
+
_setTokens,
|
|
237
|
+
_setLoading
|
|
238
|
+
}),
|
|
239
|
+
[tokens, logout, isLoading, isAuthLoaded, config, _setTokens, _setLoading]
|
|
240
|
+
);
|
|
241
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AuthInternalContext.Provider, { value: contextValue, children });
|
|
242
|
+
}
|
|
243
|
+
function useAuth() {
|
|
244
|
+
const context = react.useContext(AuthInternalContext);
|
|
245
|
+
if (!context) {
|
|
246
|
+
throw new Error("useAuth must be used within an AuthProvider");
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
tokens: context.tokens,
|
|
250
|
+
logout: context.logout,
|
|
251
|
+
isLoading: context.isLoading,
|
|
252
|
+
isAuthLoaded: context.isAuthLoaded
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function useAuthInternal() {
|
|
256
|
+
const context = react.useContext(AuthInternalContext);
|
|
257
|
+
if (!context) {
|
|
258
|
+
throw new Error("useAuthInternal must be used within an AuthProvider");
|
|
259
|
+
}
|
|
260
|
+
return context;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/auth/services/tokenService.ts
|
|
264
|
+
async function exchangeToken(params, provider, config) {
|
|
265
|
+
console.log(`[Auth Token] Exchanging ${provider} credentials for tokens`);
|
|
266
|
+
try {
|
|
267
|
+
const bodyParams = {
|
|
268
|
+
grant_type: provider,
|
|
269
|
+
client_id: "AevatarAuthServer",
|
|
270
|
+
// TODO: Make configurable via config
|
|
271
|
+
scope: "Aevatar offline_access"
|
|
272
|
+
};
|
|
273
|
+
if (provider !== "password") {
|
|
274
|
+
bodyParams.source = "web";
|
|
275
|
+
}
|
|
276
|
+
if (config.google && config.appId) {
|
|
277
|
+
bodyParams.google_app_id = config.appId;
|
|
278
|
+
}
|
|
279
|
+
if (config.apple) {
|
|
280
|
+
bodyParams.apple_app_id = config.apple.clientId;
|
|
281
|
+
}
|
|
282
|
+
if (params.id_token) {
|
|
283
|
+
bodyParams.id_token = params.id_token;
|
|
284
|
+
}
|
|
285
|
+
if (params.code) {
|
|
286
|
+
bodyParams.code = params.code;
|
|
287
|
+
}
|
|
288
|
+
if (params.username) {
|
|
289
|
+
bodyParams.username = params.username;
|
|
290
|
+
}
|
|
291
|
+
if (params.password) {
|
|
292
|
+
bodyParams.password = params.password;
|
|
293
|
+
}
|
|
294
|
+
const body = Object.entries(bodyParams).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&");
|
|
295
|
+
const controller = new AbortController();
|
|
296
|
+
const timeoutId = setTimeout(() => controller.abort(), 15e3);
|
|
297
|
+
const response = await fetch(`${config.backendUrl}/connect/token`, {
|
|
298
|
+
method: "POST",
|
|
299
|
+
headers: {
|
|
300
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
301
|
+
Accept: "application/json"
|
|
302
|
+
},
|
|
303
|
+
body,
|
|
304
|
+
signal: controller.signal
|
|
305
|
+
});
|
|
306
|
+
clearTimeout(timeoutId);
|
|
307
|
+
if (!response.ok) {
|
|
308
|
+
const errorData = await response.json().catch(() => ({}));
|
|
309
|
+
console.error(
|
|
310
|
+
`[Auth Token] Exchange failed (${provider}):`,
|
|
311
|
+
response.status,
|
|
312
|
+
errorData
|
|
313
|
+
);
|
|
314
|
+
return {
|
|
315
|
+
success: false,
|
|
316
|
+
state: "error",
|
|
317
|
+
tokens: null,
|
|
318
|
+
message: errorData.error_description || errorData.error || `HTTP ${response.status}: ${response.statusText}`,
|
|
319
|
+
provider
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
const tokens = await response.json();
|
|
323
|
+
console.log(`[Auth Token] Exchange successful for ${provider}`);
|
|
324
|
+
return {
|
|
325
|
+
success: true,
|
|
326
|
+
state: "success",
|
|
327
|
+
tokens: {
|
|
328
|
+
access_token: tokens.access_token,
|
|
329
|
+
refresh_token: tokens.refresh_token,
|
|
330
|
+
expires_in: tokens.expires_in,
|
|
331
|
+
token_type: tokens.token_type
|
|
332
|
+
},
|
|
333
|
+
message: "Login successful",
|
|
334
|
+
provider
|
|
335
|
+
};
|
|
336
|
+
} catch (error) {
|
|
337
|
+
const isTimeout = error instanceof Error && error.name === "AbortError";
|
|
338
|
+
const errorMessage = isTimeout ? "Request timed out. Please check your connection." : error instanceof Error ? error.message : "Login failed";
|
|
339
|
+
console.error(`[Auth Token] Exchange error (${provider}):`, error);
|
|
340
|
+
return {
|
|
341
|
+
success: false,
|
|
342
|
+
state: "error",
|
|
343
|
+
tokens: null,
|
|
344
|
+
message: errorMessage,
|
|
345
|
+
provider
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function refreshToken(refresh_token, backendUrl) {
|
|
350
|
+
console.log("[Auth Token] Refreshing access token");
|
|
351
|
+
try {
|
|
352
|
+
const body = new URLSearchParams({
|
|
353
|
+
grant_type: "refresh_token",
|
|
354
|
+
client_id: "AevatarAuthServer",
|
|
355
|
+
// TODO: Make configurable
|
|
356
|
+
refresh_token
|
|
357
|
+
}).toString();
|
|
358
|
+
const controller = new AbortController();
|
|
359
|
+
const timeoutId = setTimeout(() => controller.abort(), 15e3);
|
|
360
|
+
const response = await fetch(`${backendUrl}/connect/token`, {
|
|
361
|
+
method: "POST",
|
|
362
|
+
headers: {
|
|
363
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
364
|
+
Accept: "application/json"
|
|
365
|
+
},
|
|
366
|
+
body,
|
|
367
|
+
signal: controller.signal
|
|
368
|
+
});
|
|
369
|
+
clearTimeout(timeoutId);
|
|
370
|
+
if (!response.ok) {
|
|
371
|
+
const errorData = await response.json().catch(() => ({}));
|
|
372
|
+
console.error("[Auth Token] Refresh failed:", response.status, errorData);
|
|
373
|
+
throw new Error(
|
|
374
|
+
errorData.error_description || `Refresh failed: ${response.status}`
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
const tokens = await response.json();
|
|
378
|
+
console.log("[Auth Token] Refresh successful");
|
|
379
|
+
return {
|
|
380
|
+
access_token: tokens.access_token,
|
|
381
|
+
refresh_token: tokens.refresh_token,
|
|
382
|
+
expires_in: tokens.expires_in,
|
|
383
|
+
token_type: tokens.token_type
|
|
384
|
+
};
|
|
385
|
+
} catch (error) {
|
|
386
|
+
const isTimeout = error instanceof Error && error.name === "AbortError";
|
|
387
|
+
console.error(
|
|
388
|
+
"[Auth Token] Refresh error:",
|
|
389
|
+
isTimeout ? "Request timed out" : error
|
|
390
|
+
);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/auth/strategies/google.ts
|
|
396
|
+
var googleSDKLoaded = false;
|
|
397
|
+
var googleSDKLoading = null;
|
|
398
|
+
function loadGoogleSDK() {
|
|
399
|
+
if (googleSDKLoaded && window.google?.accounts) {
|
|
400
|
+
return Promise.resolve();
|
|
401
|
+
}
|
|
402
|
+
if (googleSDKLoading) {
|
|
403
|
+
return googleSDKLoading;
|
|
404
|
+
}
|
|
405
|
+
googleSDKLoading = new Promise((resolve, reject) => {
|
|
406
|
+
if (window.google?.accounts) {
|
|
407
|
+
googleSDKLoaded = true;
|
|
408
|
+
resolve();
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
console.log("[Auth Google] Loading Google Identity Services SDK");
|
|
412
|
+
const script = document.createElement("script");
|
|
413
|
+
script.src = "https://accounts.google.com/gsi/client";
|
|
414
|
+
script.async = true;
|
|
415
|
+
script.defer = true;
|
|
416
|
+
script.onload = () => {
|
|
417
|
+
console.log("[Auth Google] SDK loaded successfully");
|
|
418
|
+
googleSDKLoaded = true;
|
|
419
|
+
resolve();
|
|
420
|
+
};
|
|
421
|
+
script.onerror = () => {
|
|
422
|
+
console.error("[Auth Google] Failed to load SDK");
|
|
423
|
+
googleSDKLoading = null;
|
|
424
|
+
reject(new Error("Failed to load Google Identity Services SDK"));
|
|
425
|
+
};
|
|
426
|
+
document.head.appendChild(script);
|
|
427
|
+
});
|
|
428
|
+
return googleSDKLoading;
|
|
429
|
+
}
|
|
430
|
+
async function signInWithGoogle(config) {
|
|
431
|
+
console.log("[Auth Google] Starting Google sign-in");
|
|
432
|
+
console.log("[Auth Google] Current origin:", window.location.origin);
|
|
433
|
+
if (!config.google?.clientId) {
|
|
434
|
+
console.error("[Auth Google] No client ID configured");
|
|
435
|
+
return {
|
|
436
|
+
success: false,
|
|
437
|
+
state: "error",
|
|
438
|
+
tokens: null,
|
|
439
|
+
message: "Google client ID not configured",
|
|
440
|
+
provider: "google"
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
console.log("[Auth Google] Client ID:", config.google.clientId.substring(0, 20) + "...");
|
|
444
|
+
try {
|
|
445
|
+
console.log("[Auth Google] Loading SDK...");
|
|
446
|
+
await loadGoogleSDK();
|
|
447
|
+
if (!window.google?.accounts) {
|
|
448
|
+
throw new Error("Google SDK not available after loading");
|
|
449
|
+
}
|
|
450
|
+
console.log("[Auth Google] SDK loaded, starting OAuth flow...");
|
|
451
|
+
return await signInWithGoogleRedirect(config);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error("[Auth Google] Sign-in error:", error);
|
|
454
|
+
return {
|
|
455
|
+
success: false,
|
|
456
|
+
state: "error",
|
|
457
|
+
tokens: null,
|
|
458
|
+
message: error instanceof Error ? error.message : "Google login failed",
|
|
459
|
+
provider: "google"
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async function signInWithGoogleRedirect(config) {
|
|
464
|
+
return new Promise((resolve) => {
|
|
465
|
+
const clientId = config.google.clientId;
|
|
466
|
+
const redirectUri = window.location.origin;
|
|
467
|
+
const nonce = Math.random().toString(36).substring(2, 15);
|
|
468
|
+
console.log("[Auth Google] OAuth redirect flow:");
|
|
469
|
+
console.log(" - Client ID:", clientId);
|
|
470
|
+
console.log(" - Redirect URI:", redirectUri);
|
|
471
|
+
console.log(" \u26A0\uFE0F Make sure this redirect URI is added to Google Cloud Console!");
|
|
472
|
+
const params = new URLSearchParams({
|
|
473
|
+
client_id: clientId,
|
|
474
|
+
redirect_uri: redirectUri,
|
|
475
|
+
response_type: "id_token",
|
|
476
|
+
scope: "openid email profile",
|
|
477
|
+
nonce,
|
|
478
|
+
prompt: "select_account"
|
|
479
|
+
});
|
|
480
|
+
const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
|
|
481
|
+
console.log("[Auth Google] Auth URL:", authUrl);
|
|
482
|
+
const width = 500;
|
|
483
|
+
const height = 600;
|
|
484
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
485
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
486
|
+
const popup = window.open(
|
|
487
|
+
authUrl,
|
|
488
|
+
"google-auth",
|
|
489
|
+
`width=${width},height=${height},left=${left},top=${top}`
|
|
490
|
+
);
|
|
491
|
+
if (!popup) {
|
|
492
|
+
resolve({
|
|
493
|
+
success: false,
|
|
494
|
+
state: "error",
|
|
495
|
+
tokens: null,
|
|
496
|
+
message: "Popup was blocked. Please allow popups for this site.",
|
|
497
|
+
provider: "google"
|
|
498
|
+
});
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const pollInterval = setInterval(() => {
|
|
502
|
+
try {
|
|
503
|
+
if (popup.closed) {
|
|
504
|
+
clearInterval(pollInterval);
|
|
505
|
+
resolve({
|
|
506
|
+
success: false,
|
|
507
|
+
state: "cancelled",
|
|
508
|
+
tokens: null,
|
|
509
|
+
message: "Google Sign-In was cancelled",
|
|
510
|
+
provider: "google"
|
|
511
|
+
});
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const url = popup.location.href;
|
|
515
|
+
if (url.startsWith(redirectUri)) {
|
|
516
|
+
clearInterval(pollInterval);
|
|
517
|
+
popup.close();
|
|
518
|
+
const hash = new URL(url.replace("#", "?")).searchParams;
|
|
519
|
+
const idToken = hash.get("id_token");
|
|
520
|
+
const error = hash.get("error");
|
|
521
|
+
if (error) {
|
|
522
|
+
resolve({
|
|
523
|
+
success: false,
|
|
524
|
+
state: "error",
|
|
525
|
+
tokens: null,
|
|
526
|
+
message: hash.get("error_description") || error,
|
|
527
|
+
provider: "google"
|
|
528
|
+
});
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (idToken) {
|
|
532
|
+
exchangeToken({ id_token: idToken }, "google", config).then(resolve);
|
|
533
|
+
} else {
|
|
534
|
+
resolve({
|
|
535
|
+
success: false,
|
|
536
|
+
state: "error",
|
|
537
|
+
tokens: null,
|
|
538
|
+
message: "No token received from Google",
|
|
539
|
+
provider: "google"
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
}, 500);
|
|
546
|
+
setTimeout(() => {
|
|
547
|
+
clearInterval(pollInterval);
|
|
548
|
+
if (!popup.closed) {
|
|
549
|
+
popup.close();
|
|
550
|
+
}
|
|
551
|
+
resolve({
|
|
552
|
+
success: false,
|
|
553
|
+
state: "error",
|
|
554
|
+
tokens: null,
|
|
555
|
+
message: "Google Sign-In timed out",
|
|
556
|
+
provider: "google"
|
|
557
|
+
});
|
|
558
|
+
}, 5 * 60 * 1e3);
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// src/auth/strategies/apple.ts
|
|
563
|
+
var appleSDKLoaded = false;
|
|
564
|
+
var appleSDKLoading = null;
|
|
565
|
+
var appleInitialized = false;
|
|
566
|
+
function loadAppleSDK() {
|
|
567
|
+
if (appleSDKLoaded && window.AppleID) {
|
|
568
|
+
return Promise.resolve();
|
|
569
|
+
}
|
|
570
|
+
if (appleSDKLoading) {
|
|
571
|
+
return appleSDKLoading;
|
|
572
|
+
}
|
|
573
|
+
appleSDKLoading = new Promise((resolve, reject) => {
|
|
574
|
+
if (window.AppleID) {
|
|
575
|
+
appleSDKLoaded = true;
|
|
576
|
+
resolve();
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
console.log("[Auth Apple] Loading Apple Sign In JS SDK");
|
|
580
|
+
const script = document.createElement("script");
|
|
581
|
+
script.src = "https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js";
|
|
582
|
+
script.async = true;
|
|
583
|
+
script.onload = () => {
|
|
584
|
+
console.log("[Auth Apple] SDK loaded successfully");
|
|
585
|
+
appleSDKLoaded = true;
|
|
586
|
+
resolve();
|
|
587
|
+
};
|
|
588
|
+
script.onerror = () => {
|
|
589
|
+
console.error("[Auth Apple] Failed to load SDK");
|
|
590
|
+
appleSDKLoading = null;
|
|
591
|
+
reject(new Error("Failed to load Apple Sign In JS SDK"));
|
|
592
|
+
};
|
|
593
|
+
document.head.appendChild(script);
|
|
594
|
+
});
|
|
595
|
+
return appleSDKLoading;
|
|
596
|
+
}
|
|
597
|
+
function initializeApple(config) {
|
|
598
|
+
if (appleInitialized || !window.AppleID || !config.apple) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const isLocalhost = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
|
|
602
|
+
const redirectUri = isLocalhost ? window.location.origin : config.apple.redirectUri || window.location.origin;
|
|
603
|
+
console.log("[Auth Apple] Initializing with config:", {
|
|
604
|
+
clientId: config.apple.clientId,
|
|
605
|
+
redirectUri,
|
|
606
|
+
isLocalhost,
|
|
607
|
+
currentOrigin: window.location.origin
|
|
608
|
+
});
|
|
609
|
+
window.AppleID.auth.init({
|
|
610
|
+
clientId: config.apple.clientId,
|
|
611
|
+
scope: "name email",
|
|
612
|
+
redirectURI: redirectUri,
|
|
613
|
+
usePopup: true
|
|
614
|
+
});
|
|
615
|
+
appleInitialized = true;
|
|
616
|
+
}
|
|
617
|
+
async function signInWithApple(config) {
|
|
618
|
+
console.log("[Auth Apple] Starting Apple sign-in");
|
|
619
|
+
if (!config.apple?.clientId) {
|
|
620
|
+
console.error("[Auth Apple] No client ID configured");
|
|
621
|
+
return {
|
|
622
|
+
success: false,
|
|
623
|
+
state: "error",
|
|
624
|
+
tokens: null,
|
|
625
|
+
message: "Apple client ID not configured",
|
|
626
|
+
provider: "apple"
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
const isLocalhost = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
|
|
630
|
+
if (isLocalhost) {
|
|
631
|
+
console.warn("[Auth Apple] Running on localhost - Apple Sign In requires:");
|
|
632
|
+
console.warn(" 1. Your Service ID (com.gpt.godweb) must have localhost configured in Apple Developer Portal");
|
|
633
|
+
console.warn(" 2. Return URL must include: http://localhost:5173 (or your port)");
|
|
634
|
+
}
|
|
635
|
+
try {
|
|
636
|
+
console.log("[Auth Apple] Loading SDK...");
|
|
637
|
+
await loadAppleSDK();
|
|
638
|
+
if (!window.AppleID) {
|
|
639
|
+
throw new Error("Apple SDK not available after loading");
|
|
640
|
+
}
|
|
641
|
+
appleInitialized = false;
|
|
642
|
+
initializeApple(config);
|
|
643
|
+
console.log("[Auth Apple] Calling Apple signIn...");
|
|
644
|
+
const response = await window.AppleID.auth.signIn();
|
|
645
|
+
console.log("[Auth Apple] Received response");
|
|
646
|
+
const code = response.authorization?.code;
|
|
647
|
+
const idToken = response.authorization?.id_token;
|
|
648
|
+
if (code) {
|
|
649
|
+
console.log("[Auth Apple] Exchanging authorization code");
|
|
650
|
+
return await exchangeToken({ code }, "apple", config);
|
|
651
|
+
} else if (idToken) {
|
|
652
|
+
console.log("[Auth Apple] Exchanging id_token");
|
|
653
|
+
return await exchangeToken({ id_token: idToken }, "apple", config);
|
|
654
|
+
} else {
|
|
655
|
+
return {
|
|
656
|
+
success: false,
|
|
657
|
+
state: "error",
|
|
658
|
+
tokens: null,
|
|
659
|
+
message: "No authorization code or token received from Apple",
|
|
660
|
+
provider: "apple"
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
} catch (error) {
|
|
664
|
+
const appleError = error;
|
|
665
|
+
if (appleError?.error === "popup_closed_by_user" || appleError?.error === "user_cancelled_authorize") {
|
|
666
|
+
console.log("[Auth Apple] Sign-in cancelled by user");
|
|
667
|
+
return {
|
|
668
|
+
success: false,
|
|
669
|
+
state: "cancelled",
|
|
670
|
+
tokens: null,
|
|
671
|
+
message: "Apple Sign-In was cancelled",
|
|
672
|
+
provider: "apple"
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
console.error("[Auth Apple] Sign-in error:", error);
|
|
676
|
+
return {
|
|
677
|
+
success: false,
|
|
678
|
+
state: "error",
|
|
679
|
+
tokens: null,
|
|
680
|
+
message: appleError?.error || (error instanceof Error ? error.message : "Apple login failed"),
|
|
681
|
+
provider: "apple"
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// src/auth/strategies/email.ts
|
|
687
|
+
async function signInWithEmail(email, password, config) {
|
|
688
|
+
console.log("[Auth Email] Starting email sign-in");
|
|
689
|
+
if (!email || !email.trim()) {
|
|
690
|
+
return {
|
|
691
|
+
success: false,
|
|
692
|
+
state: "error",
|
|
693
|
+
tokens: null,
|
|
694
|
+
message: "Email is required",
|
|
695
|
+
provider: "password"
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
if (!password) {
|
|
699
|
+
return {
|
|
700
|
+
success: false,
|
|
701
|
+
state: "error",
|
|
702
|
+
tokens: null,
|
|
703
|
+
message: "Password is required",
|
|
704
|
+
provider: "password"
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
708
|
+
if (!emailRegex.test(email.trim())) {
|
|
709
|
+
return {
|
|
710
|
+
success: false,
|
|
711
|
+
state: "error",
|
|
712
|
+
tokens: null,
|
|
713
|
+
message: "Please enter a valid email address",
|
|
714
|
+
provider: "password"
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
const result = await exchangeToken(
|
|
719
|
+
{
|
|
720
|
+
username: email.trim(),
|
|
721
|
+
password
|
|
722
|
+
},
|
|
723
|
+
"password",
|
|
724
|
+
config
|
|
725
|
+
);
|
|
726
|
+
if (result.success) {
|
|
727
|
+
console.log("[Auth Email] Sign-in successful");
|
|
728
|
+
} else {
|
|
729
|
+
console.log("[Auth Email] Sign-in failed:", result.message);
|
|
730
|
+
}
|
|
731
|
+
return result;
|
|
732
|
+
} catch (error) {
|
|
733
|
+
console.error("[Auth Email] Sign-in error:", error);
|
|
734
|
+
return {
|
|
735
|
+
success: false,
|
|
736
|
+
state: "error",
|
|
737
|
+
tokens: null,
|
|
738
|
+
message: error instanceof Error ? error.message : "Email login failed",
|
|
739
|
+
provider: "password"
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
var GoogleIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "w-5 h-5", viewBox: "0 0 24 24", fill: "currentColor", children: [
|
|
744
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
745
|
+
"path",
|
|
746
|
+
{
|
|
747
|
+
d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z",
|
|
748
|
+
fill: "#4285F4"
|
|
749
|
+
}
|
|
750
|
+
),
|
|
751
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
752
|
+
"path",
|
|
753
|
+
{
|
|
754
|
+
d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z",
|
|
755
|
+
fill: "#34A853"
|
|
756
|
+
}
|
|
757
|
+
),
|
|
758
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
759
|
+
"path",
|
|
760
|
+
{
|
|
761
|
+
d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z",
|
|
762
|
+
fill: "#FBBC05"
|
|
763
|
+
}
|
|
764
|
+
),
|
|
765
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
766
|
+
"path",
|
|
767
|
+
{
|
|
768
|
+
d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z",
|
|
769
|
+
fill: "#EA4335"
|
|
770
|
+
}
|
|
771
|
+
)
|
|
772
|
+
] });
|
|
773
|
+
var AppleIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-5 h-5", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" }) });
|
|
774
|
+
var LoadingSpinner = () => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
775
|
+
"svg",
|
|
776
|
+
{
|
|
777
|
+
className: "animate-spin h-5 w-5",
|
|
778
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
779
|
+
fill: "none",
|
|
780
|
+
viewBox: "0 0 24 24",
|
|
781
|
+
children: [
|
|
782
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
783
|
+
"circle",
|
|
784
|
+
{
|
|
785
|
+
className: "opacity-25",
|
|
786
|
+
cx: "12",
|
|
787
|
+
cy: "12",
|
|
788
|
+
r: "10",
|
|
789
|
+
stroke: "currentColor",
|
|
790
|
+
strokeWidth: "4"
|
|
791
|
+
}
|
|
792
|
+
),
|
|
793
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
794
|
+
"path",
|
|
795
|
+
{
|
|
796
|
+
className: "opacity-75",
|
|
797
|
+
fill: "currentColor",
|
|
798
|
+
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
799
|
+
}
|
|
800
|
+
)
|
|
801
|
+
]
|
|
802
|
+
}
|
|
803
|
+
);
|
|
804
|
+
var Button = ({
|
|
805
|
+
variant = "primary",
|
|
806
|
+
isLoading = false,
|
|
807
|
+
disabled,
|
|
808
|
+
children,
|
|
809
|
+
className = "",
|
|
810
|
+
...props
|
|
811
|
+
}) => {
|
|
812
|
+
const baseClasses = "min-h-[52px] px-6 rounded-[99px] font-semibold text-base transition-all duration-200 flex items-center justify-center gap-2 focus:outline-none";
|
|
813
|
+
const variantClasses = {
|
|
814
|
+
primary: "bg-black text-white active:opacity-80 active:scale-[0.98]",
|
|
815
|
+
secondary: "bg-white text-black border border-black active:opacity-80 active:scale-[0.98]"
|
|
816
|
+
};
|
|
817
|
+
const disabledClasses = "opacity-40 cursor-not-allowed pointer-events-none";
|
|
818
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
819
|
+
"button",
|
|
820
|
+
{
|
|
821
|
+
className: `${baseClasses} ${variantClasses[variant]} ${disabled || isLoading ? disabledClasses : ""} ${className}`,
|
|
822
|
+
disabled: disabled || isLoading,
|
|
823
|
+
...props,
|
|
824
|
+
children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
825
|
+
/* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner, {}),
|
|
826
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Loading..." })
|
|
827
|
+
] }) : children
|
|
828
|
+
}
|
|
829
|
+
);
|
|
830
|
+
};
|
|
831
|
+
var Input = ({
|
|
832
|
+
label,
|
|
833
|
+
error,
|
|
834
|
+
className = "",
|
|
835
|
+
...props
|
|
836
|
+
}) => {
|
|
837
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
838
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700", children: label }),
|
|
839
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
840
|
+
"input",
|
|
841
|
+
{
|
|
842
|
+
className: `w-full px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-black focus:border-transparent ${error ? "border-red-500" : "border-gray-300"} ${className}`,
|
|
843
|
+
...props
|
|
844
|
+
}
|
|
845
|
+
),
|
|
846
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-500", children: error })
|
|
847
|
+
] });
|
|
848
|
+
};
|
|
849
|
+
function LoginPage() {
|
|
850
|
+
const { config, _setTokens, _setLoading, isLoading } = useAuthInternal();
|
|
851
|
+
const [view, setView] = react.useState("main");
|
|
852
|
+
const [email, setEmail] = react.useState("");
|
|
853
|
+
const [password, setPassword] = react.useState("");
|
|
854
|
+
const [error, setError] = react.useState(null);
|
|
855
|
+
const [fieldErrors, setFieldErrors] = react.useState({});
|
|
856
|
+
const handleGoogleLogin = async () => {
|
|
857
|
+
if (!config.google) {
|
|
858
|
+
setError("Google sign-in is not configured");
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
setError(null);
|
|
862
|
+
_setLoading(true);
|
|
863
|
+
try {
|
|
864
|
+
const result = await signInWithGoogle(config);
|
|
865
|
+
if (result.success && result.tokens) {
|
|
866
|
+
await _setTokens(result.tokens);
|
|
867
|
+
} else if (result.state !== "cancelled") {
|
|
868
|
+
setError(result.message || "Google sign-in failed");
|
|
869
|
+
}
|
|
870
|
+
} catch (err) {
|
|
871
|
+
setError(err instanceof Error ? err.message : "Google sign-in failed");
|
|
872
|
+
} finally {
|
|
873
|
+
_setLoading(false);
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
const handleAppleLogin = async () => {
|
|
877
|
+
if (!config.apple) {
|
|
878
|
+
setError("Apple sign-in is not configured");
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
setError(null);
|
|
882
|
+
_setLoading(true);
|
|
883
|
+
try {
|
|
884
|
+
const result = await signInWithApple(config);
|
|
885
|
+
if (result.success && result.tokens) {
|
|
886
|
+
await _setTokens(result.tokens);
|
|
887
|
+
} else if (result.state !== "cancelled") {
|
|
888
|
+
setError(result.message || "Apple sign-in failed");
|
|
889
|
+
}
|
|
890
|
+
} catch (err) {
|
|
891
|
+
setError(err instanceof Error ? err.message : "Apple sign-in failed");
|
|
892
|
+
} finally {
|
|
893
|
+
_setLoading(false);
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
const handleEmailLogin = async (e) => {
|
|
897
|
+
e.preventDefault();
|
|
898
|
+
const errors = {};
|
|
899
|
+
if (!email.trim()) {
|
|
900
|
+
errors.email = "Email is required";
|
|
901
|
+
}
|
|
902
|
+
if (!password) {
|
|
903
|
+
errors.password = "Password is required";
|
|
904
|
+
}
|
|
905
|
+
if (Object.keys(errors).length > 0) {
|
|
906
|
+
setFieldErrors(errors);
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
setError(null);
|
|
910
|
+
setFieldErrors({});
|
|
911
|
+
_setLoading(true);
|
|
912
|
+
try {
|
|
913
|
+
const result = await signInWithEmail(email, password, config);
|
|
914
|
+
if (result.success && result.tokens) {
|
|
915
|
+
await _setTokens(result.tokens);
|
|
916
|
+
} else {
|
|
917
|
+
setError(result.message || "Email sign-in failed");
|
|
918
|
+
}
|
|
919
|
+
} catch (err) {
|
|
920
|
+
setError(err instanceof Error ? err.message : "Email sign-in failed");
|
|
921
|
+
} finally {
|
|
922
|
+
_setLoading(false);
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
if (view === "email") {
|
|
926
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen bg-white flex flex-col px-5 py-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col max-w-md mx-auto w-full", children: [
|
|
927
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
928
|
+
"button",
|
|
929
|
+
{
|
|
930
|
+
onClick: () => {
|
|
931
|
+
setView("main");
|
|
932
|
+
setError(null);
|
|
933
|
+
setFieldErrors({});
|
|
934
|
+
},
|
|
935
|
+
className: "self-start mb-8 flex items-center text-gray-600 active:opacity-80",
|
|
936
|
+
children: [
|
|
937
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
938
|
+
"svg",
|
|
939
|
+
{
|
|
940
|
+
className: "w-5 h-5 mr-2",
|
|
941
|
+
fill: "none",
|
|
942
|
+
stroke: "currentColor",
|
|
943
|
+
viewBox: "0 0 24 24",
|
|
944
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
945
|
+
"path",
|
|
946
|
+
{
|
|
947
|
+
strokeLinecap: "round",
|
|
948
|
+
strokeLinejoin: "round",
|
|
949
|
+
strokeWidth: 2,
|
|
950
|
+
d: "M15 19l-7-7 7-7"
|
|
951
|
+
}
|
|
952
|
+
)
|
|
953
|
+
}
|
|
954
|
+
),
|
|
955
|
+
"Back"
|
|
956
|
+
]
|
|
957
|
+
}
|
|
958
|
+
),
|
|
959
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-12 text-center", children: [
|
|
960
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-[30px] font-bold text-black mb-2", children: "Sign In" }),
|
|
961
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base text-gray-600", children: "Enter your email and password" })
|
|
962
|
+
] }),
|
|
963
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 p-4 bg-red-50 border border-red-200 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600", children: error }) }),
|
|
964
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleEmailLogin, className: "space-y-6", children: [
|
|
965
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
966
|
+
Input,
|
|
967
|
+
{
|
|
968
|
+
type: "email",
|
|
969
|
+
label: "Email",
|
|
970
|
+
placeholder: "Enter your email",
|
|
971
|
+
value: email,
|
|
972
|
+
onChange: (e) => {
|
|
973
|
+
setEmail(e.target.value);
|
|
974
|
+
if (fieldErrors.email)
|
|
975
|
+
setFieldErrors({ ...fieldErrors, email: void 0 });
|
|
976
|
+
},
|
|
977
|
+
error: fieldErrors.email,
|
|
978
|
+
autoComplete: "email"
|
|
979
|
+
}
|
|
980
|
+
),
|
|
981
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
982
|
+
Input,
|
|
983
|
+
{
|
|
984
|
+
type: "password",
|
|
985
|
+
label: "Password",
|
|
986
|
+
placeholder: "Enter your password",
|
|
987
|
+
value: password,
|
|
988
|
+
onChange: (e) => {
|
|
989
|
+
setPassword(e.target.value);
|
|
990
|
+
if (fieldErrors.password)
|
|
991
|
+
setFieldErrors({ ...fieldErrors, password: void 0 });
|
|
992
|
+
},
|
|
993
|
+
error: fieldErrors.password,
|
|
994
|
+
autoComplete: "current-password"
|
|
995
|
+
}
|
|
996
|
+
),
|
|
997
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
998
|
+
Button,
|
|
999
|
+
{
|
|
1000
|
+
type: "submit",
|
|
1001
|
+
variant: "primary",
|
|
1002
|
+
isLoading,
|
|
1003
|
+
className: "w-full mt-8",
|
|
1004
|
+
children: "Sign In"
|
|
1005
|
+
}
|
|
1006
|
+
)
|
|
1007
|
+
] })
|
|
1008
|
+
] }) });
|
|
1009
|
+
}
|
|
1010
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-white flex flex-col px-5 py-8", children: [
|
|
1011
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col justify-center max-w-md mx-auto w-full", children: [
|
|
1012
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-12 text-center", children: [
|
|
1013
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-[30px] font-bold text-black mb-2", children: "Welcome Back" }),
|
|
1014
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base text-gray-600", children: "Sign in to continue" })
|
|
1015
|
+
] }),
|
|
1016
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 p-4 bg-red-50 border border-red-200 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600", children: error }) }),
|
|
1017
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 mb-8", children: [
|
|
1018
|
+
config.google && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1019
|
+
Button,
|
|
1020
|
+
{
|
|
1021
|
+
variant: "secondary",
|
|
1022
|
+
onClick: handleGoogleLogin,
|
|
1023
|
+
disabled: isLoading,
|
|
1024
|
+
className: "w-full",
|
|
1025
|
+
children: [
|
|
1026
|
+
/* @__PURE__ */ jsxRuntime.jsx(GoogleIcon, {}),
|
|
1027
|
+
"Continue with Google"
|
|
1028
|
+
]
|
|
1029
|
+
}
|
|
1030
|
+
),
|
|
1031
|
+
config.apple && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1032
|
+
Button,
|
|
1033
|
+
{
|
|
1034
|
+
variant: "secondary",
|
|
1035
|
+
onClick: handleAppleLogin,
|
|
1036
|
+
disabled: isLoading,
|
|
1037
|
+
className: "w-full",
|
|
1038
|
+
children: [
|
|
1039
|
+
/* @__PURE__ */ jsxRuntime.jsx(AppleIcon, {}),
|
|
1040
|
+
"Continue with Apple"
|
|
1041
|
+
]
|
|
1042
|
+
}
|
|
1043
|
+
)
|
|
1044
|
+
] }),
|
|
1045
|
+
config.email?.enabled && (config.google || config.apple) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative mb-8", children: [
|
|
1046
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border-t border-gray-300" }) }),
|
|
1047
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex justify-center text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-4 bg-white text-gray-500", children: "Or continue with email" }) })
|
|
1048
|
+
] }),
|
|
1049
|
+
config.email?.enabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1050
|
+
Button,
|
|
1051
|
+
{
|
|
1052
|
+
variant: "primary",
|
|
1053
|
+
onClick: () => setView("email"),
|
|
1054
|
+
disabled: isLoading,
|
|
1055
|
+
className: "w-full",
|
|
1056
|
+
children: "Continue with Email"
|
|
1057
|
+
}
|
|
1058
|
+
)
|
|
1059
|
+
] }),
|
|
1060
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pb-[36px] px-5", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 text-center leading-relaxed max-w-sm mx-auto", children: [
|
|
1061
|
+
"By signing in, you agree to our",
|
|
1062
|
+
" ",
|
|
1063
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: "text-gray-700 underline underline-offset-2 active:opacity-80", children: "Terms and Conditions" }),
|
|
1064
|
+
" ",
|
|
1065
|
+
"and",
|
|
1066
|
+
" ",
|
|
1067
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: "text-gray-700 underline underline-offset-2 active:opacity-80", children: "Privacy Policy" }),
|
|
1068
|
+
"."
|
|
1069
|
+
] }) })
|
|
1070
|
+
] });
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
exports.AuthProvider = AuthProvider;
|
|
1074
|
+
exports.LoginPage = LoginPage;
|
|
1075
|
+
exports.createStorageService = createStorageService;
|
|
1076
|
+
exports.exchangeToken = exchangeToken;
|
|
1077
|
+
exports.loadAppleSDK = loadAppleSDK;
|
|
1078
|
+
exports.loadGoogleSDK = loadGoogleSDK;
|
|
1079
|
+
exports.localStorageAdapter = localStorageAdapter;
|
|
1080
|
+
exports.memoryStorageAdapter = memoryStorageAdapter;
|
|
1081
|
+
exports.refreshToken = refreshToken;
|
|
1082
|
+
exports.signInWithApple = signInWithApple;
|
|
1083
|
+
exports.signInWithEmail = signInWithEmail;
|
|
1084
|
+
exports.signInWithGoogle = signInWithGoogle;
|
|
1085
|
+
exports.storageService = storageService;
|
|
1086
|
+
exports.useAuth = useAuth;
|
|
1087
|
+
exports.validateConfig = validateConfig;
|
|
1088
|
+
//# sourceMappingURL=index.cjs.map
|
|
1089
|
+
//# sourceMappingURL=index.cjs.map
|