authscape 1.0.766 → 1.0.772
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/index.js +387 -485
- package/package.json +1 -1
- package/src/components/AuthScapeApp.js +105 -26
- package/src/services/apiService.js +55 -53
- package/src/services/authService.js +15 -47
- package/src/services/signInValidator.js +0 -117
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import Head from "next/head";
|
|
|
4
4
|
|
|
5
5
|
// Re-export toast and transitions so pages can import from authscape
|
|
6
6
|
export { toast, Bounce, Slide, Zoom, Flip };
|
|
7
|
-
import {
|
|
7
|
+
import { useRouter } from "next/router";
|
|
8
8
|
import axios from "axios";
|
|
9
9
|
import querystring from "query-string";
|
|
10
10
|
import Router from "next/router";
|
|
@@ -17,18 +17,33 @@ import { HubConnectionBuilder, LogLevel, HttpTransportType } from '@microsoft/si
|
|
|
17
17
|
import Cookies from 'js-cookie';
|
|
18
18
|
|
|
19
19
|
// ============================================================================
|
|
20
|
-
//
|
|
20
|
+
// Auth Redirect Circuit Breaker
|
|
21
21
|
// ============================================================================
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
const AUTH_REDIRECT_KEY = 'authscape_redirect_count';
|
|
23
|
+
const AUTH_REDIRECT_TS_KEY = 'authscape_redirect_ts';
|
|
24
|
+
const AUTH_MAX_REDIRECTS = 3;
|
|
25
|
+
const AUTH_REDIRECT_WINDOW_MS = 30000; // 30 seconds
|
|
26
|
+
|
|
27
|
+
const checkAndIncrementRedirect = () => {
|
|
28
|
+
if (typeof window === 'undefined') return false;
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
const storedTs = parseInt(sessionStorage.getItem(AUTH_REDIRECT_TS_KEY) || '0', 10);
|
|
31
|
+
let count = parseInt(sessionStorage.getItem(AUTH_REDIRECT_KEY) || '0', 10);
|
|
32
|
+
|
|
33
|
+
if (now - storedTs > AUTH_REDIRECT_WINDOW_MS) {
|
|
34
|
+
count = 0;
|
|
35
|
+
sessionStorage.setItem(AUTH_REDIRECT_TS_KEY, String(now));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
count += 1;
|
|
39
|
+
sessionStorage.setItem(AUTH_REDIRECT_KEY, String(count));
|
|
40
|
+
return count <= AUTH_MAX_REDIRECTS;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const resetRedirectCounter = () => {
|
|
44
|
+
if (typeof window === 'undefined') return;
|
|
45
|
+
sessionStorage.removeItem(AUTH_REDIRECT_KEY);
|
|
46
|
+
sessionStorage.removeItem(AUTH_REDIRECT_TS_KEY);
|
|
32
47
|
};
|
|
33
48
|
|
|
34
49
|
// ============================================================================
|
|
@@ -484,10 +499,24 @@ export function AuthScapeApp({
|
|
|
484
499
|
const queryCodeUsed = useRef(null);
|
|
485
500
|
const ga4React = useRef(null);
|
|
486
501
|
const errorTrackingInitializedRef = useRef(false);
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
502
|
+
const loginRedirectPending = useRef(false);
|
|
503
|
+
|
|
504
|
+
// Use Pages Router's `useRouter` instead of `useSearchParams`/`usePathname`
|
|
505
|
+
// from `next/navigation`. The `next/navigation` hooks don't hydrate query
|
|
506
|
+
// params reliably on the 404 page in Webkit/Safari — when the IDP redirects
|
|
507
|
+
// back to /signin-oidc (a URL with no Next.js page in the consumer project),
|
|
508
|
+
// useSearchParams() returns null and the PKCE code is never picked up.
|
|
509
|
+
// The Pages Router router parses query from `asPath` and works on 404 too.
|
|
510
|
+
const router = useRouter();
|
|
511
|
+
const rawQueryCode = router.isReady ? router.query.code : null;
|
|
512
|
+
// router.query values can be string | string[] — coerce to a single string.
|
|
513
|
+
const queryCode = typeof rawQueryCode === "string"
|
|
514
|
+
? rawQueryCode
|
|
515
|
+
: (Array.isArray(rawQueryCode) ? rawQueryCode[0] : null);
|
|
516
|
+
// Pages Router's `router.pathname` returns the route file path (e.g. "/_error"
|
|
517
|
+
// for the 404 page). The auth-redirect guard below compares against
|
|
518
|
+
// "/signin-oidc", so we derive the actual URL path from `asPath` instead.
|
|
519
|
+
const pathname = (router.asPath || "").split("?")[0].split("#")[0];
|
|
491
520
|
|
|
492
521
|
const signInValidator = async (codeFromQuery) => {
|
|
493
522
|
if (queryCodeUsed.current === codeFromQuery) return;
|
|
@@ -500,7 +529,8 @@ export function AuthScapeApp({
|
|
|
500
529
|
const codeVerifier = window.localStorage.getItem("verifier");
|
|
501
530
|
if (!codeFromQuery || !codeVerifier) {
|
|
502
531
|
window.localStorage.clear();
|
|
503
|
-
|
|
532
|
+
setIsSigningIn(false);
|
|
533
|
+
setFrontEndLoadedState(true);
|
|
504
534
|
return;
|
|
505
535
|
}
|
|
506
536
|
|
|
@@ -526,34 +556,72 @@ export function AuthScapeApp({
|
|
|
526
556
|
|
|
527
557
|
window.localStorage.removeItem("verifier");
|
|
528
558
|
|
|
529
|
-
|
|
530
|
-
|
|
559
|
+
Cookies.set("access_token", response.data.access_token, {
|
|
560
|
+
expires: 365,
|
|
531
561
|
path: "/",
|
|
532
562
|
domain: domainHost,
|
|
533
563
|
secure: true,
|
|
534
564
|
});
|
|
535
|
-
|
|
536
|
-
|
|
565
|
+
Cookies.set("expires_in", String(response.data.expires_in), {
|
|
566
|
+
expires: 365,
|
|
537
567
|
path: "/",
|
|
538
568
|
domain: domainHost,
|
|
539
569
|
secure: true,
|
|
540
570
|
});
|
|
541
|
-
|
|
542
|
-
|
|
571
|
+
Cookies.set("refresh_token", response.data.refresh_token, {
|
|
572
|
+
expires: 365,
|
|
543
573
|
path: "/",
|
|
544
574
|
domain: domainHost,
|
|
545
575
|
secure: true,
|
|
546
576
|
});
|
|
547
577
|
|
|
578
|
+
resetRedirectCounter();
|
|
579
|
+
|
|
548
580
|
const redirectUri = window.localStorage.getItem("redirectUri") || "/";
|
|
549
581
|
window.localStorage.clear();
|
|
550
582
|
|
|
551
|
-
|
|
583
|
+
// Pre-load user while spinner is still showing — eliminates the
|
|
584
|
+
// second GetCurrentUser call (and resulting re-renders) on the destination page.
|
|
585
|
+
let usr = null;
|
|
586
|
+
try {
|
|
587
|
+
usr = await module.exports.apiService().GetCurrentUser();
|
|
588
|
+
} catch (fetchErr) {
|
|
589
|
+
console.warn("[AuthScape] GetCurrentUser failed after token exchange:", fetchErr);
|
|
590
|
+
}
|
|
591
|
+
const enrichedUser = ensureUserHelpers(usr);
|
|
592
|
+
|
|
593
|
+
signedInUser.current = enrichedUser;
|
|
594
|
+
setSignedInUserState(enrichedUser);
|
|
595
|
+
setFrontEndLoadedState(true);
|
|
596
|
+
|
|
597
|
+
// Prevent the useEffect from calling GetCurrentUser again when queryCode → null.
|
|
598
|
+
loadingAuth.current = true;
|
|
599
|
+
|
|
600
|
+
if (enableErrorTracking && enrichedUser && !errorTrackingInitializedRef.current) {
|
|
601
|
+
initializeErrorTracking(enrichedUser);
|
|
602
|
+
errorTrackingInitializedRef.current = true;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (onUserLoaded && enrichedUser) {
|
|
606
|
+
onUserLoaded(enrichedUser);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Dismiss spinner before navigating so destination renders logged-in UI on first paint.
|
|
610
|
+
setIsSigningIn(false);
|
|
611
|
+
|
|
612
|
+
// Client-side navigation preserves the React component tree and all ref values,
|
|
613
|
+
// eliminating the hard-reload → remount → re-render chain.
|
|
614
|
+
// Fall back to hard navigation for absolute external URLs.
|
|
615
|
+
if (redirectUri.startsWith("http://") || redirectUri.startsWith("https://")) {
|
|
616
|
+
window.location.href = redirectUri;
|
|
617
|
+
} else {
|
|
618
|
+
Router.push(redirectUri);
|
|
619
|
+
}
|
|
552
620
|
} catch (exp) {
|
|
553
621
|
console.error("PKCE sign-in failed", exp);
|
|
554
622
|
window.localStorage.clear();
|
|
555
623
|
setIsSigningIn(false);
|
|
556
|
-
|
|
624
|
+
setFrontEndLoadedState(true);
|
|
557
625
|
}
|
|
558
626
|
};
|
|
559
627
|
|
|
@@ -653,8 +721,14 @@ export function AuthScapeApp({
|
|
|
653
721
|
enforceLoggedIn &&
|
|
654
722
|
pathname !== "/signin-oidc" &&
|
|
655
723
|
frontEndLoadedState &&
|
|
656
|
-
!signedInUserState
|
|
724
|
+
!signedInUserState &&
|
|
725
|
+
!loginRedirectPending.current
|
|
657
726
|
) {
|
|
727
|
+
if (!checkAndIncrementRedirect()) {
|
|
728
|
+
console.warn('[AuthScape] Auth redirect loop detected — halting redirects.');
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
loginRedirectPending.current = true;
|
|
658
732
|
module.exports.authService().login();
|
|
659
733
|
}
|
|
660
734
|
}, [signedInUserState, enforceLoggedIn, frontEndLoadedState, pathname]);
|
|
@@ -780,3 +854,8 @@ export function AuthScapeApp({
|
|
|
780
854
|
</>
|
|
781
855
|
);
|
|
782
856
|
}
|
|
857
|
+
|
|
858
|
+
// AuthScapeProvider is the umbrella component that bundles the three always-on AuthScape
|
|
859
|
+
// features (auth, error tracking, analytics) plus optional in-app notifications. Wrap your
|
|
860
|
+
// NextJS _app.js return value with it. Existing call sites can continue using AuthScapeApp.
|
|
861
|
+
export const AuthScapeProvider = AuthScapeApp;
|
|
@@ -3,19 +3,6 @@ import querystring from 'query-string';
|
|
|
3
3
|
import fileDownload from 'js-file-download';
|
|
4
4
|
import Cookies from 'js-cookie';
|
|
5
5
|
|
|
6
|
-
// Cookie utility function
|
|
7
|
-
const setCookie = (name, value, options = {}) => {
|
|
8
|
-
return new Promise((resolve) => {
|
|
9
|
-
let cookieString = `${name}=${value};`;
|
|
10
|
-
if (options.maxAge) cookieString += `max-age=${options.maxAge};`;
|
|
11
|
-
if (options.path) cookieString += `path=${options.path};`;
|
|
12
|
-
if (options.domain) cookieString += `domain=${options.domain};`;
|
|
13
|
-
if (options.secure) cookieString += `secure;`;
|
|
14
|
-
document.cookie = cookieString;
|
|
15
|
-
resolve();
|
|
16
|
-
});
|
|
17
|
-
};
|
|
18
|
-
|
|
19
6
|
const setupDefaultOptions = async (ctx = null) => {
|
|
20
7
|
let defaultOptions = {};
|
|
21
8
|
if (ctx == null) {
|
|
@@ -42,46 +29,58 @@ const setupDefaultOptions = async (ctx = null) => {
|
|
|
42
29
|
}
|
|
43
30
|
|
|
44
31
|
const RefreshToken = async (originalRequest, instance) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
let response = await instance.post(process.env.authorityUri + "/connect/token",
|
|
49
|
-
querystring.stringify({
|
|
50
|
-
grant_type: 'refresh_token',
|
|
51
|
-
client_id: process.env.client_id,
|
|
52
|
-
client_secret: process.env.client_secret,
|
|
53
|
-
refresh_token: refreshToken
|
|
54
|
-
}), {
|
|
55
|
-
headers: {
|
|
56
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
57
|
-
"Authorization": "Bearer " + accessToken
|
|
58
|
-
}
|
|
59
|
-
});
|
|
32
|
+
try {
|
|
33
|
+
let accessToken = Cookies.get('access_token') || '';
|
|
34
|
+
let refreshToken = Cookies.get('refresh_token') || '';
|
|
60
35
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
36
|
+
if (!refreshToken) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
64
39
|
|
|
65
|
-
await
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
40
|
+
let response = await instance.post(process.env.authorityUri + "/connect/token",
|
|
41
|
+
querystring.stringify({
|
|
42
|
+
grant_type: 'refresh_token',
|
|
43
|
+
client_id: process.env.client_id,
|
|
44
|
+
client_secret: process.env.client_secret,
|
|
45
|
+
refresh_token: refreshToken
|
|
46
|
+
}), {
|
|
47
|
+
headers: {
|
|
48
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
49
|
+
"Authorization": "Bearer " + accessToken
|
|
50
|
+
}
|
|
70
51
|
});
|
|
71
52
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
53
|
+
if (response != null && response.status == 200) {
|
|
54
|
+
let domainHost = window.location.hostname.split('.').slice(-2).join('.');
|
|
55
|
+
originalRequest.headers['Authorization'] = 'Bearer ' + response.data.access_token;
|
|
56
|
+
|
|
57
|
+
Cookies.set('access_token', response.data.access_token, {
|
|
58
|
+
expires: 365,
|
|
59
|
+
path: '/',
|
|
60
|
+
domain: domainHost,
|
|
61
|
+
secure: true
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
Cookies.set('expires_in', String(response.data.expires_in), {
|
|
65
|
+
expires: 365,
|
|
66
|
+
path: '/',
|
|
67
|
+
domain: domainHost,
|
|
68
|
+
secure: true
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
Cookies.set('refresh_token', response.data.refresh_token, {
|
|
72
|
+
expires: 365,
|
|
73
|
+
path: '/',
|
|
74
|
+
domain: domainHost,
|
|
75
|
+
secure: true
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
domain: domainHost,
|
|
83
|
-
secure: true
|
|
84
|
-
});
|
|
81
|
+
return false;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
return false;
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
|
|
@@ -107,16 +106,19 @@ export const apiService = (ctx = null) => {
|
|
|
107
106
|
if (error.response) {
|
|
108
107
|
if (error.response.status === 401 && !originalConfig._retry) {
|
|
109
108
|
originalConfig._retry = true;
|
|
110
|
-
await RefreshToken(originalConfig, instance);
|
|
111
|
-
|
|
109
|
+
const refreshed = await RefreshToken(originalConfig, instance);
|
|
110
|
+
if (refreshed) {
|
|
111
|
+
return instance.request(originalConfig);
|
|
112
|
+
}
|
|
113
|
+
return Promise.reject(error);
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
if (error.response.status === 400) {
|
|
115
117
|
if (error.response.config.url.includes("/connect/token")) {
|
|
116
118
|
let domainHost = window.location.hostname.split('.').slice(-2).join('.');
|
|
117
|
-
Cookies.remove('access_token', { path: '/', domain: domainHost });
|
|
118
|
-
Cookies.remove('refresh_token', { path: '/', domain: domainHost });
|
|
119
|
-
Cookies.remove('expires_in', { path: '/', domain: domainHost });
|
|
119
|
+
Cookies.remove('access_token', { path: '/', domain: domainHost, secure: true });
|
|
120
|
+
Cookies.remove('refresh_token', { path: '/', domain: domainHost, secure: true });
|
|
121
|
+
Cookies.remove('expires_in', { path: '/', domain: domainHost, secure: true });
|
|
120
122
|
}
|
|
121
123
|
return Promise.reject(error);
|
|
122
124
|
}
|
|
@@ -5,7 +5,7 @@ export const authService = () => {
|
|
|
5
5
|
return {
|
|
6
6
|
|
|
7
7
|
dec2hex: (dec) => {
|
|
8
|
-
return ('0' + dec.toString(16)).
|
|
8
|
+
return ('0' + dec.toString(16)).slice(-2)
|
|
9
9
|
},
|
|
10
10
|
generateRandomString: () => {
|
|
11
11
|
var array = new Uint32Array(56/2);
|
|
@@ -64,9 +64,10 @@ export const authService = () => {
|
|
|
64
64
|
|
|
65
65
|
return response;
|
|
66
66
|
},
|
|
67
|
-
login: async (redirectUserUri = null,
|
|
67
|
+
login: async (redirectUserUri = null, deviceId = null) => {
|
|
68
|
+
|
|
69
|
+
let state = authService().generateRandomString();
|
|
68
70
|
|
|
69
|
-
let state = "1234";
|
|
70
71
|
if (redirectUserUri != null)
|
|
71
72
|
{
|
|
72
73
|
localStorage.setItem("redirectUri", redirectUserUri);
|
|
@@ -75,18 +76,8 @@ export const authService = () => {
|
|
|
75
76
|
let verifier = authService().generateRandomString();
|
|
76
77
|
var challenge = await authService().challenge_from_verifier(verifier);
|
|
77
78
|
|
|
78
|
-
console.log('[PKCE] Login initiated');
|
|
79
|
-
console.log('[PKCE] Verifier generated | length:', verifier.length, '| preview:', verifier.substring(0, 10) + '...');
|
|
80
|
-
console.log('[PKCE] Challenge generated | value:', challenge);
|
|
81
|
-
console.log('[PKCE] authorityUri:', process.env.authorityUri);
|
|
82
|
-
console.log('[PKCE] client_id:', process.env.client_id);
|
|
83
|
-
|
|
84
79
|
window.localStorage.setItem("verifier", verifier);
|
|
85
80
|
|
|
86
|
-
const storedVerifier = window.localStorage.getItem("verifier");
|
|
87
|
-
console.log('[PKCE] Verifier stored successfully:', storedVerifier === verifier);
|
|
88
|
-
console.log('[PKCE] Stored verifier length:', storedVerifier?.length);
|
|
89
|
-
|
|
90
81
|
let redirectUri = window.location.origin + "/signin-oidc";
|
|
91
82
|
let loginUri = process.env.authorityUri + "/connect/authorize?response_type=code&state=" + state + "&client_id=" + process.env.client_id + "&scope=email%20openid%20offline_access%20profile%20api1&redirect_uri=" + redirectUri + "&code_challenge=" + challenge + "&code_challenge_method=S256";
|
|
92
83
|
|
|
@@ -95,7 +86,6 @@ export const authService = () => {
|
|
|
95
86
|
loginUri += "&deviceId=" + deviceId; // will be for chrome extention and mobile apps later
|
|
96
87
|
}
|
|
97
88
|
|
|
98
|
-
console.log('[PKCE] Redirecting to:', loginUri);
|
|
99
89
|
window.location.href = loginUri;
|
|
100
90
|
},
|
|
101
91
|
signUp: (redirectUrl = null) => {
|
|
@@ -126,40 +116,18 @@ export const authService = () => {
|
|
|
126
116
|
let domainHost = window.location.hostname.split('.').slice(-2).join('.');
|
|
127
117
|
let AuthUri = process.env.authorityUri;
|
|
128
118
|
|
|
119
|
+
Cookies.remove('access_token', { path: '/', domain: domainHost, secure: true });
|
|
120
|
+
Cookies.remove('refresh_token', { path: '/', domain: domainHost, secure: true });
|
|
121
|
+
Cookies.remove('expires_in', { path: '/', domain: domainHost, secure: true });
|
|
129
122
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// domain: domainHost
|
|
139
|
-
// });
|
|
140
|
-
|
|
141
|
-
// destroyCookie({}, "refresh_token", {
|
|
142
|
-
// maxAge: 2147483647,
|
|
143
|
-
// path: '/',
|
|
144
|
-
// domain: domainHost
|
|
145
|
-
// });
|
|
146
|
-
|
|
147
|
-
// destroyCookie({}, "expires_in", {
|
|
148
|
-
// maxAge: 2147483647,
|
|
149
|
-
// path: '/',
|
|
150
|
-
// domain: domainHost
|
|
151
|
-
// });
|
|
152
|
-
|
|
153
|
-
setTimeout(() => {
|
|
154
|
-
if (redirectUri == null)
|
|
155
|
-
{
|
|
156
|
-
window.location.href = AuthUri + "/connect/logout?redirect=" + window.location.href;
|
|
157
|
-
}
|
|
158
|
-
else
|
|
159
|
-
{
|
|
160
|
-
window.location.href = AuthUri + "/connect/logout?redirect=" + redirectUri;
|
|
161
|
-
}
|
|
162
|
-
}, 500);
|
|
123
|
+
if (redirectUri == null)
|
|
124
|
+
{
|
|
125
|
+
window.location.href = AuthUri + "/connect/logout?redirect=" + window.location.href;
|
|
126
|
+
}
|
|
127
|
+
else
|
|
128
|
+
{
|
|
129
|
+
window.location.href = AuthUri + "/connect/logout?redirect=" + redirectUri;
|
|
130
|
+
}
|
|
163
131
|
|
|
164
132
|
},
|
|
165
133
|
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import axios from 'axios';
|
|
3
|
-
import querystring from "query-string";
|
|
4
|
-
// import Cookies from 'js-cookie';
|
|
5
|
-
|
|
6
|
-
export const signInValidator = async (queryCode) => {
|
|
7
|
-
|
|
8
|
-
let codeVerifier = window.localStorage.getItem("verifier");
|
|
9
|
-
|
|
10
|
-
console.log('[PKCE] signInValidator called');
|
|
11
|
-
console.log('[PKCE] queryCode present:', !!queryCode, '| value:', queryCode);
|
|
12
|
-
console.log('[PKCE] codeVerifier present:', !!codeVerifier, '| length:', codeVerifier?.length);
|
|
13
|
-
|
|
14
|
-
if (queryCode != null && codeVerifier != null)
|
|
15
|
-
{
|
|
16
|
-
const headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
|
17
|
-
|
|
18
|
-
const tokenParams = {
|
|
19
|
-
code: queryCode,
|
|
20
|
-
grant_type: "authorization_code",
|
|
21
|
-
redirect_uri: window.location.origin + "/signin-oidc",
|
|
22
|
-
client_id: process.env.client_id,
|
|
23
|
-
client_secret: process.env.client_secret,
|
|
24
|
-
code_verifier: codeVerifier
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
console.log('[PKCE] Token request params:', {
|
|
28
|
-
...tokenParams,
|
|
29
|
-
client_secret: tokenParams.client_secret ? '***' : '(missing!)',
|
|
30
|
-
code: tokenParams.code?.substring(0, 10) + '...',
|
|
31
|
-
code_verifier: tokenParams.code_verifier?.substring(0, 10) + '... (length: ' + tokenParams.code_verifier?.length + ')',
|
|
32
|
-
});
|
|
33
|
-
console.log('[PKCE] Token endpoint:', process.env.authorityUri + '/connect/token');
|
|
34
|
-
console.log('[PKCE] redirect_uri:', tokenParams.redirect_uri);
|
|
35
|
-
|
|
36
|
-
let queryString = querystring.stringify(tokenParams);
|
|
37
|
-
|
|
38
|
-
let response;
|
|
39
|
-
try {
|
|
40
|
-
response = await axios.post(process.env.authorityUri + '/connect/token', queryString, {
|
|
41
|
-
headers: headers
|
|
42
|
-
});
|
|
43
|
-
console.log('[PKCE] Token response status:', response.status);
|
|
44
|
-
console.log('[PKCE] Token response has access_token:', !!response.data?.access_token);
|
|
45
|
-
console.log('[PKCE] Token response has refresh_token:', !!response.data?.refresh_token);
|
|
46
|
-
} catch (err) {
|
|
47
|
-
console.error('[PKCE] Token request FAILED');
|
|
48
|
-
console.error('[PKCE] Status:', err.response?.status);
|
|
49
|
-
console.error('[PKCE] Error data:', JSON.stringify(err.response?.data));
|
|
50
|
-
console.error('[PKCE] Error message:', err.message);
|
|
51
|
-
throw err;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let domainHost = window.location.hostname.split('.').slice(-2).join('.');
|
|
55
|
-
window.localStorage.removeItem("verifier");
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
await setCookie('access_token', response.data.access_token, {
|
|
60
|
-
maxAge: 60 * 60 * 24 * 365, // 1 year,
|
|
61
|
-
path: '/',
|
|
62
|
-
domain: domainHost,
|
|
63
|
-
secure: true
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
await setCookie('expires_in', response.data.expires_in, {
|
|
67
|
-
maxAge: 60 * 60 * 24 * 365, // 1 year,
|
|
68
|
-
path: '/',
|
|
69
|
-
domain: domainHost,
|
|
70
|
-
secure: true
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
await setCookie('refresh_token', response.data.refresh_token, {
|
|
74
|
-
maxAge: 60 * 60 * 24 * 365, // 1 year,
|
|
75
|
-
path: '/',
|
|
76
|
-
domain: domainHost,
|
|
77
|
-
secure: true
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// await setCookie(null, "access_token", response.data.access_token,
|
|
82
|
-
// {
|
|
83
|
-
// maxAge: 2147483647,
|
|
84
|
-
// path: '/',
|
|
85
|
-
// domain: domainHost,
|
|
86
|
-
// secure: true
|
|
87
|
-
// });
|
|
88
|
-
|
|
89
|
-
// await setCookie(null, "expires_in", response.data.expires_in,
|
|
90
|
-
// {
|
|
91
|
-
// maxAge: 2147483647,
|
|
92
|
-
// path: '/',
|
|
93
|
-
// domain: domainHost,
|
|
94
|
-
// secure: true
|
|
95
|
-
// });
|
|
96
|
-
|
|
97
|
-
// await setCookie(null, "refresh_token", response.data.refresh_token,
|
|
98
|
-
// {
|
|
99
|
-
// maxAge: 2147483647,
|
|
100
|
-
// path: '/',
|
|
101
|
-
// domain: domainHost,
|
|
102
|
-
// secure: true
|
|
103
|
-
// });
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
let redirectUri = localStorage.getItem("redirectUri")
|
|
107
|
-
localStorage.clear();
|
|
108
|
-
if (redirectUri != null)
|
|
109
|
-
{
|
|
110
|
-
window.location.href = redirectUri;
|
|
111
|
-
}
|
|
112
|
-
else
|
|
113
|
-
{
|
|
114
|
-
window.location.href = "/";
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|