@workos-inc/authkit-nextjs 2.4.1 → 2.4.3
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 +13 -8
- package/dist/esm/components/authkit-provider.js +9 -9
- package/dist/esm/components/authkit-provider.js.map +1 -1
- package/dist/esm/components/useAccessToken.js +15 -9
- package/dist/esm/components/useAccessToken.js.map +1 -1
- package/dist/esm/components/useTokenClaims.js +2 -2
- package/dist/esm/components/useTokenClaims.js.map +1 -1
- package/dist/esm/jwt.js +32 -0
- package/dist/esm/jwt.js.map +1 -0
- package/dist/esm/types/components/useTokenClaims.d.ts +1 -3
- package/dist/esm/types/jwt.d.ts +71 -0
- package/dist/esm/types/workos.d.ts +1 -1
- package/dist/esm/workos.js +1 -1
- package/package.json +1 -1
- package/src/components/authkit-provider.tsx +43 -40
- package/src/components/useAccessToken.ts +16 -10
- package/src/components/useTokenClaims.ts +2 -4
- package/src/jwt.ts +106 -0
- package/src/workos.ts +1 -1
package/README.md
CHANGED
|
@@ -47,16 +47,21 @@ To use the `signOut` method, you'll need to set a default Logout URI in your Wor
|
|
|
47
47
|
|
|
48
48
|
Certain environment variables are optional and can be used to debug or configure cookie settings.
|
|
49
49
|
|
|
50
|
+
| Environment Variable | Default Value | Description |
|
|
51
|
+
|---------------------|---------------|-------------|
|
|
52
|
+
| `WORKOS_COOKIE_MAX_AGE` | `34560000` (400 days) | Maximum age of the cookie in seconds |
|
|
53
|
+
| `WORKOS_COOKIE_DOMAIN` | None | Domain for the cookie. When empty, the cookie is only valid for the current domain |
|
|
54
|
+
| `WORKOS_COOKIE_NAME` | `'wos-session'` | Name of the session cookie |
|
|
55
|
+
| `WORKOS_API_HOSTNAME` | `'api.workos.com'` | Base WorkOS API URL |
|
|
56
|
+
| `WORKOS_API_HTTPS` | `true` | Whether to use HTTPS in API calls |
|
|
57
|
+
| `WORKOS_API_PORT` | None | Port to use for API calls. When not set, uses standard ports (443 for HTTPS, 80 for HTTP) |
|
|
58
|
+
| `WORKOS_COOKIE_SAMESITE` | `'lax'` | SameSite attribute for cookies. Options: `'lax'`, `'strict'`, or `'none'` |
|
|
59
|
+
|
|
60
|
+
Example usage:
|
|
50
61
|
```sh
|
|
51
|
-
WORKOS_COOKIE_MAX_AGE='600'
|
|
62
|
+
WORKOS_COOKIE_MAX_AGE='600'
|
|
52
63
|
WORKOS_COOKIE_DOMAIN='example.com'
|
|
53
|
-
WORKOS_COOKIE_NAME='
|
|
54
|
-
WORKOS_API_HOSTNAME='api.workos.com' # base WorkOS API URL
|
|
55
|
-
WORKOS_API_HTTPS=true # whether to use HTTPS in API calls
|
|
56
|
-
WORKOS_API_PORT=3000 # port to use for API calls
|
|
57
|
-
|
|
58
|
-
# Only change this if you specifically need cross-origin cookie support.
|
|
59
|
-
WORKOS_COOKIE_SAMESITE='lax' # SameSite attribute for cookies: 'lax' (default), 'strict', or 'none'.
|
|
64
|
+
WORKOS_COOKIE_NAME='my-auth-cookie'
|
|
60
65
|
```
|
|
61
66
|
|
|
62
67
|
> [!WARNING]
|
|
@@ -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":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,EAAE,EAAE,aAAa,EAAa,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"authkit-provider.js","sourceRoot":"","sources":["../../../src/components/authkit-provider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
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
|
|
7
|
+
const MAX_REFRESH_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
|
|
6
8
|
const RETRY_DELAY_SECONDS = 300; // 5 minutes
|
|
7
9
|
function tokenReducer(state, action) {
|
|
8
10
|
switch (action.type) {
|
|
@@ -20,20 +22,21 @@ function tokenReducer(state, action) {
|
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
function getRefreshDelay(timeUntilExpiry) {
|
|
23
|
-
|
|
25
|
+
const idealDelay = (timeUntilExpiry - TOKEN_EXPIRY_BUFFER_SECONDS) * 1000;
|
|
26
|
+
return Math.min(Math.max(idealDelay, MIN_REFRESH_DELAY_SECONDS * 1000), MAX_REFRESH_DELAY_SECONDS * 1000);
|
|
24
27
|
}
|
|
25
|
-
function
|
|
28
|
+
function parseTokenPayload(token) {
|
|
26
29
|
// istanbul ignore next
|
|
27
30
|
if (!token) {
|
|
28
31
|
return null;
|
|
29
32
|
}
|
|
30
33
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
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') {
|
|
33
38
|
return null;
|
|
34
39
|
}
|
|
35
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
36
|
-
const now = Math.floor(Date.now() / 1000);
|
|
37
40
|
return {
|
|
38
41
|
payload,
|
|
39
42
|
expiresAt: payload.exp,
|
|
@@ -66,6 +69,7 @@ export function useAccessToken() {
|
|
|
66
69
|
}
|
|
67
70
|
}, []);
|
|
68
71
|
const updateToken = useCallback(async () => {
|
|
72
|
+
// istanbul ignore next - safety guard against concurrent fetches
|
|
69
73
|
if (fetchingRef.current) {
|
|
70
74
|
return;
|
|
71
75
|
}
|
|
@@ -74,14 +78,14 @@ export function useAccessToken() {
|
|
|
74
78
|
try {
|
|
75
79
|
let token = await getAccessTokenAction();
|
|
76
80
|
if (token) {
|
|
77
|
-
const tokenData =
|
|
81
|
+
const tokenData = parseTokenPayload(token);
|
|
78
82
|
if (!tokenData || tokenData.isExpiring) {
|
|
79
83
|
token = await refreshAccessTokenAction();
|
|
80
84
|
}
|
|
81
85
|
}
|
|
82
86
|
dispatch({ type: 'FETCH_SUCCESS', token });
|
|
83
87
|
if (token) {
|
|
84
|
-
const tokenData =
|
|
88
|
+
const tokenData = parseTokenPayload(token);
|
|
85
89
|
if (tokenData) {
|
|
86
90
|
const delay = getRefreshDelay(tokenData.timeUntilExpiry);
|
|
87
91
|
clearRefreshTimeout();
|
|
@@ -92,6 +96,7 @@ export function useAccessToken() {
|
|
|
92
96
|
}
|
|
93
97
|
catch (error) {
|
|
94
98
|
dispatch({ type: 'FETCH_ERROR', error: error instanceof Error ? error : new Error(String(error)) });
|
|
99
|
+
clearRefreshTimeout();
|
|
95
100
|
refreshTimeoutRef.current = setTimeout(updateToken, RETRY_DELAY_SECONDS * 1000);
|
|
96
101
|
}
|
|
97
102
|
finally {
|
|
@@ -109,7 +114,7 @@ export function useAccessToken() {
|
|
|
109
114
|
const token = await getAccessTokenAction();
|
|
110
115
|
dispatch({ type: 'FETCH_SUCCESS', token });
|
|
111
116
|
if (token) {
|
|
112
|
-
const tokenData =
|
|
117
|
+
const tokenData = parseTokenPayload(token);
|
|
113
118
|
if (tokenData) {
|
|
114
119
|
const delay = getRefreshDelay(tokenData.timeUntilExpiry);
|
|
115
120
|
clearRefreshTimeout();
|
|
@@ -121,6 +126,7 @@ export function useAccessToken() {
|
|
|
121
126
|
catch (error) {
|
|
122
127
|
const typedError = error instanceof Error ? error : new Error(String(error));
|
|
123
128
|
dispatch({ type: 'FETCH_ERROR', error: typedError });
|
|
129
|
+
clearRefreshTimeout();
|
|
124
130
|
refreshTimeoutRef.current = setTimeout(updateToken, RETRY_DELAY_SECONDS * 1000);
|
|
125
131
|
}
|
|
126
132
|
finally {
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"useAccessToken.js","sourceRoot":"","sources":["../../../src/components/useAccessToken.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { useAccessToken } from './useAccessToken.js';
|
|
3
|
-
import { decodeJwt } from '
|
|
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,
|
|
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"}
|
package/dist/esm/jwt.js
ADDED
|
@@ -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
|
|
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
|
+
};
|
package/dist/esm/workos.js
CHANGED
|
@@ -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.
|
|
4
|
+
export const VERSION = '2.4.3';
|
|
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
|
'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 =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
setLoading(true);
|
|
101
|
-
const auth = await refreshAuthAction({ ensureSignedIn, organizationId });
|
|
93
|
+
return result;
|
|
94
|
+
},
|
|
95
|
+
[],
|
|
96
|
+
);
|
|
102
97
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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,9 +1,11 @@
|
|
|
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
|
|
8
|
+
const MAX_REFRESH_DELAY_SECONDS = 24 * 60 * 60; // 24 hours
|
|
7
9
|
const RETRY_DELAY_SECONDS = 300; // 5 minutes
|
|
8
10
|
|
|
9
11
|
interface TokenState {
|
|
@@ -35,24 +37,25 @@ function tokenReducer(state: TokenState, action: TokenAction): TokenState {
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
function getRefreshDelay(timeUntilExpiry: number) {
|
|
38
|
-
|
|
40
|
+
const idealDelay = (timeUntilExpiry - TOKEN_EXPIRY_BUFFER_SECONDS) * 1000;
|
|
41
|
+
return Math.min(Math.max(idealDelay, MIN_REFRESH_DELAY_SECONDS * 1000), MAX_REFRESH_DELAY_SECONDS * 1000);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
function
|
|
44
|
+
function parseTokenPayload(token: string | undefined) {
|
|
42
45
|
// istanbul ignore next
|
|
43
46
|
if (!token) {
|
|
44
47
|
return null;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
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') {
|
|
50
56
|
return null;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
54
|
-
const now = Math.floor(Date.now() / 1000);
|
|
55
|
-
|
|
56
59
|
return {
|
|
57
60
|
payload,
|
|
58
61
|
expiresAt: payload.exp,
|
|
@@ -88,6 +91,7 @@ export function useAccessToken() {
|
|
|
88
91
|
}, []);
|
|
89
92
|
|
|
90
93
|
const updateToken = useCallback(async () => {
|
|
94
|
+
// istanbul ignore next - safety guard against concurrent fetches
|
|
91
95
|
if (fetchingRef.current) {
|
|
92
96
|
return;
|
|
93
97
|
}
|
|
@@ -97,7 +101,7 @@ export function useAccessToken() {
|
|
|
97
101
|
try {
|
|
98
102
|
let token = await getAccessTokenAction();
|
|
99
103
|
if (token) {
|
|
100
|
-
const tokenData =
|
|
104
|
+
const tokenData = parseTokenPayload(token);
|
|
101
105
|
if (!tokenData || tokenData.isExpiring) {
|
|
102
106
|
token = await refreshAccessTokenAction();
|
|
103
107
|
}
|
|
@@ -106,7 +110,7 @@ export function useAccessToken() {
|
|
|
106
110
|
dispatch({ type: 'FETCH_SUCCESS', token });
|
|
107
111
|
|
|
108
112
|
if (token) {
|
|
109
|
-
const tokenData =
|
|
113
|
+
const tokenData = parseTokenPayload(token);
|
|
110
114
|
if (tokenData) {
|
|
111
115
|
const delay = getRefreshDelay(tokenData.timeUntilExpiry);
|
|
112
116
|
clearRefreshTimeout();
|
|
@@ -117,6 +121,7 @@ export function useAccessToken() {
|
|
|
117
121
|
return token;
|
|
118
122
|
} catch (error) {
|
|
119
123
|
dispatch({ type: 'FETCH_ERROR', error: error instanceof Error ? error : new Error(String(error)) });
|
|
124
|
+
clearRefreshTimeout();
|
|
120
125
|
refreshTimeoutRef.current = setTimeout(updateToken, RETRY_DELAY_SECONDS * 1000);
|
|
121
126
|
} finally {
|
|
122
127
|
fetchingRef.current = false;
|
|
@@ -138,7 +143,7 @@ export function useAccessToken() {
|
|
|
138
143
|
dispatch({ type: 'FETCH_SUCCESS', token });
|
|
139
144
|
|
|
140
145
|
if (token) {
|
|
141
|
-
const tokenData =
|
|
146
|
+
const tokenData = parseTokenPayload(token);
|
|
142
147
|
if (tokenData) {
|
|
143
148
|
const delay = getRefreshDelay(tokenData.timeUntilExpiry);
|
|
144
149
|
clearRefreshTimeout();
|
|
@@ -150,6 +155,7 @@ export function useAccessToken() {
|
|
|
150
155
|
} catch (error) {
|
|
151
156
|
const typedError = error instanceof Error ? error : new Error(String(error));
|
|
152
157
|
dispatch({ type: 'FETCH_ERROR', error: typedError });
|
|
158
|
+
clearRefreshTimeout();
|
|
153
159
|
refreshTimeoutRef.current = setTimeout(updateToken, RETRY_DELAY_SECONDS * 1000);
|
|
154
160
|
} finally {
|
|
155
161
|
fetchingRef.current = false;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { useAccessToken } from './useAccessToken.js';
|
|
3
|
-
import { decodeJwt, type
|
|
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.
|
|
5
|
+
export const VERSION = '2.4.3';
|
|
6
6
|
|
|
7
7
|
const options = {
|
|
8
8
|
apiHostname: WORKOS_API_HOSTNAME,
|