@workos-inc/authkit-nextjs 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -29
- package/dist/esm/auth.js +18 -5
- package/dist/esm/auth.js.map +1 -1
- package/dist/esm/components/tokenStore.js +110 -11
- package/dist/esm/components/tokenStore.js.map +1 -1
- package/dist/esm/components/useAccessToken.js +34 -4
- package/dist/esm/components/useAccessToken.js.map +1 -1
- package/dist/esm/cookie.js +51 -0
- package/dist/esm/cookie.js.map +1 -1
- package/dist/esm/get-authorization-url.js +2 -1
- package/dist/esm/get-authorization-url.js.map +1 -1
- package/dist/esm/middleware.js +2 -2
- package/dist/esm/middleware.js.map +1 -1
- package/dist/esm/session.js +36 -3
- package/dist/esm/session.js.map +1 -1
- package/dist/esm/test-helpers.js +57 -0
- package/dist/esm/test-helpers.js.map +1 -0
- package/dist/esm/types/auth.d.ts +5 -3
- package/dist/esm/types/components/tokenStore.d.ts +7 -2
- package/dist/esm/types/cookie.d.ts +1 -0
- package/dist/esm/types/interfaces.d.ts +3 -0
- package/dist/esm/types/middleware.d.ts +1 -1
- package/dist/esm/types/session.d.ts +2 -1
- package/dist/esm/types/test-helpers.d.ts +3 -0
- package/dist/esm/types/workos.d.ts +1 -1
- package/dist/esm/workos.js +1 -1
- package/package.json +5 -4
- package/src/actions.spec.ts +100 -0
- package/src/auth.spec.ts +347 -0
- package/src/auth.ts +19 -6
- package/src/authkit-callback-route.spec.ts +258 -0
- package/src/components/authkit-provider.spec.tsx +471 -0
- package/src/components/button.spec.tsx +46 -0
- package/src/components/impersonation.spec.tsx +134 -0
- package/src/components/min-max-button.spec.tsx +60 -0
- package/src/components/tokenStore.spec.ts +816 -0
- package/src/components/tokenStore.ts +147 -12
- package/src/components/useAccessToken.spec.tsx +731 -0
- package/src/components/useAccessToken.ts +40 -6
- package/src/components/useTokenClaims.spec.tsx +194 -0
- package/src/cookie.spec.ts +276 -0
- package/src/cookie.ts +56 -0
- package/src/get-authorization-url.spec.ts +60 -0
- package/src/get-authorization-url.ts +2 -0
- package/src/interfaces.ts +3 -0
- package/src/jwt.spec.ts +159 -0
- package/src/middleware.ts +2 -1
- package/src/session.spec.ts +1152 -0
- package/src/session.ts +42 -2
- package/src/test-helpers.ts +70 -0
- package/src/utils.spec.ts +142 -0
- package/src/workos.spec.ts +67 -0
- package/src/workos.ts +1 -1
package/README.md
CHANGED
|
@@ -47,17 +47,18 @@ 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
|
|
51
|
-
|
|
52
|
-
| `WORKOS_COOKIE_MAX_AGE`
|
|
53
|
-
| `WORKOS_COOKIE_DOMAIN`
|
|
54
|
-
| `WORKOS_COOKIE_NAME`
|
|
55
|
-
| `WORKOS_API_HOSTNAME`
|
|
56
|
-
| `WORKOS_API_HTTPS`
|
|
57
|
-
| `WORKOS_API_PORT`
|
|
58
|
-
| `WORKOS_COOKIE_SAMESITE` | `'lax'`
|
|
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
59
|
|
|
60
60
|
Example usage:
|
|
61
|
+
|
|
61
62
|
```sh
|
|
62
63
|
WORKOS_COOKIE_MAX_AGE='600'
|
|
63
64
|
WORKOS_COOKIE_DOMAIN='example.com'
|
|
@@ -102,7 +103,7 @@ export const GET = handleAuth({
|
|
|
102
103
|
});
|
|
103
104
|
```
|
|
104
105
|
|
|
105
|
-
When running in environments like Docker, set the `baseURL` explicitly to ensure the redirects point to the correct location.
|
|
106
|
+
When running in environments like Docker, set the `baseURL` explicitly to ensure the redirects point to the correct location.
|
|
106
107
|
|
|
107
108
|
```ts
|
|
108
109
|
export const GET = handleAuth({
|
|
@@ -112,12 +113,12 @@ export const GET = handleAuth({
|
|
|
112
113
|
|
|
113
114
|
`handleAuth` can be used with the following options.
|
|
114
115
|
|
|
115
|
-
| Option | Default | Description
|
|
116
|
-
| ---------------- | ----------- |
|
|
117
|
-
| `returnPathname` | `/` | The pathname to redirect the user to after signing in
|
|
116
|
+
| Option | Default | Description |
|
|
117
|
+
| ---------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
118
|
+
| `returnPathname` | `/` | The pathname to redirect the user to after signing in |
|
|
118
119
|
| `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 |
|
|
119
|
-
| `onSuccess` | `undefined` | A function that receives successful authentication data and can be used for side-effects like persisting tokens
|
|
120
|
-
| `onError` | `undefined` | A function that can receive the error and the request and handle the error in its own way.
|
|
120
|
+
| `onSuccess` | `undefined` | A function that receives successful authentication data and can be used for side-effects like persisting tokens |
|
|
121
|
+
| `onError` | `undefined` | A function that can receive the error and the request and handle the error in its own way. |
|
|
121
122
|
|
|
122
123
|
#### onSuccess callback data
|
|
123
124
|
|
|
@@ -151,12 +152,13 @@ export const config = { matcher: ['/', '/admin'] };
|
|
|
151
152
|
|
|
152
153
|
The middleware can be configured with several options.
|
|
153
154
|
|
|
154
|
-
| Option | Default | Description
|
|
155
|
-
| ---------------- | ----------- |
|
|
156
|
-
| `redirectUri` | `undefined` | Used in cases where you need your redirect URI to be set dynamically (e.g. Vercel preview deployments)
|
|
157
|
-
| `middlewareAuth` | `undefined` | Used to configure middleware auth options. See [middleware auth](#middleware-auth) for more details.
|
|
158
|
-
| `debug` | `false` | Enables debug logs.
|
|
159
|
-
| `signUpPaths` | `[]` | Used to specify paths that should use the 'sign-up' screen hint when redirecting to AuthKit.
|
|
155
|
+
| Option | Default | Description |
|
|
156
|
+
| ---------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
157
|
+
| `redirectUri` | `undefined` | Used in cases where you need your redirect URI to be set dynamically (e.g. Vercel preview deployments) |
|
|
158
|
+
| `middlewareAuth` | `undefined` | Used to configure middleware auth options. See [middleware auth](#middleware-auth) for more details. |
|
|
159
|
+
| `debug` | `false` | Enables debug logs. |
|
|
160
|
+
| `signUpPaths` | `[]` | Used to specify paths that should use the 'sign-up' screen hint when redirecting to AuthKit. |
|
|
161
|
+
| `eagerAuth` | `false` | Enables synchronous access token availability for third-party services. See [eager auth](#eager-auth) for more details. |
|
|
160
162
|
|
|
161
163
|
#### Custom redirect URI
|
|
162
164
|
|
|
@@ -429,8 +431,78 @@ In the above example the `/admin` page will require a user to be signed in, wher
|
|
|
429
431
|
|
|
430
432
|
`unauthenticatedPaths` uses the same glob logic as the [Next.js matcher](https://nextjs.org/docs/pages/building-your-application/routing/middleware#matcher).
|
|
431
433
|
|
|
434
|
+
### Eager auth
|
|
435
|
+
|
|
436
|
+
The `eagerAuth` option enables synchronous access to authentication tokens on initial page load, which is required by some third-party services that validate tokens directly with WorkOS. When enabled, tokens are available immediately without requiring an asynchronous fetch.
|
|
437
|
+
|
|
438
|
+
#### How it works
|
|
439
|
+
|
|
440
|
+
When `eagerAuth: true` is set, the middleware temporarily stores the access token in a short-lived cookie (30 seconds) that is:
|
|
441
|
+
|
|
442
|
+
- Only set on initial page loads (not API or prefetch requests)
|
|
443
|
+
- Immediately consumed and deleted by the client
|
|
444
|
+
- Available synchronously on the first render
|
|
445
|
+
|
|
446
|
+
#### Usage
|
|
447
|
+
|
|
448
|
+
Enable eager auth in your middleware configuration:
|
|
449
|
+
|
|
450
|
+
```ts
|
|
451
|
+
import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
|
|
452
|
+
|
|
453
|
+
export default authkitMiddleware({
|
|
454
|
+
eagerAuth: true,
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
Then access the token synchronously in your client components:
|
|
459
|
+
|
|
460
|
+
```tsx
|
|
461
|
+
'use client';
|
|
462
|
+
|
|
463
|
+
import { useAuth } from '@workos-inc/authkit-nextjs';
|
|
464
|
+
|
|
465
|
+
function MyComponent() {
|
|
466
|
+
const { getAccessToken } = useAuth();
|
|
467
|
+
|
|
468
|
+
// Token is available immediately on initial page load
|
|
469
|
+
const token = getAccessToken();
|
|
470
|
+
|
|
471
|
+
// Use with third-party services that need immediate token access
|
|
472
|
+
if (token) {
|
|
473
|
+
// Initialize your third-party client with the token
|
|
474
|
+
thirdPartyClient.authenticate(token);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return <div>...</div>;
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
#### Security considerations
|
|
482
|
+
|
|
483
|
+
Eager auth makes tokens briefly accessible via JavaScript (30-second window) to enable synchronous access. This is a common pattern used by many authentication libraries and is generally safe with standard XSS protections.
|
|
484
|
+
|
|
485
|
+
**Best practices:**
|
|
486
|
+
|
|
487
|
+
- Implement a Content Security Policy (CSP) if handling sensitive data
|
|
488
|
+
- Review third-party scripts on authenticated pages
|
|
489
|
+
- Use the standard `getAccessToken()` method when synchronous access isn't required
|
|
490
|
+
|
|
491
|
+
**When to use:**
|
|
492
|
+
|
|
493
|
+
- Third-party services that require synchronous token access
|
|
494
|
+
- Real-time features that need immediate authentication
|
|
495
|
+
- When you want to avoid loading states on initial render
|
|
496
|
+
|
|
497
|
+
**When to use standard async tokens:**
|
|
498
|
+
|
|
499
|
+
- Most API calls where a brief loading state is acceptable
|
|
500
|
+
- When you don't need immediate token access on page load
|
|
501
|
+
|
|
432
502
|
### Composing middleware
|
|
433
503
|
|
|
504
|
+
> **Security note:** Always forward `request.headers` when returning `NextResponse.*` to mitigate SSRF issues in Next.js < 14.2.32 (14.x) or < 15.4.7 (15.x). This pattern is safe on all versions. We strongly recommend upgrading to the latest Next.js.
|
|
505
|
+
|
|
434
506
|
If you don't want to use `authkitMiddleware` and instead want to compose your own middleware, you can use the `authkit` method. In this mode you are responsible to handling what to do when there's no session on a protected route.
|
|
435
507
|
|
|
436
508
|
```ts
|
|
@@ -439,23 +511,46 @@ export default async function middleware(request: NextRequest) {
|
|
|
439
511
|
|
|
440
512
|
// Auth object contains the session, response headers and an authorization URL in the case that the session isn't valid
|
|
441
513
|
// This method will automatically handle setting the cookie and refreshing the session
|
|
442
|
-
const {
|
|
514
|
+
const {
|
|
515
|
+
session,
|
|
516
|
+
headers: authkitHeaders,
|
|
517
|
+
authorizationUrl,
|
|
518
|
+
} = await authkit(request, {
|
|
443
519
|
debug: true,
|
|
444
520
|
});
|
|
445
521
|
|
|
522
|
+
const { pathname } = new URL(request.url);
|
|
523
|
+
|
|
446
524
|
// Control of what to do when there's no session on a protected route is left to the developer
|
|
447
|
-
if (
|
|
525
|
+
if (pathname.startsWith('/account') && !session.user) {
|
|
448
526
|
console.log('No session on protected path');
|
|
449
|
-
return NextResponse.redirect(authorizationUrl);
|
|
450
527
|
|
|
451
|
-
//
|
|
452
|
-
|
|
528
|
+
// Preserve AuthKit headers on redirects (e.g., cookies)
|
|
529
|
+
const response = NextResponse.redirect(authorizationUrl);
|
|
530
|
+
for (const [key, value] of authkitHeaders) {
|
|
531
|
+
if (key.toLowerCase() === 'set-cookie') {
|
|
532
|
+
response.headers.append(key, value);
|
|
533
|
+
} else {
|
|
534
|
+
response.headers.set(key, value);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return response;
|
|
453
538
|
}
|
|
454
539
|
|
|
455
|
-
//
|
|
456
|
-
|
|
457
|
-
headers: headers,
|
|
540
|
+
// Forward the incoming request headers (mitigation) and then add AuthKit's headers
|
|
541
|
+
const response = NextResponse.next({
|
|
542
|
+
request: { headers: new Headers(request.headers) },
|
|
458
543
|
});
|
|
544
|
+
|
|
545
|
+
for (const [key, value] of authkitHeaders) {
|
|
546
|
+
if (key.toLowerCase() === 'set-cookie') {
|
|
547
|
+
response.headers.append(key, value);
|
|
548
|
+
} else {
|
|
549
|
+
response.headers.set(key, value);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return response;
|
|
459
554
|
}
|
|
460
555
|
|
|
461
556
|
// Match against the pages
|
package/dist/esm/auth.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
'use server';
|
|
2
|
+
import { decodeJwt } from 'jose';
|
|
2
3
|
import { revalidatePath, revalidateTag } from 'next/cache';
|
|
3
4
|
import { cookies, headers } from 'next/headers';
|
|
4
5
|
import { redirect } from 'next/navigation';
|
|
5
6
|
import { WORKOS_COOKIE_NAME } from './env-variables.js';
|
|
6
7
|
import { getCookieOptions } from './cookie.js';
|
|
7
8
|
import { getAuthorizationUrl } from './get-authorization-url.js';
|
|
8
|
-
import { refreshSession, withAuth } from './session.js';
|
|
9
|
+
import { getSessionFromCookie, refreshSession, withAuth } from './session.js';
|
|
9
10
|
import { getWorkOS } from './workos.js';
|
|
10
|
-
export async function getSignInUrl({ organizationId, loginHint, redirectUri, } = {}) {
|
|
11
|
-
return getAuthorizationUrl({ organizationId, screenHint: 'sign-in', loginHint, redirectUri });
|
|
11
|
+
export async function getSignInUrl({ organizationId, loginHint, redirectUri, prompt, } = {}) {
|
|
12
|
+
return getAuthorizationUrl({ organizationId, screenHint: 'sign-in', loginHint, redirectUri, prompt });
|
|
12
13
|
}
|
|
13
|
-
export async function getSignUpUrl({ organizationId, loginHint, redirectUri, } = {}) {
|
|
14
|
-
return getAuthorizationUrl({ organizationId, screenHint: 'sign-up', loginHint, redirectUri });
|
|
14
|
+
export async function getSignUpUrl({ organizationId, loginHint, redirectUri, prompt, } = {}) {
|
|
15
|
+
return getAuthorizationUrl({ organizationId, screenHint: 'sign-up', loginHint, redirectUri, prompt });
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* Sign out the user and delete the session cookie.
|
|
@@ -24,6 +25,18 @@ export async function signOut({ returnTo } = {}) {
|
|
|
24
25
|
const { sessionId: sid } = await withAuth();
|
|
25
26
|
sessionId = sid;
|
|
26
27
|
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
// Fall back to reading session directly from cookie when middleware isn't available
|
|
30
|
+
const session = await getSessionFromCookie();
|
|
31
|
+
if (session && session.accessToken) {
|
|
32
|
+
const { sid } = decodeJwt(session.accessToken);
|
|
33
|
+
sessionId = sid;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// can't recover - throw the original error.
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
27
40
|
finally {
|
|
28
41
|
const nextCookies = await cookies();
|
|
29
42
|
const cookieName = WORKOS_COOKIE_NAME || 'wos-session';
|
package/dist/esm/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,15 +4,12 @@ const TOKEN_EXPIRY_BUFFER_SECONDS = 60;
|
|
|
4
4
|
const MIN_REFRESH_DELAY_SECONDS = 15;
|
|
5
5
|
const MAX_REFRESH_DELAY_SECONDS = 24 * 60 * 60;
|
|
6
6
|
const RETRY_DELAY_SECONDS = 300; // 5 minutes for retry on error
|
|
7
|
-
|
|
7
|
+
const jwtCookieName = 'workos-access-token';
|
|
8
|
+
export class TokenStore {
|
|
8
9
|
constructor() {
|
|
9
|
-
this.state = {
|
|
10
|
-
token: undefined,
|
|
11
|
-
loading: false,
|
|
12
|
-
error: null,
|
|
13
|
-
};
|
|
14
10
|
this.listeners = new Set();
|
|
15
11
|
this.refreshPromise = null;
|
|
12
|
+
this.fastCookieConsumed = false;
|
|
16
13
|
this.subscribe = (listener) => {
|
|
17
14
|
this.listeners.add(listener);
|
|
18
15
|
return () => {
|
|
@@ -24,7 +21,30 @@ class TokenStore {
|
|
|
24
21
|
};
|
|
25
22
|
};
|
|
26
23
|
this.getSnapshot = () => this.state;
|
|
27
|
-
this.getServerSnapshot = () =>
|
|
24
|
+
this.getServerSnapshot = () => this.serverSnapshot;
|
|
25
|
+
// Initialize state with token from cookie if available
|
|
26
|
+
const initialToken = this.getInitialTokenFromCookie();
|
|
27
|
+
this.state = {
|
|
28
|
+
token: initialToken,
|
|
29
|
+
loading: false,
|
|
30
|
+
error: null,
|
|
31
|
+
};
|
|
32
|
+
// Server snapshot should match initial state for hydration
|
|
33
|
+
this.serverSnapshot = {
|
|
34
|
+
token: initialToken,
|
|
35
|
+
loading: false,
|
|
36
|
+
error: null,
|
|
37
|
+
};
|
|
38
|
+
/* istanbul ignore next */
|
|
39
|
+
if (initialToken) {
|
|
40
|
+
// Mark as consumed if we found a token
|
|
41
|
+
this.fastCookieConsumed = true;
|
|
42
|
+
// Schedule refresh based on token expiry
|
|
43
|
+
const tokenData = this.parseToken(initialToken);
|
|
44
|
+
if (tokenData) {
|
|
45
|
+
this.scheduleRefresh(tokenData.timeUntilExpiry);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
28
48
|
}
|
|
29
49
|
notify() {
|
|
30
50
|
this.listeners.forEach((listener) => listener());
|
|
@@ -39,9 +59,9 @@ class TokenStore {
|
|
|
39
59
|
this.refreshTimeout = undefined;
|
|
40
60
|
}
|
|
41
61
|
const delay = typeof timeUntilExpiry === 'undefined' ? RETRY_DELAY_SECONDS * 1000 : this.getRefreshDelay(timeUntilExpiry);
|
|
42
|
-
this.refreshTimeout = setTimeout(
|
|
43
|
-
|
|
44
|
-
void this.getAccessTokenSilently().catch(() => { });
|
|
62
|
+
this.refreshTimeout = setTimeout(
|
|
63
|
+
/* istanbul ignore next */ () => {
|
|
64
|
+
void this.getAccessTokenSilently().catch(/* istanbul ignore next */ () => { });
|
|
45
65
|
}, delay);
|
|
46
66
|
}
|
|
47
67
|
getRefreshDelay(timeUntilExpiry) {
|
|
@@ -51,6 +71,70 @@ class TokenStore {
|
|
|
51
71
|
const idealDelay = (timeUntilExpiry - TOKEN_EXPIRY_BUFFER_SECONDS) * 1000;
|
|
52
72
|
return Math.min(Math.max(idealDelay, MIN_REFRESH_DELAY_SECONDS * 1000), MAX_REFRESH_DELAY_SECONDS * 1000);
|
|
53
73
|
}
|
|
74
|
+
deleteCookie() {
|
|
75
|
+
const isSecure = window.location.protocol === 'https:';
|
|
76
|
+
// Build deletion string to match EXACTLY what the server sets
|
|
77
|
+
// Server sets: Path=/, SameSite=Lax, and Secure (if HTTPS)
|
|
78
|
+
// NO Domain attribute is set by server, so we don't set it either
|
|
79
|
+
const deletionString = isSecure
|
|
80
|
+
? `${jwtCookieName}=; SameSite=Lax; Max-Age=0; Secure`
|
|
81
|
+
: `${jwtCookieName}=; SameSite=Lax; Max-Age=0`;
|
|
82
|
+
document.cookie = deletionString;
|
|
83
|
+
// The cookie might still appear in document.cookie even after deletion
|
|
84
|
+
// due to browser caching, but it should be expired and not sent to server
|
|
85
|
+
}
|
|
86
|
+
getInitialTokenFromCookie() {
|
|
87
|
+
if (typeof document === 'undefined' || typeof document.cookie === 'undefined') {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Parse cookies without regex
|
|
91
|
+
const cookies = document.cookie.split(';').reduce((acc, cookie) => {
|
|
92
|
+
const [name, ...valueParts] = cookie.trim().split('=');
|
|
93
|
+
if (name && valueParts.length > 0) {
|
|
94
|
+
const value = valueParts.join('='); // Handle values that contain '='
|
|
95
|
+
acc[name.trim()] = decodeURIComponent(value);
|
|
96
|
+
}
|
|
97
|
+
return acc;
|
|
98
|
+
}, {});
|
|
99
|
+
const token = cookies[jwtCookieName];
|
|
100
|
+
if (!token) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Delete the cookie immediately after reading it
|
|
104
|
+
this.deleteCookie();
|
|
105
|
+
return token;
|
|
106
|
+
}
|
|
107
|
+
consumeFastCookie() {
|
|
108
|
+
// Only try to consume once per page load
|
|
109
|
+
if (this.fastCookieConsumed) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (typeof document === 'undefined' || typeof document.cookie === 'undefined') {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Parse cookies without regex
|
|
116
|
+
const cookies = document.cookie.split(';').reduce((acc, cookie) => {
|
|
117
|
+
const [name, ...valueParts] = cookie.trim().split('=');
|
|
118
|
+
if (name && valueParts.length > 0) {
|
|
119
|
+
const value = valueParts.join('='); // Handle values that contain '='
|
|
120
|
+
acc[name.trim()] = decodeURIComponent(value);
|
|
121
|
+
}
|
|
122
|
+
return acc;
|
|
123
|
+
}, {});
|
|
124
|
+
const newToken = cookies[jwtCookieName];
|
|
125
|
+
if (!newToken) {
|
|
126
|
+
// Mark as consumed even if not found, to avoid repeated checks
|
|
127
|
+
this.fastCookieConsumed = true;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Mark as consumed BEFORE deleting to prevent race conditions
|
|
131
|
+
this.fastCookieConsumed = true;
|
|
132
|
+
// Delete the cookie using protocol-aware deletion
|
|
133
|
+
this.deleteCookie();
|
|
134
|
+
if (newToken !== this.state.token) {
|
|
135
|
+
return newToken;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
54
138
|
parseToken(token) {
|
|
55
139
|
if (!token)
|
|
56
140
|
return null;
|
|
@@ -92,6 +176,11 @@ class TokenStore {
|
|
|
92
176
|
}
|
|
93
177
|
}
|
|
94
178
|
async getAccessToken() {
|
|
179
|
+
const fastToken = this.consumeFastCookie();
|
|
180
|
+
if (fastToken) {
|
|
181
|
+
this.setState({ token: fastToken, loading: false, error: null });
|
|
182
|
+
return fastToken;
|
|
183
|
+
}
|
|
95
184
|
const tokenData = this.parseToken(this.state.token);
|
|
96
185
|
// If we have a valid JWT that's not expiring, return it
|
|
97
186
|
if (tokenData && !tokenData.isExpiring) {
|
|
@@ -105,6 +194,16 @@ class TokenStore {
|
|
|
105
194
|
return this.refreshTokenSilently();
|
|
106
195
|
}
|
|
107
196
|
async getAccessTokenSilently() {
|
|
197
|
+
const fastToken = this.consumeFastCookie();
|
|
198
|
+
if (fastToken) {
|
|
199
|
+
this.setState({ token: fastToken, loading: false, error: null });
|
|
200
|
+
// Schedule refresh based on token expiry
|
|
201
|
+
const tokenData = this.parseToken(fastToken);
|
|
202
|
+
if (tokenData) {
|
|
203
|
+
this.scheduleRefresh(tokenData.timeUntilExpiry);
|
|
204
|
+
}
|
|
205
|
+
return fastToken;
|
|
206
|
+
}
|
|
108
207
|
const tokenData = this.parseToken(this.state.token);
|
|
109
208
|
// If we have a valid JWT that's not expiring, return it
|
|
110
209
|
if (tokenData && !tokenData.isExpiring) {
|
|
@@ -208,6 +307,7 @@ class TokenStore {
|
|
|
208
307
|
reset() {
|
|
209
308
|
this.state = { token: undefined, loading: false, error: null };
|
|
210
309
|
this.refreshPromise = null;
|
|
310
|
+
this.fastCookieConsumed = false;
|
|
211
311
|
if (this.refreshTimeout) {
|
|
212
312
|
clearTimeout(this.refreshTimeout);
|
|
213
313
|
this.refreshTimeout = undefined;
|
|
@@ -215,6 +315,5 @@ class TokenStore {
|
|
|
215
315
|
this.listeners.clear();
|
|
216
316
|
}
|
|
217
317
|
}
|
|
218
|
-
TokenStore.SERVER_SNAPSHOT = { token: undefined, loading: false, error: null };
|
|
219
318
|
export const tokenStore = new TokenStore();
|
|
220
319
|
//# sourceMappingURL=tokenStore.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenStore.js","sourceRoot":"","sources":["../../../src/components/tokenStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAQtC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AACvC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AACrC,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC/C,MAAM,mBAAmB,GAAG,GAAG,CAAC,CAAC,+BAA+B;
|
|
1
|
+
{"version":3,"file":"tokenStore.js","sourceRoot":"","sources":["../../../src/components/tokenStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
2
2
|
import { useAuth } from './authkit-provider.js';
|
|
3
3
|
import { tokenStore } from './tokenStore.js';
|
|
4
4
|
/**
|
|
@@ -12,9 +12,21 @@ export function useAccessToken() {
|
|
|
12
12
|
const prevSessionRef = useRef(sessionId);
|
|
13
13
|
const prevUserIdRef = useRef(userId);
|
|
14
14
|
const tokenState = useSyncExternalStore(tokenStore.subscribe, tokenStore.getSnapshot, tokenStore.getServerSnapshot);
|
|
15
|
+
// Track if we're waiting for the initial token fetch for the current user
|
|
16
|
+
// Initialize synchronously to prevent first-paint flash
|
|
17
|
+
const [isInitialTokenLoading, setIsInitialTokenLoading] = useState(() => {
|
|
18
|
+
// Only show loading if we have a user but no token yet
|
|
19
|
+
return Boolean(user && !tokenState.token && !tokenState.error);
|
|
20
|
+
});
|
|
15
21
|
useEffect(() => {
|
|
16
22
|
if (!user) {
|
|
17
|
-
|
|
23
|
+
setIsInitialTokenLoading(false);
|
|
24
|
+
// Clear token when user logs out
|
|
25
|
+
if (prevUserIdRef.current !== undefined) {
|
|
26
|
+
tokenStore.clearToken();
|
|
27
|
+
}
|
|
28
|
+
prevUserIdRef.current = undefined;
|
|
29
|
+
prevSessionRef.current = undefined;
|
|
18
30
|
return;
|
|
19
31
|
}
|
|
20
32
|
// Only clear token if user or session actually changed (not on initial mount)
|
|
@@ -25,9 +37,25 @@ export function useAccessToken() {
|
|
|
25
37
|
}
|
|
26
38
|
prevSessionRef.current = sessionId;
|
|
27
39
|
prevUserIdRef.current = userId;
|
|
40
|
+
// Check if getAccessTokenSilently will actually fetch (not just return cached)
|
|
41
|
+
const currentToken = tokenStore.getSnapshot().token;
|
|
42
|
+
const tokenData = currentToken ? tokenStore.parseToken(currentToken) : null;
|
|
43
|
+
const willActuallyFetch = !currentToken || (tokenData && tokenData.isExpiring);
|
|
44
|
+
// Only show loading if we're actually going to fetch
|
|
45
|
+
if (willActuallyFetch) {
|
|
46
|
+
setIsInitialTokenLoading(true);
|
|
47
|
+
}
|
|
28
48
|
/* istanbul ignore next */
|
|
29
|
-
tokenStore
|
|
49
|
+
tokenStore
|
|
50
|
+
.getAccessTokenSilently()
|
|
51
|
+
.catch(() => {
|
|
30
52
|
// Error is handled in the store
|
|
53
|
+
})
|
|
54
|
+
.finally(() => {
|
|
55
|
+
// Only clear loading if we were actually loading
|
|
56
|
+
if (willActuallyFetch) {
|
|
57
|
+
setIsInitialTokenLoading(false);
|
|
58
|
+
}
|
|
31
59
|
});
|
|
32
60
|
}, [userId, sessionId]);
|
|
33
61
|
useEffect(() => {
|
|
@@ -70,9 +98,11 @@ export function useAccessToken() {
|
|
|
70
98
|
}
|
|
71
99
|
return tokenStore.refreshToken();
|
|
72
100
|
}, []);
|
|
101
|
+
// Combine loading states: initial token fetch OR token store is loading
|
|
102
|
+
const isLoading = isInitialTokenLoading || tokenState.loading;
|
|
73
103
|
return {
|
|
74
104
|
accessToken: tokenState.token,
|
|
75
|
-
loading:
|
|
105
|
+
loading: isLoading,
|
|
76
106
|
error: tokenState.error,
|
|
77
107
|
refresh,
|
|
78
108
|
getAccessToken,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAccessToken.js","sourceRoot":"","sources":["../../../src/components/useAccessToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useAccessToken.js","sourceRoot":"","sources":["../../../src/components/useAccessToken.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/cookie.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { WORKOS_REDIRECT_URI, WORKOS_COOKIE_MAX_AGE, WORKOS_COOKIE_DOMAIN, WORKOS_COOKIE_SAMESITE, } from './env-variables.js';
|
|
2
|
+
const JWT_COOKIE_MAX_AGE = 30; // seconds
|
|
3
|
+
const JWT_COOKIE_NAME = 'workos-access-token';
|
|
2
4
|
function assertValidSamSite(sameSite) {
|
|
3
5
|
if (!['lax', 'strict', 'none'].includes(sameSite.toLowerCase())) {
|
|
4
6
|
throw new Error(`Invalid SameSite value: ${sameSite}`);
|
|
@@ -61,4 +63,53 @@ export function getCookieOptions(redirectUri, asString = false, expired = false)
|
|
|
61
63
|
domain: WORKOS_COOKIE_DOMAIN || '',
|
|
62
64
|
};
|
|
63
65
|
}
|
|
66
|
+
export function getJwtCookie(body, requestUrlOrRedirectUri, expired) {
|
|
67
|
+
const cookie = `${JWT_COOKIE_NAME}=${expired ? '' : (body !== null && body !== void 0 ? body : '')}`;
|
|
68
|
+
// Force Secure in production, except for localhost
|
|
69
|
+
let secure = false;
|
|
70
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
71
|
+
if (requestUrlOrRedirectUri) {
|
|
72
|
+
try {
|
|
73
|
+
const url = new URL(requestUrlOrRedirectUri);
|
|
74
|
+
const isLocalhost = url.hostname === 'localhost' || url.hostname === '127.0.0.1';
|
|
75
|
+
// In production, always use Secure unless explicitly on localhost
|
|
76
|
+
secure = isProduction ? !isLocalhost : url.protocol === 'https:';
|
|
77
|
+
}
|
|
78
|
+
catch (_a) {
|
|
79
|
+
// If URL parsing fails, default to secure in production
|
|
80
|
+
secure = isProduction;
|
|
81
|
+
// If it's not a valid URL, fall back to WORKOS_REDIRECT_URI
|
|
82
|
+
const fallbackUrl = WORKOS_REDIRECT_URI;
|
|
83
|
+
if (fallbackUrl) {
|
|
84
|
+
try {
|
|
85
|
+
const url = new URL(fallbackUrl);
|
|
86
|
+
secure = url.protocol === 'https:';
|
|
87
|
+
}
|
|
88
|
+
catch (_b) {
|
|
89
|
+
secure = false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (WORKOS_REDIRECT_URI) {
|
|
95
|
+
// No URL provided, check WORKOS_REDIRECT_URI
|
|
96
|
+
try {
|
|
97
|
+
const url = new URL(WORKOS_REDIRECT_URI);
|
|
98
|
+
secure = url.protocol === 'https:';
|
|
99
|
+
}
|
|
100
|
+
catch (_c) {
|
|
101
|
+
secure = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const maxAge = expired ? 0 : JWT_COOKIE_MAX_AGE;
|
|
105
|
+
const parts = [cookie, 'SameSite=Lax', `Max-Age=${maxAge}`];
|
|
106
|
+
// Only add Secure flag if on HTTPS
|
|
107
|
+
if (secure) {
|
|
108
|
+
parts.push('Secure');
|
|
109
|
+
}
|
|
110
|
+
if (expired) {
|
|
111
|
+
parts.push(`Expires=${new Date(0).toUTCString()}`);
|
|
112
|
+
}
|
|
113
|
+
return parts.join('; ');
|
|
114
|
+
}
|
|
64
115
|
//# sourceMappingURL=cookie.js.map
|