authscape 1.0.768 → 1.0.773

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "authscape",
3
- "version": "1.0.768",
3
+ "version": "1.0.773",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -27,11 +27,13 @@
27
27
  "devDependencies": {
28
28
  "@babel/cli": "^7.27.0",
29
29
  "@babel/core": "^7.26.10",
30
+ "@babel/plugin-transform-runtime": "^7.29.0",
30
31
  "@babel/preset-env": "^7.26.9",
31
32
  "@babel/preset-react": "^7.26.3",
32
33
  "react-dom": "^18.3.1"
33
34
  },
34
35
  "dependencies": {
36
+ "@babel/runtime": "^7.29.2",
35
37
  "@lexical/code": "^0.21.0",
36
38
  "@lexical/html": "^0.21.0",
37
39
  "@lexical/link": "^0.21.0",
@@ -40,7 +42,6 @@
40
42
  "@lexical/rich-text": "^0.21.0",
41
43
  "@lexical/selection": "^0.21.0",
42
44
  "@microsoft/signalr": "^8.0.0",
43
- "lexical": "^0.21.0",
44
45
  "audit": "^0.0.6",
45
46
  "axios": "^1.6.1",
46
47
  "eslint-config-next": "^16.0.3",
@@ -50,6 +51,7 @@
50
51
  "js-cookie": "^3.0.5",
51
52
  "js-file-download": "^0.4.12",
52
53
  "jspdf": "^4.0.0",
54
+ "lexical": "^0.21.0",
53
55
  "query-string": "^7.1.1",
54
56
  "react": "^18.2.0",
55
57
  "react-color": "^2.19.3",
@@ -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 { useSearchParams, usePathname } from "next/navigation";
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";
@@ -501,9 +501,22 @@ export function AuthScapeApp({
501
501
  const errorTrackingInitializedRef = useRef(false);
502
502
  const loginRedirectPending = useRef(false);
503
503
 
504
- const searchParams = useSearchParams();
505
- const queryCode = searchParams?.get("code") ?? null;
506
- const pathname = usePathname();
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];
507
520
 
508
521
  const signInValidator = async (codeFromQuery) => {
509
522
  if (queryCodeUsed.current === codeFromQuery) return;
@@ -547,19 +560,19 @@ export function AuthScapeApp({
547
560
  expires: 365,
548
561
  path: "/",
549
562
  domain: domainHost,
550
- secure: true,
563
+ secure: (typeof window !== "undefined" && window.location.protocol === "https:"),
551
564
  });
552
565
  Cookies.set("expires_in", String(response.data.expires_in), {
553
566
  expires: 365,
554
567
  path: "/",
555
568
  domain: domainHost,
556
- secure: true,
569
+ secure: (typeof window !== "undefined" && window.location.protocol === "https:"),
557
570
  });
558
571
  Cookies.set("refresh_token", response.data.refresh_token, {
559
572
  expires: 365,
560
573
  path: "/",
561
574
  domain: domainHost,
562
- secure: true,
575
+ secure: (typeof window !== "undefined" && window.location.protocol === "https:"),
563
576
  });
564
577
 
565
578
  resetRedirectCounter();
@@ -567,7 +580,43 @@ export function AuthScapeApp({
567
580
  const redirectUri = window.localStorage.getItem("redirectUri") || "/";
568
581
  window.localStorage.clear();
569
582
 
570
- window.location.href = redirectUri;
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
+ }
571
620
  } catch (exp) {
572
621
  console.error("PKCE sign-in failed", exp);
573
622
  window.localStorage.clear();
@@ -805,3 +854,8 @@ export function AuthScapeApp({
805
854
  </>
806
855
  );
807
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;
@@ -58,21 +58,21 @@ const RefreshToken = async (originalRequest, instance) => {
58
58
  expires: 365,
59
59
  path: '/',
60
60
  domain: domainHost,
61
- secure: true
61
+ secure: (typeof window !== "undefined" && window.location.protocol === "https:")
62
62
  });
63
63
 
64
64
  Cookies.set('expires_in', String(response.data.expires_in), {
65
65
  expires: 365,
66
66
  path: '/',
67
67
  domain: domainHost,
68
- secure: true
68
+ secure: (typeof window !== "undefined" && window.location.protocol === "https:")
69
69
  });
70
70
 
71
71
  Cookies.set('refresh_token', response.data.refresh_token, {
72
72
  expires: 365,
73
73
  path: '/',
74
74
  domain: domainHost,
75
- secure: true
75
+ secure: (typeof window !== "undefined" && window.location.protocol === "https:")
76
76
  });
77
77
 
78
78
  return true;
@@ -116,9 +116,9 @@ export const apiService = (ctx = null) => {
116
116
  if (error.response.status === 400) {
117
117
  if (error.response.config.url.includes("/connect/token")) {
118
118
  let domainHost = window.location.hostname.split('.').slice(-2).join('.');
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 });
119
+ Cookies.remove('access_token', { path: '/', domain: domainHost, secure: (typeof window !== "undefined" && window.location.protocol === "https:") });
120
+ Cookies.remove('refresh_token', { path: '/', domain: domainHost, secure: (typeof window !== "undefined" && window.location.protocol === "https:") });
121
+ Cookies.remove('expires_in', { path: '/', domain: domainHost, secure: (typeof window !== "undefined" && window.location.protocol === "https:") });
122
122
  }
123
123
  return Promise.reject(error);
124
124
  }
@@ -5,7 +5,7 @@ export const authService = () => {
5
5
  return {
6
6
 
7
7
  dec2hex: (dec) => {
8
- return ('0' + dec.toString(16)).substr(-2)
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, dnsRecord = null, deviceId = 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: (typeof window !== "undefined" && window.location.protocol === "https:") });
120
+ Cookies.remove('refresh_token', { path: '/', domain: domainHost, secure: (typeof window !== "undefined" && window.location.protocol === "https:") });
121
+ Cookies.remove('expires_in', { path: '/', domain: domainHost, secure: (typeof window !== "undefined" && window.location.protocol === "https:") });
129
122
 
130
- Cookies.remove('access_token', { path: '/', domain: domainHost, secure: true });
131
- Cookies.remove('refresh_token', { path: '/', domain: domainHost, secure: true });
132
- Cookies.remove('expires_in', { path: '/', domain: domainHost, secure: true });
133
-
134
-
135
- // destroyCookie({}, "access_token", {
136
- // maxAge: 2147483647,
137
- // path: '/',
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
- }