@workos-inc/authkit-nextjs 2.4.2 → 2.4.4

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 CHANGED
@@ -102,12 +102,20 @@ export const GET = handleAuth({
102
102
  });
103
103
  ```
104
104
 
105
+ When running in environments like Docker, set the `baseURL` explicitly to ensure the redirects point to the correct location.
106
+
107
+ ```ts
108
+ export const GET = handleAuth({
109
+ baseURL: 'http://localhost:3000',
110
+ });
111
+ ```
112
+
105
113
  `handleAuth` can be used with the following options.
106
114
 
107
115
  | Option | Default | Description |
108
116
  | ---------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
109
117
  | `returnPathname` | `/` | The pathname to redirect the user to after signing in |
110
- | `baseURL` | `undefined` | The base URL to use for the redirect URI instead of the one in the request. Useful if the app is being run in a container like docker where the hostname can be different from the one in the request |
118
+ | `baseURL` | `undefined` | The base URL to use for the redirect URI instead of the one in the request. **Required** if the app is being run in a container like docker where the hostname can be different from the one in the request |
111
119
  | `onSuccess` | `undefined` | A function that receives successful authentication data and can be used for side-effects like persisting tokens |
112
120
  | `onError` | `undefined` | A function that can receive the error and the request and handle the error in its own way. |
113
121
 
@@ -429,7 +437,7 @@ If you don't want to use `authkitMiddleware` and instead want to compose your ow
429
437
  export default async function middleware(request: NextRequest) {
430
438
  // Perform logic before or after AuthKit
431
439
 
432
- // Auth object contains the session, response headers and an auhorization URL in the case that the session isn't valid
440
+ // Auth object contains the session, response headers and an authorization URL in the case that the session isn't valid
433
441
  // This method will automatically handle setting the cookie and refreshing the session
434
442
  const { session, headers, authorizationUrl } = await authkit(request, {
435
443
  debug: true,
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import React, { createContext, useContext, useEffect, useState } from 'react';
2
+ import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';
3
3
  import { checkSessionAction, getAuthAction, handleSignOutAction, refreshAuthAction, switchToOrganizationAction, } from '../actions.js';
4
4
  const AuthContext = createContext(undefined);
5
5
  export const AuthKitProvider = ({ children, onSessionExpired }) => {
@@ -12,7 +12,7 @@ export const AuthKitProvider = ({ children, onSessionExpired }) => {
12
12
  const [featureFlags, setFeatureFlags] = useState(undefined);
13
13
  const [impersonator, setImpersonator] = useState(undefined);
14
14
  const [loading, setLoading] = useState(true);
15
- const getAuth = async ({ ensureSignedIn = false } = {}) => {
15
+ const getAuth = useCallback(async ({ ensureSignedIn = false } = {}) => {
16
16
  setLoading(true);
17
17
  try {
18
18
  const auth = await getAuthAction({ ensureSignedIn });
@@ -38,8 +38,8 @@ export const AuthKitProvider = ({ children, onSessionExpired }) => {
38
38
  finally {
39
39
  setLoading(false);
40
40
  }
41
- };
42
- const switchToOrganization = async (organizationId, options = {}) => {
41
+ }, []);
42
+ const switchToOrganization = useCallback(async (organizationId, options = {}) => {
43
43
  const opts = { revalidationStrategy: 'none', ...options };
44
44
  const result = await switchToOrganizationAction(organizationId, {
45
45
  revalidationStrategy: 'none',
@@ -49,8 +49,8 @@ export const AuthKitProvider = ({ children, onSessionExpired }) => {
49
49
  await getAuth({ ensureSignedIn: true });
50
50
  }
51
51
  return result;
52
- };
53
- const refreshAuth = async ({ ensureSignedIn = false, organizationId, } = {}) => {
52
+ }, []);
53
+ const refreshAuth = useCallback(async ({ ensureSignedIn = false, organizationId } = {}) => {
54
54
  try {
55
55
  setLoading(true);
56
56
  const auth = await refreshAuthAction({ ensureSignedIn, organizationId });
@@ -69,10 +69,10 @@ export const AuthKitProvider = ({ children, onSessionExpired }) => {
69
69
  finally {
70
70
  setLoading(false);
71
71
  }
72
- };
73
- const signOut = async ({ returnTo } = {}) => {
72
+ }, []);
73
+ const signOut = useCallback(async ({ returnTo } = {}) => {
74
74
  await handleSignOutAction({ returnTo });
75
- };
75
+ }, []);
76
76
  useEffect(() => {
77
77
  getAuth();
78
78
  // Return early if the session expired checks are disabled.
@@ -1 +1 @@
1
- {"version":3,"file":"authkit-provider.js","sourceRoot":"","sources":["../../../src/components/authkit-provider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"authkit-provider.js","sourceRoot":"","sources":["../../../src/components/authkit-provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,EAAE,EAAE,aAAa,EAAa,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACtG,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AAuBvB,MAAM,WAAW,GAAG,aAAa,CAA8B,SAAS,CAAC,CAAC;AAW1E,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAAE,QAAQ,EAAE,gBAAgB,EAAwB,EAAE,EAAE;IACtF,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAc,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAqB,SAAS,CAAC,CAAC;IAC1E,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAqB,SAAS,CAAC,CAAC;IACpF,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAqB,SAAS,CAAC,CAAC;IAChE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAuB,SAAS,CAAC,CAAC;IAChF,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAuB,SAAS,CAAC,CAAC;IAClF,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAuB,SAAS,CAAC,CAAC;IAClF,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAA2B,SAAS,CAAC,CAAC;IACtF,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE7C,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,EAAE,cAAc,GAAG,KAAK,KAAmC,EAAE,EAAE,EAAE;QAClG,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;YACrD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACvC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACjC,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACnC,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACnC,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAC7B,OAAO,CAAC,SAAS,CAAC,CAAC;YACnB,cAAc,CAAC,SAAS,CAAC,CAAC;YAC1B,eAAe,CAAC,SAAS,CAAC,CAAC;YAC3B,eAAe,CAAC,SAAS,CAAC,CAAC;YAC3B,eAAe,CAAC,SAAS,CAAC,CAAC;QAC7B,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,oBAAoB,GAAG,WAAW,CACtC,KAAK,EAAE,cAAsB,EAAE,UAAuC,EAAE,EAAE,EAAE;QAC1E,MAAM,IAAI,GAAG,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,cAAc,EAAE;YAC9D,oBAAoB,EAAE,MAAM;YAC5B,GAAG,OAAO;SACX,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,oBAAoB,KAAK,MAAM,EAAE,CAAC;YACzC,MAAM,OAAO,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,WAAW,GAAG,WAAW,CAC7B,KAAK,EAAE,EAAE,cAAc,GAAG,KAAK,EAAE,cAAc,KAA4D,EAAE,EAAE,EAAE;QAC/G,IAAI,CAAC;YACH,UAAU,CAAC,IAAI,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC,CAAC;YAEzE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACvC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACjC,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACnC,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACnC,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACtF,CAAC;gBAAS,CAAC;YACT,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,EAAE,QAAQ,KAA4B,EAAE,EAAE,EAAE;QAC7E,MAAM,mBAAmB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,EAAE,CAAC;QAEV,2DAA2D;QAC3D,IAAI,gBAAgB,KAAK,KAAK,EAAE,CAAC;YAC/B,OAAO;QACT,CAAC;QAED,IAAI,uBAAuB,GAAG,KAAK,CAAC;QAEpC,MAAM,sBAAsB,GAAG,KAAK,IAAI,EAAE;YACxC,IAAI,uBAAuB,EAAE,CAAC;gBAC5B,OAAO;YACT,CAAC;YAED,oGAAoG;YACpG,qFAAqF;YACrF,oGAAoG;YACpG,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC3C,uBAAuB,GAAG,IAAI,CAAC;gBAE/B,IAAI,CAAC;oBACH,MAAM,UAAU,GAAG,MAAM,kBAAkB,EAAE,CAAC;oBAC9C,IAAI,CAAC,UAAU,EAAE,CAAC;wBAChB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;oBACrC,CAAC;gBACH,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,wEAAwE;oBACxE,+EAA+E;oBAC/E,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;wBACxE,IAAI,gBAAgB,EAAE,CAAC;4BACrB,gBAAgB,EAAE,CAAC;wBACrB,CAAC;6BAAM,CAAC;4BACN,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;wBAC3B,CAAC;oBACH,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACT,uBAAuB,GAAG,KAAK,CAAC;gBAClC,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;QACpE,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;QAEzD,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;YAC5D,MAAM,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;QACzE,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAEvB,OAAO,CACL,oBAAC,WAAW,CAAC,QAAQ,IACnB,KAAK,EAAE;YACL,IAAI;YACJ,SAAS;YACT,cAAc;YACd,IAAI;YACJ,WAAW;YACX,YAAY;YACZ,YAAY;YACZ,YAAY;YACZ,OAAO;YACP,OAAO;YACP,WAAW;YACX,OAAO;YACP,oBAAoB;SACrB,IAEA,QAAQ,CACY,CACxB,CAAC;AACJ,CAAC,CAAC;AAMF,MAAM,UAAU,OAAO,CAAC,EAAE,cAAc,GAAG,KAAK,KAAmC,EAAE;IACnF,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IAExC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,IAAI,cAAc,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACnE,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,EAAE,CAAC,cAAc,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,CAAC,CAAC,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useReducer, useRef } from 'react';
2
2
  import { getAccessTokenAction, refreshAccessTokenAction } from '../actions.js';
3
3
  import { useAuth } from './authkit-provider.js';
4
+ import { decodeJwt } from '../jwt.js';
4
5
  const TOKEN_EXPIRY_BUFFER_SECONDS = 60;
5
6
  const MIN_REFRESH_DELAY_SECONDS = 15; // minimum delay before refreshing token
6
7
  const MAX_REFRESH_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
@@ -10,7 +11,7 @@ function tokenReducer(state, action) {
10
11
  case 'FETCH_START':
11
12
  return { ...state, loading: true, error: null };
12
13
  case 'FETCH_SUCCESS':
13
- return { ...state, loading: false, token: action.token };
14
+ return { ...state, loading: false, token: action.token, error: null };
14
15
  case 'FETCH_ERROR':
15
16
  return { ...state, loading: false, error: action.error };
16
17
  case 'RESET':
@@ -24,18 +25,18 @@ function getRefreshDelay(timeUntilExpiry) {
24
25
  const idealDelay = (timeUntilExpiry - TOKEN_EXPIRY_BUFFER_SECONDS) * 1000;
25
26
  return Math.min(Math.max(idealDelay, MIN_REFRESH_DELAY_SECONDS * 1000), MAX_REFRESH_DELAY_SECONDS * 1000);
26
27
  }
27
- function parseToken(token) {
28
+ function parseTokenPayload(token) {
28
29
  // istanbul ignore next
29
30
  if (!token) {
30
31
  return null;
31
32
  }
32
33
  try {
33
- const parts = token.split('.');
34
- if (parts.length !== 3) {
34
+ const { payload } = decodeJwt(token);
35
+ const now = Math.floor(Date.now() / 1000);
36
+ // istanbul ignore next - if the token does not have an exp claim, we cannot determine expiry
37
+ if (typeof payload.exp !== 'number') {
35
38
  return null;
36
39
  }
37
- const payload = JSON.parse(atob(parts[1]));
38
- const now = Math.floor(Date.now() / 1000);
39
40
  return {
40
41
  payload,
41
42
  expiresAt: payload.exp,
@@ -67,40 +68,57 @@ export function useAccessToken() {
67
68
  refreshTimeoutRef.current = undefined;
68
69
  }
69
70
  }, []);
71
+ // Store the current token in a ref to avoid stale closures
72
+ const currentTokenRef = useRef(state.token);
73
+ currentTokenRef.current = state.token;
74
+ // Store updateToken in a ref to break circular dependency
75
+ const updateTokenRef = useRef();
76
+ // Centralized timer scheduling function
77
+ const scheduleNextRefresh = useCallback((delay) => {
78
+ clearRefreshTimeout();
79
+ refreshTimeoutRef.current = setTimeout(() => {
80
+ if (updateTokenRef.current) {
81
+ updateTokenRef.current();
82
+ }
83
+ }, delay);
84
+ }, [clearRefreshTimeout]);
70
85
  const updateToken = useCallback(async () => {
71
86
  // istanbul ignore next - safety guard against concurrent fetches
72
87
  if (fetchingRef.current) {
73
88
  return;
74
89
  }
75
90
  fetchingRef.current = true;
76
- dispatch({ type: 'FETCH_START' });
77
91
  try {
78
92
  let token = await getAccessTokenAction();
79
93
  if (token) {
80
- const tokenData = parseToken(token);
94
+ const tokenData = parseTokenPayload(token);
81
95
  if (!tokenData || tokenData.isExpiring) {
82
96
  token = await refreshAccessTokenAction();
83
97
  }
84
98
  }
85
- dispatch({ type: 'FETCH_SUCCESS', token });
99
+ // Only update state if token has changed
100
+ if (token !== currentTokenRef.current) {
101
+ dispatch({ type: 'FETCH_SUCCESS', token });
102
+ }
86
103
  if (token) {
87
- const tokenData = parseToken(token);
104
+ const tokenData = parseTokenPayload(token);
88
105
  if (tokenData) {
89
106
  const delay = getRefreshDelay(tokenData.timeUntilExpiry);
90
- clearRefreshTimeout();
91
- refreshTimeoutRef.current = setTimeout(updateToken, delay);
107
+ scheduleNextRefresh(delay);
92
108
  }
93
109
  }
94
110
  return token;
95
111
  }
96
112
  catch (error) {
97
113
  dispatch({ type: 'FETCH_ERROR', error: error instanceof Error ? error : new Error(String(error)) });
98
- refreshTimeoutRef.current = setTimeout(updateToken, RETRY_DELAY_SECONDS * 1000);
114
+ scheduleNextRefresh(RETRY_DELAY_SECONDS * 1000);
99
115
  }
100
116
  finally {
101
117
  fetchingRef.current = false;
102
118
  }
103
- }, [clearRefreshTimeout]);
119
+ }, [scheduleNextRefresh]);
120
+ // Assign updateToken to ref for use in scheduleNextRefresh
121
+ updateTokenRef.current = updateToken;
104
122
  const refresh = useCallback(async () => {
105
123
  if (fetchingRef.current) {
106
124
  return;
@@ -112,11 +130,10 @@ export function useAccessToken() {
112
130
  const token = await getAccessTokenAction();
113
131
  dispatch({ type: 'FETCH_SUCCESS', token });
114
132
  if (token) {
115
- const tokenData = parseToken(token);
133
+ const tokenData = parseTokenPayload(token);
116
134
  if (tokenData) {
117
135
  const delay = getRefreshDelay(tokenData.timeUntilExpiry);
118
- clearRefreshTimeout();
119
- refreshTimeoutRef.current = setTimeout(updateToken, delay);
136
+ scheduleNextRefresh(delay);
120
137
  }
121
138
  }
122
139
  return token;
@@ -124,12 +141,12 @@ export function useAccessToken() {
124
141
  catch (error) {
125
142
  const typedError = error instanceof Error ? error : new Error(String(error));
126
143
  dispatch({ type: 'FETCH_ERROR', error: typedError });
127
- refreshTimeoutRef.current = setTimeout(updateToken, RETRY_DELAY_SECONDS * 1000);
144
+ scheduleNextRefresh(RETRY_DELAY_SECONDS * 1000);
128
145
  }
129
146
  finally {
130
147
  fetchingRef.current = false;
131
148
  }
132
- }, [refreshAuth, clearRefreshTimeout, updateToken]);
149
+ }, [refreshAuth, scheduleNextRefresh, updateToken]);
133
150
  useEffect(() => {
134
151
  if (!user) {
135
152
  dispatch({ type: 'RESET' });
@@ -138,7 +155,7 @@ export function useAccessToken() {
138
155
  }
139
156
  updateToken();
140
157
  return clearRefreshTimeout;
141
- }, [userId, sessionId, updateToken, clearRefreshTimeout]);
158
+ }, [userId, sessionId, clearRefreshTimeout]);
142
159
  return {
143
160
  accessToken: state.token,
144
161
  loading: state.loading,
@@ -1 +1 @@
1
- {"version":3,"file":"useAccessToken.js","sourceRoot":"","sources":["../../../src/components/useAccessToken.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useAccessToken.js","sourceRoot":"","sources":["../../../src/components/useAccessToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AACvC,MAAM,yBAAyB,GAAG,EAAE,CAAC,CAAC,wCAAwC;AAC9E,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,WAAW;AAC3D,MAAM,mBAAmB,GAAG,GAAG,CAAC,CAAC,YAAY;AAc7C,SAAS,YAAY,CAAC,KAAiB,EAAE,MAAmB;IAC1D,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,aAAa;YAChB,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAClD,KAAK,eAAe;YAClB,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACxE,KAAK,aAAa;YAChB,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QAC3D,KAAK,OAAO;YACV,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACrE,uBAAuB;QACvB;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,eAAuB;IAC9C,MAAM,UAAU,GAAG,CAAC,eAAe,GAAG,2BAA2B,CAAC,GAAG,IAAI,CAAC;IAC1E,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,yBAAyB,GAAG,IAAI,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC,CAAC;AAC5G,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,uBAAuB;IACvB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAE1C,6FAA6F;QAC7F,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO;YACL,OAAO;YACP,SAAS,EAAE,OAAO,CAAC,GAAG;YACtB,UAAU,EAAE,OAAO,CAAC,GAAG,GAAG,GAAG,GAAG,2BAA2B;YAC3D,eAAe,EAAE,OAAO,CAAC,GAAG,GAAG,GAAG;SACnC,CAAC;IACJ,CAAC;IAAC,WAAM,CAAC;QACP,uBAAuB;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,EAAE,CAAC;IACxB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,YAAY,EAAE;QACjD,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,MAAM,iBAAiB,GAAG,MAAM,EAAiC,CAAC;IAClE,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAElC,MAAM,mBAAmB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC3C,IAAI,iBAAiB,CAAC,OAAO,EAAE,CAAC;YAC9B,YAAY,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YACxC,iBAAiB,CAAC,OAAO,GAAG,SAAS,CAAC;QACxC,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,2DAA2D;IAC3D,MAAM,eAAe,GAAG,MAAM,CAAqB,KAAK,CAAC,KAAK,CAAC,CAAC;IAChE,eAAe,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC;IAEtC,0DAA0D;IAC1D,MAAM,cAAc,GAAG,MAAM,EAAqC,CAAC;IAEnE,wCAAwC;IACxC,MAAM,mBAAmB,GAAG,WAAW,CACrC,CAAC,KAAa,EAAE,EAAE;QAChB,mBAAmB,EAAE,CAAC;QACtB,iBAAiB,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1C,IAAI,cAAc,CAAC,OAAO,EAAE,CAAC;gBAC3B,cAAc,CAAC,OAAO,EAAE,CAAC;YAC3B,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC,EACD,CAAC,mBAAmB,CAAC,CACtB,CAAC;IAEF,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACzC,iEAAiE;QACjE,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QAED,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;QAE3B,IAAI,CAAC;YACH,IAAI,KAAK,GAAG,MAAM,oBAAoB,EAAE,CAAC;YACzC,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;gBAC3C,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;oBACvC,KAAK,GAAG,MAAM,wBAAwB,EAAE,CAAC;gBAC3C,CAAC;YACH,CAAC;YAED,yCAAyC;YACzC,IAAI,KAAK,KAAK,eAAe,CAAC,OAAO,EAAE,CAAC;gBACtC,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7C,CAAC;YAED,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;gBAC3C,IAAI,SAAS,EAAE,CAAC;oBACd,MAAM,KAAK,GAAG,eAAe,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;oBACzD,mBAAmB,CAAC,KAAK,CAAC,CAAC;gBAC7B,CAAC;YACH,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YACpG,mBAAmB,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;QAClD,CAAC;gBAAS,CAAC;YACT,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;QAC9B,CAAC;IACH,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAE1B,2DAA2D;IAC3D,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC;IAErC,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QAED,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;QAC3B,QAAQ,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QAElC,IAAI,CAAC;YACH,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,MAAM,oBAAoB,EAAE,CAAC;YAE3C,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;YAE3C,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;gBAC3C,IAAI,SAAS,EAAE,CAAC;oBACd,MAAM,KAAK,GAAG,eAAe,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;oBACzD,mBAAmB,CAAC,KAAK,CAAC,CAAC;gBAC7B,CAAC;YACH,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC7E,QAAQ,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;YACrD,mBAAmB,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;QAClD,CAAC;gBAAS,CAAC;YACT,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;QAC9B,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,EAAE,mBAAmB,EAAE,WAAW,CAAC,CAAC,CAAC;IAEpD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YAC5B,mBAAmB,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QACD,WAAW,EAAE,CAAC;QAEd,OAAO,mBAAmB,CAAC;IAC7B,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAE7C,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,KAAK;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { useMemo } from 'react';
2
2
  import { useAccessToken } from './useAccessToken.js';
3
- import { decodeJwt } from 'jose';
3
+ import { decodeJwt } from '../jwt.js';
4
4
  /**
5
5
  * A hook that retrieves the claims from the access token.
6
6
  *
@@ -17,7 +17,7 @@ export function useTokenClaims() {
17
17
  return {};
18
18
  }
19
19
  try {
20
- return decodeJwt(accessToken);
20
+ return decodeJwt(accessToken).payload;
21
21
  }
22
22
  catch (_a) {
23
23
  return {};
@@ -1 +1 @@
1
- {"version":3,"file":"useTokenClaims.js","sourceRoot":"","sources":["../../../src/components/useTokenClaims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAmB,MAAM,MAAM,CAAC;AAIlD;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,WAAW,EAAE,GAAG,cAAc,EAAE,CAAC;IAEzC,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC;YACH,OAAO,SAAS,CAAI,WAAW,CAAC,CAAC;QACnC,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"useTokenClaims.js","sourceRoot":"","sources":["../../../src/components/useTokenClaims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAoB,MAAM,WAAW,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,WAAW,EAAE,GAAG,cAAc,EAAE,CAAC;IAEzC,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC;YACH,OAAO,SAAS,CAAI,WAAW,CAAC,CAAC,OAAO,CAAC;QAC3C,CAAC;QAAC,WAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Decodes a base64url encoded string
3
+ * @param input The base64url string to decode
4
+ * @returns The decoded string
5
+ */
6
+ function decodeBase64Url(input) {
7
+ const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
8
+ const padding = '='.repeat((4 - (base64.length % 4)) % 4);
9
+ return atob(base64 + padding);
10
+ }
11
+ /**
12
+ * Decodes a JWT token and returns its header and payload
13
+ * @param token The JWT token to decode
14
+ * @return An object containing the decoded header and payload
15
+ * @throws Error if the token is not in a valid JWT format or if decoding fails
16
+ */
17
+ // should replace this with jose if we ever need to verify the JWT
18
+ export function decodeJwt(token) {
19
+ const parts = token.split('.');
20
+ if (parts.length !== 3) {
21
+ throw new Error('Invalid JWT format');
22
+ }
23
+ try {
24
+ const header = JSON.parse(decodeBase64Url(parts[0]));
25
+ const payload = JSON.parse(decodeBase64Url(parts[1]));
26
+ return { header, payload };
27
+ }
28
+ catch (error) {
29
+ throw new Error(`Failed to decode JWT: ${error instanceof Error ? error.message : String(error)}`);
30
+ }
31
+ }
32
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/jwt.ts"],"names":[],"mappings":"AAmEA;;;;GAIG;AACH,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,kEAAkE;AAClE,MAAM,UAAU,SAAS,CACvB,KAAa;IAKb,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAc,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAmB,CAAC;QAExE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrG,CAAC;AACH,CAAC"}
@@ -1,5 +1,4 @@
1
- import { type JWTPayload } from 'jose';
2
- type TokenClaims<T> = Partial<JWTPayload & T>;
1
+ import { type TokenClaims } from '../jwt.js';
3
2
  /**
4
3
  * A hook that retrieves the claims from the access token.
5
4
  *
@@ -10,4 +9,3 @@ type TokenClaims<T> = Partial<JWTPayload & T>;
10
9
  * @returns The claims from the access token, or an empty object if the token is not available or cannot be parsed.
11
10
  */
12
11
  export declare function useTokenClaims<T = Record<string, unknown>>(): TokenClaims<T>;
13
- export {};
@@ -0,0 +1,71 @@
1
+ /**
2
+ * JWT (JSON Web Token) Interface Definitions
3
+ */
4
+ export interface JWTHeader {
5
+ 'alg': string;
6
+ 'typ'?: string | undefined;
7
+ 'cty'?: string | undefined;
8
+ 'crit'?: Array<string | Exclude<keyof JWTHeader, 'crit'>> | undefined;
9
+ 'kid'?: string | undefined;
10
+ 'jku'?: string | undefined;
11
+ 'x5u'?: string | string[] | undefined;
12
+ 'x5t#S256'?: string | undefined;
13
+ 'x5t'?: string | undefined;
14
+ 'x5c'?: string | string[] | undefined;
15
+ }
16
+ /**
17
+ * JWT Payload Interface
18
+ */
19
+ export interface JWTPayload {
20
+ /**
21
+ * Session ID of the JWT, used to identify the session
22
+ */
23
+ sid: string;
24
+ /**
25
+ * Issuer of the JWT
26
+ */
27
+ iss: string;
28
+ /**
29
+ * Subject of the JWT
30
+ */
31
+ sub: string;
32
+ /**
33
+ * Audience of the JWT, can be a single string or an array of strings
34
+ */
35
+ aud?: string | string[];
36
+ /**
37
+ * Expiration time of the JWT, represented as a Unix timestamp
38
+ */
39
+ exp: number;
40
+ /**
41
+ * Issued at time of the JWT, represented as a Unix timestamp
42
+ */
43
+ iat: number;
44
+ /**
45
+ * JWT ID, a unique identifier for the JWT
46
+ */
47
+ jti: string;
48
+ /**
49
+ * Organization ID associated with the JWT
50
+ */
51
+ org_id?: string;
52
+ /**
53
+ * Role of the user associated with the JWT
54
+ */
55
+ role?: string;
56
+ /**
57
+ * Permissions granted to the user associated with the JWT
58
+ */
59
+ permissions?: string[];
60
+ }
61
+ export type TokenClaims<T> = Partial<JWTPayload & T>;
62
+ /**
63
+ * Decodes a JWT token and returns its header and payload
64
+ * @param token The JWT token to decode
65
+ * @return An object containing the decoded header and payload
66
+ * @throws Error if the token is not in a valid JWT format or if decoding fails
67
+ */
68
+ export declare function decodeJwt<T = Record<string, unknown>>(token: string): {
69
+ header: JWTHeader;
70
+ payload: TokenClaims<T>;
71
+ };
@@ -1,5 +1,5 @@
1
1
  import { WorkOS } from '@workos-inc/node';
2
- export declare const VERSION = "2.4.2";
2
+ export declare const VERSION = "2.4.4";
3
3
  /**
4
4
  * Create a WorkOS instance with the provided API key and options.
5
5
  * If an instance already exists, it returns the existing instance.
@@ -1,7 +1,7 @@
1
1
  import { WorkOS } from '@workos-inc/node';
2
2
  import { WORKOS_API_HOSTNAME, WORKOS_API_KEY, WORKOS_API_HTTPS, WORKOS_API_PORT } from './env-variables.js';
3
3
  import { lazy } from './utils.js';
4
- export const VERSION = '2.4.2';
4
+ export const VERSION = '2.4.4';
5
5
  const options = {
6
6
  apiHostname: WORKOS_API_HOSTNAME,
7
7
  https: WORKOS_API_HTTPS ? WORKOS_API_HTTPS === 'true' : true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workos-inc/authkit-nextjs",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
4
4
  "description": "Authentication and session helpers for using WorkOS & AuthKit with Next.js",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import React, { createContext, ReactNode, useContext, useEffect, useState } from 'react';
3
+ import React, { createContext, ReactNode, useCallback, useContext, useEffect, useState } from 'react';
4
4
  import {
5
5
  checkSessionAction,
6
6
  getAuthAction,
@@ -52,7 +52,7 @@ export const AuthKitProvider = ({ children, onSessionExpired }: AuthKitProviderP
52
52
  const [impersonator, setImpersonator] = useState<Impersonator | undefined>(undefined);
53
53
  const [loading, setLoading] = useState(true);
54
54
 
55
- const getAuth = async ({ ensureSignedIn = false }: { ensureSignedIn?: boolean } = {}) => {
55
+ const getAuth = useCallback(async ({ ensureSignedIn = false }: { ensureSignedIn?: boolean } = {}) => {
56
56
  setLoading(true);
57
57
  try {
58
58
  const auth = await getAuthAction({ ensureSignedIn });
@@ -76,48 +76,51 @@ export const AuthKitProvider = ({ children, onSessionExpired }: AuthKitProviderP
76
76
  } finally {
77
77
  setLoading(false);
78
78
  }
79
- };
80
-
81
- const switchToOrganization = async (organizationId: string, options: SwitchToOrganizationOptions = {}) => {
82
- const opts = { revalidationStrategy: 'none', ...options };
83
- const result = await switchToOrganizationAction(organizationId, {
84
- revalidationStrategy: 'none',
85
- ...options,
86
- });
87
-
88
- if (opts.revalidationStrategy === 'none') {
89
- await getAuth({ ensureSignedIn: true });
90
- }
91
-
92
- return result;
93
- };
79
+ }, []);
80
+
81
+ const switchToOrganization = useCallback(
82
+ async (organizationId: string, options: SwitchToOrganizationOptions = {}) => {
83
+ const opts = { revalidationStrategy: 'none', ...options };
84
+ const result = await switchToOrganizationAction(organizationId, {
85
+ revalidationStrategy: 'none',
86
+ ...options,
87
+ });
88
+
89
+ if (opts.revalidationStrategy === 'none') {
90
+ await getAuth({ ensureSignedIn: true });
91
+ }
94
92
 
95
- const refreshAuth = async ({
96
- ensureSignedIn = false,
97
- organizationId,
98
- }: { ensureSignedIn?: boolean; organizationId?: string } = {}) => {
99
- try {
100
- setLoading(true);
101
- const auth = await refreshAuthAction({ ensureSignedIn, organizationId });
93
+ return result;
94
+ },
95
+ [],
96
+ );
102
97
 
103
- setUser(auth.user);
104
- setSessionId(auth.sessionId);
105
- setOrganizationId(auth.organizationId);
106
- setRole(auth.role);
107
- setPermissions(auth.permissions);
108
- setEntitlements(auth.entitlements);
109
- setFeatureFlags(auth.featureFlags);
110
- setImpersonator(auth.impersonator);
111
- } catch (error) {
112
- return error instanceof Error ? { error: error.message } : { error: String(error) };
113
- } finally {
114
- setLoading(false);
115
- }
116
- };
98
+ const refreshAuth = useCallback(
99
+ async ({ ensureSignedIn = false, organizationId }: { ensureSignedIn?: boolean; organizationId?: string } = {}) => {
100
+ try {
101
+ setLoading(true);
102
+ const auth = await refreshAuthAction({ ensureSignedIn, organizationId });
103
+
104
+ setUser(auth.user);
105
+ setSessionId(auth.sessionId);
106
+ setOrganizationId(auth.organizationId);
107
+ setRole(auth.role);
108
+ setPermissions(auth.permissions);
109
+ setEntitlements(auth.entitlements);
110
+ setFeatureFlags(auth.featureFlags);
111
+ setImpersonator(auth.impersonator);
112
+ } catch (error) {
113
+ return error instanceof Error ? { error: error.message } : { error: String(error) };
114
+ } finally {
115
+ setLoading(false);
116
+ }
117
+ },
118
+ [],
119
+ );
117
120
 
118
- const signOut = async ({ returnTo }: { returnTo?: string } = {}) => {
121
+ const signOut = useCallback(async ({ returnTo }: { returnTo?: string } = {}) => {
119
122
  await handleSignOutAction({ returnTo });
120
- };
123
+ }, []);
121
124
 
122
125
  useEffect(() => {
123
126
  getAuth();
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useReducer, useRef } from 'react';
2
2
  import { getAccessTokenAction, refreshAccessTokenAction } from '../actions.js';
3
3
  import { useAuth } from './authkit-provider.js';
4
+ import { decodeJwt } from '../jwt.js';
4
5
 
5
6
  const TOKEN_EXPIRY_BUFFER_SECONDS = 60;
6
7
  const MIN_REFRESH_DELAY_SECONDS = 15; // minimum delay before refreshing token
@@ -24,7 +25,7 @@ function tokenReducer(state: TokenState, action: TokenAction): TokenState {
24
25
  case 'FETCH_START':
25
26
  return { ...state, loading: true, error: null };
26
27
  case 'FETCH_SUCCESS':
27
- return { ...state, loading: false, token: action.token };
28
+ return { ...state, loading: false, token: action.token, error: null };
28
29
  case 'FETCH_ERROR':
29
30
  return { ...state, loading: false, error: action.error };
30
31
  case 'RESET':
@@ -40,21 +41,21 @@ function getRefreshDelay(timeUntilExpiry: number) {
40
41
  return Math.min(Math.max(idealDelay, MIN_REFRESH_DELAY_SECONDS * 1000), MAX_REFRESH_DELAY_SECONDS * 1000);
41
42
  }
42
43
 
43
- function parseToken(token: string | undefined) {
44
+ function parseTokenPayload(token: string | undefined) {
44
45
  // istanbul ignore next
45
46
  if (!token) {
46
47
  return null;
47
48
  }
48
49
 
49
50
  try {
50
- const parts = token.split('.');
51
- if (parts.length !== 3) {
51
+ const { payload } = decodeJwt(token);
52
+ const now = Math.floor(Date.now() / 1000);
53
+
54
+ // istanbul ignore next - if the token does not have an exp claim, we cannot determine expiry
55
+ if (typeof payload.exp !== 'number') {
52
56
  return null;
53
57
  }
54
58
 
55
- const payload = JSON.parse(atob(parts[1]));
56
- const now = Math.floor(Date.now() / 1000);
57
-
58
59
  return {
59
60
  payload,
60
61
  expiresAt: payload.exp,
@@ -89,6 +90,26 @@ export function useAccessToken() {
89
90
  }
90
91
  }, []);
91
92
 
93
+ // Store the current token in a ref to avoid stale closures
94
+ const currentTokenRef = useRef<string | undefined>(state.token);
95
+ currentTokenRef.current = state.token;
96
+
97
+ // Store updateToken in a ref to break circular dependency
98
+ const updateTokenRef = useRef<() => Promise<string | undefined>>();
99
+
100
+ // Centralized timer scheduling function
101
+ const scheduleNextRefresh = useCallback(
102
+ (delay: number) => {
103
+ clearRefreshTimeout();
104
+ refreshTimeoutRef.current = setTimeout(() => {
105
+ if (updateTokenRef.current) {
106
+ updateTokenRef.current();
107
+ }
108
+ }, delay);
109
+ },
110
+ [clearRefreshTimeout],
111
+ );
112
+
92
113
  const updateToken = useCallback(async () => {
93
114
  // istanbul ignore next - safety guard against concurrent fetches
94
115
  if (fetchingRef.current) {
@@ -96,35 +117,40 @@ export function useAccessToken() {
96
117
  }
97
118
 
98
119
  fetchingRef.current = true;
99
- dispatch({ type: 'FETCH_START' });
120
+
100
121
  try {
101
122
  let token = await getAccessTokenAction();
102
123
  if (token) {
103
- const tokenData = parseToken(token);
124
+ const tokenData = parseTokenPayload(token);
104
125
  if (!tokenData || tokenData.isExpiring) {
105
126
  token = await refreshAccessTokenAction();
106
127
  }
107
128
  }
108
129
 
109
- dispatch({ type: 'FETCH_SUCCESS', token });
130
+ // Only update state if token has changed
131
+ if (token !== currentTokenRef.current) {
132
+ dispatch({ type: 'FETCH_SUCCESS', token });
133
+ }
110
134
 
111
135
  if (token) {
112
- const tokenData = parseToken(token);
136
+ const tokenData = parseTokenPayload(token);
113
137
  if (tokenData) {
114
138
  const delay = getRefreshDelay(tokenData.timeUntilExpiry);
115
- clearRefreshTimeout();
116
- refreshTimeoutRef.current = setTimeout(updateToken, delay);
139
+ scheduleNextRefresh(delay);
117
140
  }
118
141
  }
119
142
 
120
143
  return token;
121
144
  } catch (error) {
122
145
  dispatch({ type: 'FETCH_ERROR', error: error instanceof Error ? error : new Error(String(error)) });
123
- refreshTimeoutRef.current = setTimeout(updateToken, RETRY_DELAY_SECONDS * 1000);
146
+ scheduleNextRefresh(RETRY_DELAY_SECONDS * 1000);
124
147
  } finally {
125
148
  fetchingRef.current = false;
126
149
  }
127
- }, [clearRefreshTimeout]);
150
+ }, [scheduleNextRefresh]);
151
+
152
+ // Assign updateToken to ref for use in scheduleNextRefresh
153
+ updateTokenRef.current = updateToken;
128
154
 
129
155
  const refresh = useCallback(async () => {
130
156
  if (fetchingRef.current) {
@@ -141,11 +167,10 @@ export function useAccessToken() {
141
167
  dispatch({ type: 'FETCH_SUCCESS', token });
142
168
 
143
169
  if (token) {
144
- const tokenData = parseToken(token);
170
+ const tokenData = parseTokenPayload(token);
145
171
  if (tokenData) {
146
172
  const delay = getRefreshDelay(tokenData.timeUntilExpiry);
147
- clearRefreshTimeout();
148
- refreshTimeoutRef.current = setTimeout(updateToken, delay);
173
+ scheduleNextRefresh(delay);
149
174
  }
150
175
  }
151
176
 
@@ -153,11 +178,11 @@ export function useAccessToken() {
153
178
  } catch (error) {
154
179
  const typedError = error instanceof Error ? error : new Error(String(error));
155
180
  dispatch({ type: 'FETCH_ERROR', error: typedError });
156
- refreshTimeoutRef.current = setTimeout(updateToken, RETRY_DELAY_SECONDS * 1000);
181
+ scheduleNextRefresh(RETRY_DELAY_SECONDS * 1000);
157
182
  } finally {
158
183
  fetchingRef.current = false;
159
184
  }
160
- }, [refreshAuth, clearRefreshTimeout, updateToken]);
185
+ }, [refreshAuth, scheduleNextRefresh, updateToken]);
161
186
 
162
187
  useEffect(() => {
163
188
  if (!user) {
@@ -168,7 +193,7 @@ export function useAccessToken() {
168
193
  updateToken();
169
194
 
170
195
  return clearRefreshTimeout;
171
- }, [userId, sessionId, updateToken, clearRefreshTimeout]);
196
+ }, [userId, sessionId, clearRefreshTimeout]);
172
197
 
173
198
  return {
174
199
  accessToken: state.token,
@@ -1,8 +1,6 @@
1
1
  import { useMemo } from 'react';
2
2
  import { useAccessToken } from './useAccessToken.js';
3
- import { decodeJwt, type JWTPayload } from 'jose';
4
-
5
- type TokenClaims<T> = Partial<JWTPayload & T>;
3
+ import { decodeJwt, type TokenClaims } from '../jwt.js';
6
4
 
7
5
  /**
8
6
  * A hook that retrieves the claims from the access token.
@@ -22,7 +20,7 @@ export function useTokenClaims<T = Record<string, unknown>>(): TokenClaims<T> {
22
20
  }
23
21
 
24
22
  try {
25
- return decodeJwt<T>(accessToken);
23
+ return decodeJwt<T>(accessToken).payload;
26
24
  } catch {
27
25
  return {};
28
26
  }
package/src/jwt.ts ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * JWT (JSON Web Token) Interface Definitions
3
+ */
4
+ export interface JWTHeader {
5
+ 'alg': string;
6
+ 'typ'?: string | undefined;
7
+ 'cty'?: string | undefined;
8
+ 'crit'?: Array<string | Exclude<keyof JWTHeader, 'crit'>> | undefined;
9
+ 'kid'?: string | undefined;
10
+ 'jku'?: string | undefined;
11
+ 'x5u'?: string | string[] | undefined;
12
+ 'x5t#S256'?: string | undefined;
13
+ 'x5t'?: string | undefined;
14
+ 'x5c'?: string | string[] | undefined;
15
+ }
16
+ /**
17
+ * JWT Payload Interface
18
+ */
19
+ export interface JWTPayload {
20
+ /**
21
+ * Session ID of the JWT, used to identify the session
22
+ */
23
+ sid: string;
24
+
25
+ /**
26
+ * Issuer of the JWT
27
+ */
28
+ iss: string;
29
+ /**
30
+ * Subject of the JWT
31
+ */
32
+ sub: string;
33
+ /**
34
+ * Audience of the JWT, can be a single string or an array of strings
35
+ */
36
+ aud?: string | string[];
37
+ /**
38
+ * Expiration time of the JWT, represented as a Unix timestamp
39
+ */
40
+ exp: number;
41
+ /**
42
+ * Issued at time of the JWT, represented as a Unix timestamp
43
+ */
44
+ iat: number;
45
+ /**
46
+ * JWT ID, a unique identifier for the JWT
47
+ */
48
+ jti: string;
49
+
50
+ /**
51
+ * Organization ID associated with the JWT
52
+ */
53
+ org_id?: string;
54
+
55
+ /**
56
+ * Role of the user associated with the JWT
57
+ */
58
+ role?: string;
59
+
60
+ /**
61
+ * Permissions granted to the user associated with the JWT
62
+ */
63
+ permissions?: string[];
64
+ }
65
+
66
+ export type TokenClaims<T> = Partial<JWTPayload & T>;
67
+
68
+ /**
69
+ * Decodes a base64url encoded string
70
+ * @param input The base64url string to decode
71
+ * @returns The decoded string
72
+ */
73
+ function decodeBase64Url(input: string): string {
74
+ const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
75
+ const padding = '='.repeat((4 - (base64.length % 4)) % 4);
76
+ return atob(base64 + padding);
77
+ }
78
+
79
+ /**
80
+ * Decodes a JWT token and returns its header and payload
81
+ * @param token The JWT token to decode
82
+ * @return An object containing the decoded header and payload
83
+ * @throws Error if the token is not in a valid JWT format or if decoding fails
84
+ */
85
+ // should replace this with jose if we ever need to verify the JWT
86
+ export function decodeJwt<T = Record<string, unknown>>(
87
+ token: string,
88
+ ): {
89
+ header: JWTHeader;
90
+ payload: TokenClaims<T>;
91
+ } {
92
+ const parts = token.split('.');
93
+
94
+ if (parts.length !== 3) {
95
+ throw new Error('Invalid JWT format');
96
+ }
97
+
98
+ try {
99
+ const header = JSON.parse(decodeBase64Url(parts[0])) as JWTHeader;
100
+ const payload = JSON.parse(decodeBase64Url(parts[1])) as JWTPayload & T;
101
+
102
+ return { header, payload };
103
+ } catch (error) {
104
+ throw new Error(`Failed to decode JWT: ${error instanceof Error ? error.message : String(error)}`);
105
+ }
106
+ }
package/src/workos.ts CHANGED
@@ -2,7 +2,7 @@ import { WorkOS } from '@workos-inc/node';
2
2
  import { WORKOS_API_HOSTNAME, WORKOS_API_KEY, WORKOS_API_HTTPS, WORKOS_API_PORT } from './env-variables.js';
3
3
  import { lazy } from './utils.js';
4
4
 
5
- export const VERSION = '2.4.2';
5
+ export const VERSION = '2.4.4';
6
6
 
7
7
  const options = {
8
8
  apiHostname: WORKOS_API_HOSTNAME,