react-oauth-providers 1.0.0 → 1.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/dist/index.mjs ADDED
@@ -0,0 +1,565 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __async = (__this, __arguments, generator) => {
21
+ return new Promise((resolve, reject) => {
22
+ var fulfilled = (value) => {
23
+ try {
24
+ step(generator.next(value));
25
+ } catch (e) {
26
+ reject(e);
27
+ }
28
+ };
29
+ var rejected = (value) => {
30
+ try {
31
+ step(generator.throw(value));
32
+ } catch (e) {
33
+ reject(e);
34
+ }
35
+ };
36
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
37
+ step((generator = generator.apply(__this, __arguments)).next());
38
+ });
39
+ };
40
+
41
+ // src/AuthContext.tsx
42
+ import { createContext } from "react";
43
+ var AuthContext = createContext({});
44
+
45
+ // src/AuthProvider.tsx
46
+ import { useCallback, useEffect, useMemo, useState } from "react";
47
+
48
+ // src/utils/session.ts
49
+ function setSessionItem(key, value, ttlMs) {
50
+ const data = {
51
+ value,
52
+ expires: Date.now() + ttlMs
53
+ };
54
+ sessionStorage.setItem(key, JSON.stringify(data));
55
+ }
56
+ function getSessionItem(key) {
57
+ const dataStr = sessionStorage.getItem(key);
58
+ if (!dataStr)
59
+ return null;
60
+ try {
61
+ const data = JSON.parse(dataStr);
62
+ if (Date.now() > data.expires) {
63
+ sessionStorage.removeItem(key);
64
+ return null;
65
+ }
66
+ return data.value;
67
+ } catch (e) {
68
+ return null;
69
+ }
70
+ }
71
+ function removeSessionItem(key) {
72
+ try {
73
+ sessionStorage.removeItem(key);
74
+ } catch (error) {
75
+ return null;
76
+ }
77
+ }
78
+
79
+ // src/utils/http.ts
80
+ function request(url, options) {
81
+ return __async(this, null, function* () {
82
+ var _a;
83
+ let body;
84
+ const headers = __spreadValues({}, (_a = options.headers) != null ? _a : {});
85
+ try {
86
+ body = options.body;
87
+ headers["Content-Type"] = headers["Content-Type"] || "application/json";
88
+ const response = yield fetch(url, {
89
+ method: options.method,
90
+ headers,
91
+ body
92
+ });
93
+ if (!response.ok) {
94
+ const error = yield response.json().catch(() => ({}));
95
+ const expectedError = response.status >= 400 && response.status < 500;
96
+ if (!expectedError) {
97
+ console.log({ error });
98
+ console.log("Unexpected error occurs");
99
+ }
100
+ return Promise.reject(__spreadValues({ status: response.status }, error));
101
+ }
102
+ return { data: yield response.json() };
103
+ } catch (error) {
104
+ console.log("Network or unexpected error:", error);
105
+ return Promise.reject(error);
106
+ }
107
+ });
108
+ }
109
+ var http_default = {
110
+ get: (url, options) => request(url, __spreadProps(__spreadValues({}, options), { method: "GET" })),
111
+ post: (url, body, options) => request(url, __spreadProps(__spreadValues({}, options), { method: "POST", body })),
112
+ put: (url, body, options) => request(url, __spreadProps(__spreadValues({}, options), { method: "PUT", body })),
113
+ delete: (url, options) => request(url, __spreadProps(__spreadValues({}, options), { method: "DELETE" }))
114
+ };
115
+
116
+ // src/utils/pkce.ts
117
+ var generatePkce = () => __async(void 0, null, function* () {
118
+ const array = new Uint8Array(32);
119
+ crypto.getRandomValues(array);
120
+ const verifier = base64UrlEncode(array);
121
+ const buffer = new TextEncoder().encode(verifier);
122
+ const digest = yield crypto.subtle.digest("SHA-256", buffer);
123
+ const challenge = base64UrlEncode(new Uint8Array(digest));
124
+ return {
125
+ verifier,
126
+ challenge,
127
+ algorithm: "S256"
128
+ };
129
+ });
130
+ var base64UrlEncode = (array) => {
131
+ let str = "";
132
+ const chunkSize = 32768;
133
+ for (let i = 0; i < array.length; i += chunkSize) {
134
+ const chunk = array.subarray(i, i + chunkSize);
135
+ str += String.fromCharCode.apply(null, Array.from(chunk));
136
+ }
137
+ return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
138
+ };
139
+
140
+ // src/utils/others.ts
141
+ var toURL = (input) => {
142
+ try {
143
+ return new URL(input);
144
+ } catch (e) {
145
+ return new URL(input, window.location.origin);
146
+ }
147
+ };
148
+ var makeKey = (key, prefix) => {
149
+ return prefix ? `${prefix}_${key}` : key;
150
+ };
151
+
152
+ // src/authConfig.ts
153
+ var config = null;
154
+ var getConfig = () => config;
155
+ function createInternalConfig(passedConfig) {
156
+ const {
157
+ preLogin = () => null,
158
+ postLogin = () => null,
159
+ onLogInError = () => null,
160
+ storage = "local",
161
+ spaCallbackUri = passedConfig.spaCallbackUri ? toURL(passedConfig.spaCallbackUri).toString() : window.location.origin,
162
+ logoutRedirect = passedConfig.logoutRedirect ? toURL(passedConfig.logoutRedirect).toString() : window.location.origin,
163
+ storageKeyPrefix = "auth_",
164
+ googleProvider
165
+ } = passedConfig;
166
+ config = __spreadProps(__spreadValues({}, passedConfig), {
167
+ preLogin,
168
+ postLogin,
169
+ onLogInError,
170
+ storage,
171
+ storageKeyPrefix,
172
+ spaCallbackUri,
173
+ logoutRedirect,
174
+ keys: createAuthKeys(storageKeyPrefix),
175
+ googleProvider: googleProvider ? __spreadValues({ type: "spa", scope: "openid email profile" }, googleProvider) : void 0
176
+ });
177
+ return config;
178
+ }
179
+ function createAuthKeys(prefix) {
180
+ return {
181
+ PROVIDER_TYPE: makeKey("provider_type", prefix),
182
+ PROVIDER: makeKey("provider", prefix),
183
+ VERIFIER: makeKey("verifier", prefix),
184
+ ACCESS_TOKEN: makeKey("access_token", prefix),
185
+ EXPIRES_IN: makeKey("expires_in", prefix),
186
+ REFRESH_TOKEN: makeKey("refresh_token", prefix),
187
+ TOKEN_TYPE: makeKey("token_type", prefix),
188
+ ID_TOKEN: makeKey("id_token", prefix)
189
+ };
190
+ }
191
+
192
+ // src/utils/constants.ts
193
+ var AUTH_PROVIDERS = { google: "google" };
194
+
195
+ // src/utils/storage.ts
196
+ var keepToStorage = (type, key, value) => {
197
+ if (type === "local")
198
+ localStorage.setItem(key, value);
199
+ else if (type === "session")
200
+ sessionStorage.setItem(key, value);
201
+ };
202
+ var removeFromStorage = (type, key) => {
203
+ if (type === "local")
204
+ localStorage.removeItem(key);
205
+ else if (type === "session")
206
+ sessionStorage.removeItem(key);
207
+ };
208
+ var getFromStorage = (type, key) => {
209
+ var _a, _b;
210
+ if (type === "local")
211
+ return (_a = localStorage.getItem(key)) != null ? _a : "";
212
+ else if (type === "session")
213
+ return (_b = sessionStorage.getItem(key)) != null ? _b : "";
214
+ return "";
215
+ };
216
+
217
+ // src/google/index.ts
218
+ var signinWithGoogleSpa = () => __async(void 0, null, function* () {
219
+ const config2 = getConfig();
220
+ if (!config2)
221
+ return;
222
+ const { googleProvider, keys } = config2;
223
+ const { verifier, challenge, algorithm } = yield generatePkce();
224
+ setSessionItem(keys.VERIFIER, verifier, 3 * 60 * 1e3);
225
+ setSessionItem(keys.PROVIDER, AUTH_PROVIDERS.google, 3 * 60 * 1e3);
226
+ setSessionItem(keys.PROVIDER_TYPE, googleProvider == null ? void 0 : googleProvider.type, 3 * 60 * 1e3);
227
+ const params = new URLSearchParams({
228
+ client_id: googleProvider == null ? void 0 : googleProvider.clientId,
229
+ redirect_uri: config2 == null ? void 0 : config2.spaCallbackUri,
230
+ response_type: "code",
231
+ scope: (googleProvider == null ? void 0 : googleProvider.scope) || "",
232
+ code_challenge: challenge,
233
+ code_challenge_method: algorithm,
234
+ access_type: "offline",
235
+ prompt: "consent"
236
+ });
237
+ window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
238
+ });
239
+ var signinWithGoogleCallback = (code, verifier) => __async(void 0, null, function* () {
240
+ if (!code || !verifier)
241
+ return;
242
+ const config2 = getConfig();
243
+ if (!config2)
244
+ return;
245
+ const { googleProvider, keys } = config2;
246
+ const type = "spa";
247
+ try {
248
+ const data = new URLSearchParams({
249
+ client_id: googleProvider == null ? void 0 : googleProvider.clientId,
250
+ grant_type: "authorization_code",
251
+ code,
252
+ redirect_uri: config2 == null ? void 0 : config2.spaCallbackUri,
253
+ code_verifier: verifier
254
+ });
255
+ const res = yield http_default.post(
256
+ "https://oauth2.googleapis.com/token",
257
+ data.toString(),
258
+ {
259
+ headers: { "Content-Type": "application/x-www-form-urlencoded" }
260
+ }
261
+ );
262
+ const tokens = res.data;
263
+ if (tokens == null ? void 0 : tokens.access_token) {
264
+ removeSessionItem(keys.PROVIDER);
265
+ removeSessionItem(keys.VERIFIER);
266
+ removeSessionItem(keys.PROVIDER_TYPE);
267
+ keepToStorage(config2.storage, keys.PROVIDER, AUTH_PROVIDERS.google);
268
+ keepToStorage(config2.storage, keys.PROVIDER_TYPE, type);
269
+ keepToStorage(config2.storage, keys.ACCESS_TOKEN, tokens.access_token);
270
+ keepToStorage(
271
+ config2.storage,
272
+ keys.EXPIRES_IN,
273
+ String(Date.now() + tokens.expires_in * 1e3)
274
+ );
275
+ keepToStorage(config2.storage, keys.REFRESH_TOKEN, tokens.refresh_token);
276
+ keepToStorage(config2.storage, keys.TOKEN_TYPE, tokens.token_type);
277
+ keepToStorage(config2.storage, keys.ID_TOKEN, tokens.id_token);
278
+ }
279
+ } catch (error) {
280
+ return Promise.reject(error);
281
+ }
282
+ });
283
+ var getGoogleSessionUser = () => __async(void 0, null, function* () {
284
+ const config2 = getConfig();
285
+ if (!config2)
286
+ return;
287
+ const { keys } = config2;
288
+ try {
289
+ const accessToken = getFromStorage(config2.storage, keys.ACCESS_TOKEN);
290
+ if (!accessToken)
291
+ return;
292
+ const res = yield http_default.get(
293
+ `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${accessToken}`
294
+ );
295
+ const user = res.data;
296
+ return user;
297
+ } catch (error) {
298
+ return Promise.reject(error);
299
+ }
300
+ });
301
+ var refreshGoogleAccessToken = () => __async(void 0, null, function* () {
302
+ const config2 = getConfig();
303
+ if (!config2)
304
+ return;
305
+ const { googleProvider, keys } = config2;
306
+ const refreshToken = getFromStorage(config2.storage, keys.REFRESH_TOKEN);
307
+ if (!refreshToken)
308
+ return;
309
+ try {
310
+ const data = new URLSearchParams({
311
+ client_id: googleProvider == null ? void 0 : googleProvider.clientId,
312
+ grant_type: "refresh_token",
313
+ refresh_token: refreshToken
314
+ });
315
+ const res = yield http_default.post(
316
+ "https://oauth2.googleapis.com/token",
317
+ data.toString(),
318
+ {
319
+ headers: { "Content-Type": "application/x-www-form-urlencoded" }
320
+ }
321
+ );
322
+ const tokens = res.data;
323
+ if (!tokens.access_token) {
324
+ console.error("Failed to refresh access token", tokens);
325
+ return null;
326
+ }
327
+ keepToStorage(config2.storage, keys.ACCESS_TOKEN, tokens.access_token);
328
+ if (tokens.expires_in) {
329
+ keepToStorage(
330
+ config2.storage,
331
+ keys.EXPIRES_IN,
332
+ String(Date.now() + tokens.expires_in * 1e3)
333
+ );
334
+ }
335
+ if (tokens.id_token)
336
+ keepToStorage(config2.storage, keys.ID_TOKEN, tokens.id_token);
337
+ if (tokens.token_type)
338
+ keepToStorage(config2.storage, keys.TOKEN_TYPE, tokens.token_type);
339
+ } catch (error) {
340
+ return Promise.reject(error);
341
+ }
342
+ });
343
+
344
+ // src/actions.ts
345
+ var refreshTimeout = null;
346
+ var handleCallback = () => __async(void 0, null, function* () {
347
+ const config2 = getConfig();
348
+ if (!config2)
349
+ return;
350
+ const keys = config2 == null ? void 0 : config2.keys;
351
+ const authMode = getSessionItem(keys.PROVIDER);
352
+ const verifier = getSessionItem(keys.VERIFIER);
353
+ if (!authMode || !verifier)
354
+ return;
355
+ try {
356
+ switch (authMode) {
357
+ case AUTH_PROVIDERS.google: {
358
+ const searchQuery = new URLSearchParams(window.location.search);
359
+ const code = searchQuery.get("code");
360
+ if (!code)
361
+ return;
362
+ yield signinWithGoogleCallback(code, verifier);
363
+ break;
364
+ }
365
+ default:
366
+ break;
367
+ }
368
+ } catch (error) {
369
+ return Promise.reject(error);
370
+ }
371
+ });
372
+ var handleFetchSessionUser = (retry = true) => __async(void 0, null, function* () {
373
+ const config2 = getConfig();
374
+ if (!config2)
375
+ return;
376
+ const keys = config2 == null ? void 0 : config2.keys;
377
+ const authMode = getFromStorage(config2.storage, keys.PROVIDER);
378
+ if (!authMode)
379
+ return;
380
+ let sessionUser = null;
381
+ try {
382
+ switch (authMode) {
383
+ case AUTH_PROVIDERS.google: {
384
+ sessionUser = yield getGoogleSessionUser();
385
+ break;
386
+ }
387
+ default:
388
+ break;
389
+ }
390
+ if (sessionUser)
391
+ handleScheduleTokenRefresh();
392
+ return sessionUser;
393
+ } catch (error) {
394
+ console.log("Fetch Session User Error:", error);
395
+ if (!retry)
396
+ return null;
397
+ switch (authMode) {
398
+ case AUTH_PROVIDERS.google: {
399
+ yield refreshGoogleAccessToken();
400
+ yield handleFetchSessionUser(false);
401
+ break;
402
+ }
403
+ default:
404
+ break;
405
+ }
406
+ return null;
407
+ }
408
+ });
409
+ var handleScheduleTokenRefresh = () => {
410
+ var _a;
411
+ const config2 = getConfig();
412
+ if (!config2)
413
+ return;
414
+ const keys = config2 == null ? void 0 : config2.keys;
415
+ const authMode = getFromStorage(config2.storage, keys.PROVIDER);
416
+ if (!authMode)
417
+ return;
418
+ const expiresIn = Number(
419
+ (_a = getFromStorage(config2.storage, keys.EXPIRES_IN)) != null ? _a : 0
420
+ );
421
+ if (!expiresIn)
422
+ return;
423
+ clearTokenRefresh();
424
+ const refreshTime = expiresIn - 60 * 1e3;
425
+ refreshTimeout = setTimeout(() => __async(void 0, null, function* () {
426
+ switch (authMode) {
427
+ case AUTH_PROVIDERS.google: {
428
+ yield refreshGoogleAccessToken();
429
+ break;
430
+ }
431
+ default:
432
+ break;
433
+ }
434
+ }), refreshTime);
435
+ };
436
+ var clearTokenRefresh = () => {
437
+ if (refreshTimeout) {
438
+ clearTimeout(refreshTimeout);
439
+ refreshTimeout = null;
440
+ }
441
+ };
442
+ var logout = () => {
443
+ clearTokens();
444
+ clearTokenRefresh();
445
+ };
446
+ var clearTokens = () => {
447
+ const config2 = getConfig();
448
+ if (!config2)
449
+ return;
450
+ const keys = config2 == null ? void 0 : config2.keys;
451
+ const {
452
+ PROVIDER,
453
+ ACCESS_TOKEN,
454
+ EXPIRES_IN,
455
+ REFRESH_TOKEN,
456
+ TOKEN_TYPE,
457
+ ID_TOKEN,
458
+ PROVIDER_TYPE
459
+ } = keys;
460
+ removeFromStorage(config2.storage, PROVIDER);
461
+ removeFromStorage(config2.storage, PROVIDER_TYPE);
462
+ removeFromStorage(config2.storage, ACCESS_TOKEN);
463
+ removeFromStorage(config2.storage, EXPIRES_IN);
464
+ removeFromStorage(config2.storage, REFRESH_TOKEN);
465
+ removeFromStorage(config2.storage, TOKEN_TYPE);
466
+ removeFromStorage(config2.storage, ID_TOKEN);
467
+ };
468
+
469
+ // src/AuthProvider.tsx
470
+ import { jsx } from "react/jsx-runtime";
471
+ var AuthProvider = ({
472
+ authConfig,
473
+ children
474
+ }) => {
475
+ const config2 = useMemo(
476
+ () => createInternalConfig(authConfig),
477
+ [authConfig]
478
+ );
479
+ const tmpAuthProviderType = useMemo(
480
+ () => getSessionItem(config2.keys.PROVIDER_TYPE),
481
+ [config2]
482
+ );
483
+ const [loginInProgress, setLoginInProgress] = useState(true);
484
+ const [user, setUser] = useState();
485
+ useEffect(() => {
486
+ handleFetchSessionUser().then((user2) => {
487
+ setUser(user2);
488
+ setLoginInProgress(false);
489
+ });
490
+ }, []);
491
+ const signinWithGoogle = useCallback(() => __async(void 0, null, function* () {
492
+ var _a, _b, _c;
493
+ if (!((_a = config2.googleProvider) == null ? void 0 : _a.clientId)) {
494
+ throw Error(
495
+ "'clientId' must be set in the 'googleProvider' object in the AuthProvider Config"
496
+ );
497
+ }
498
+ (_b = config2.preLogin) == null ? void 0 : _b.call(config2);
499
+ try {
500
+ if (config2.googleProvider.type === "spa") {
501
+ yield signinWithGoogleSpa();
502
+ }
503
+ } catch (error) {
504
+ (_c = config2.onLogInError) == null ? void 0 : _c.call(config2, error);
505
+ }
506
+ }), []);
507
+ useEffect(() => {
508
+ if (!config2 || !tmpAuthProviderType)
509
+ return;
510
+ if (window.location.pathname !== new URL(config2.spaCallbackUri).pathname) {
511
+ return;
512
+ }
513
+ const callback = () => __async(void 0, null, function* () {
514
+ var _a, _b;
515
+ try {
516
+ yield handleCallback();
517
+ (_a = config2.postLogin) == null ? void 0 : _a.call(config2);
518
+ } catch (error) {
519
+ (_b = config2.onLogInError) == null ? void 0 : _b.call(config2, error);
520
+ }
521
+ });
522
+ callback();
523
+ }, [tmpAuthProviderType, config2]);
524
+ const logOut = useCallback(
525
+ ({ logoutHint, redirect = true, redirectUri } = {}) => {
526
+ logout();
527
+ if (!redirect)
528
+ return;
529
+ const baseUrl = redirectUri || config2.logoutRedirect || "/";
530
+ const params = new URLSearchParams();
531
+ if (logoutHint)
532
+ params.append("logout_hint", logoutHint);
533
+ const url = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
534
+ window.location.assign(url);
535
+ },
536
+ []
537
+ );
538
+ return /* @__PURE__ */ jsx(
539
+ AuthContext.Provider,
540
+ {
541
+ value: {
542
+ loginInProgress,
543
+ signinWithGoogle,
544
+ user,
545
+ logOut
546
+ },
547
+ children
548
+ }
549
+ );
550
+ };
551
+
552
+ // src/useAuth.tsx
553
+ import { useContext } from "react";
554
+ function useAuth() {
555
+ const ctx = useContext(AuthContext);
556
+ if (!ctx) {
557
+ throw new Error("useAuth must be used within an AuthProvider");
558
+ }
559
+ return ctx;
560
+ }
561
+ export {
562
+ AuthContext,
563
+ AuthProvider,
564
+ useAuth
565
+ };
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "react-oauth-providers",
3
- "version": "1.0.0",
4
- "main": "dist/index.cjs.js",
5
- "module": "dist/index.esm.js",
6
- "types": "dist/types/index.d.ts",
3
+ "version": "1.1.0",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.mjs",
6
+ "types": "dist/index.d.ts",
7
7
  "scripts": {
8
- "build": "tsup src/index.tsx --format cjs,esm --dts --out-dir dist",
8
+ "build": "tsup",
9
9
  "test": "echo \"Error: no test specified\" && exit 1"
10
10
  },
11
- "dependencies": {
12
- "oauth-pkce": "^0.0.7"
13
- },
11
+ "files": [
12
+ "dist"
13
+ ],
14
14
  "peerDependencies": {
15
15
  "react": "^18.0.0",
16
16
  "react-dom": "^18.0.0"
@@ -1,4 +0,0 @@
1
- import { createContext } from 'react'
2
- import type { IAuthContext } from './types'
3
-
4
- export const AuthContext = createContext({} as IAuthContext)
@@ -1,103 +0,0 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from "react";
2
- import { AuthContext } from "./AuthContext";
3
- import * as actions from "./actions";
4
- import type { IAuthProvider, ILogoutFunc, IAuthInternalConfig } from "./types";
5
- import { createInternalConfig } from "./authConfig";
6
- import { getSessionItem } from "./utils/session";
7
-
8
- export const AuthProvider: React.FC<IAuthProvider> = ({
9
- authConfig,
10
- children,
11
- }) => {
12
- const config = useMemo<IAuthInternalConfig>(
13
- () => createInternalConfig(authConfig),
14
- [authConfig]
15
- );
16
- const tmpAuthProviderType = useMemo(
17
- () => getSessionItem(config.keys.PROVIDER_TYPE),
18
- [config]
19
- );
20
-
21
- const [loginInProgress, setLoginInProgress] = useState(true);
22
- const [user, setUser] = useState<null | any>();
23
-
24
- useEffect(() => {
25
- actions.handleFetchSessionUser().then((user) => {
26
- setUser(user);
27
- setLoginInProgress(false);
28
- });
29
- }, []);
30
-
31
- const signinWithGoogle = useCallback(async () => {
32
- if (!config.googleProvider?.clientId) {
33
- throw Error(
34
- "'clientId' must be set in the 'googleProvider' object in the AuthProvider Config"
35
- );
36
- }
37
-
38
- config.preLogin?.();
39
-
40
- try {
41
- if (config.googleProvider.type === "spa") {
42
- await actions.signinWithGoogleSpa();
43
- }
44
- } catch (error) {
45
- config.onLogInError?.(error);
46
- }
47
- }, []);
48
-
49
- // callback handler for spa login
50
- useEffect(() => {
51
- if (!config || !tmpAuthProviderType) return;
52
- if (window.location.pathname !== new URL(config.spaCallbackUri!).pathname) {
53
- return;
54
- }
55
-
56
- const callback = async () => {
57
- try {
58
- await actions.handleCallback();
59
-
60
- config.postLogin?.();
61
- } catch (error) {
62
- config.onLogInError?.(error);
63
- }
64
- };
65
-
66
- callback();
67
- }, [tmpAuthProviderType, config]);
68
-
69
- const logOut = useCallback<ILogoutFunc>(
70
- ({ logoutHint, redirect = true, redirectUri } = {}) => {
71
- actions.logout();
72
-
73
- if (!redirect) return;
74
-
75
- // Determine redirect URL or fallback to root
76
- const baseUrl = redirectUri || config.logoutRedirect || "/";
77
-
78
- const params = new URLSearchParams();
79
- if (logoutHint) params.append("logout_hint", logoutHint);
80
-
81
- // Append params only if there are any
82
- const url = params.toString()
83
- ? `${baseUrl}?${params.toString()}`
84
- : baseUrl;
85
-
86
- window.location.assign(url);
87
- },
88
- []
89
- );
90
-
91
- return (
92
- <AuthContext.Provider
93
- value={{
94
- loginInProgress,
95
- signinWithGoogle,
96
- user,
97
- logOut,
98
- }}
99
- >
100
- {children}
101
- </AuthContext.Provider>
102
- );
103
- };