@workos-inc/authkit-nextjs 2.10.0 → 2.11.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 +28 -2
- package/dist/esm/auth.js +9 -1
- package/dist/esm/auth.js.map +1 -1
- package/dist/esm/components/impersonation.js +8 -6
- package/dist/esm/components/impersonation.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/test-helpers.js +1 -0
- package/dist/esm/test-helpers.js.map +1 -1
- package/dist/esm/types/components/impersonation.d.ts +2 -1
- package/dist/esm/types/index.d.ts +2 -1
- package/dist/esm/types/validate-api-key.d.ts +1 -0
- package/dist/esm/types/workos.d.ts +1 -1
- package/dist/esm/validate-api-key.js +17 -0
- package/dist/esm/validate-api-key.js.map +1 -0
- package/dist/esm/workos.js +1 -1
- package/package.json +4 -4
- package/src/auth.ts +11 -1
- package/src/authkit-callback-route.spec.ts +1 -0
- package/src/components/impersonation.spec.tsx +136 -20
- package/src/components/impersonation.tsx +8 -6
- package/src/index.ts +2 -0
- package/src/test-helpers.ts +1 -0
- package/src/validate-api-key.spec.ts +113 -0
- package/src/validate-api-key.ts +19 -0
- package/src/workos.ts +1 -1
package/README.md
CHANGED
|
@@ -141,9 +141,14 @@ The `onSuccess` callback receives the following data:
|
|
|
141
141
|
|
|
142
142
|
**Note**: `authenticationMethod` is only provided during the initial authentication callback. It will not be available in subsequent requests or session refreshes.
|
|
143
143
|
|
|
144
|
-
### Middleware
|
|
144
|
+
### Middleware / Proxy
|
|
145
145
|
|
|
146
|
-
This library relies on
|
|
146
|
+
This library relies on Next.js middleware to provide session management for routes.
|
|
147
|
+
|
|
148
|
+
**For Next.js ≤15:** Create a `middleware.ts` file in the root of your project.
|
|
149
|
+
**For Next.js 16+:** Create a `proxy.ts` file in the root of your project.
|
|
150
|
+
|
|
151
|
+
The code remains the same; only the filename changes:
|
|
147
152
|
|
|
148
153
|
```ts
|
|
149
154
|
import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
|
|
@@ -571,6 +576,8 @@ Eager auth makes tokens briefly accessible via JavaScript (30-second window) to
|
|
|
571
576
|
|
|
572
577
|
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.
|
|
573
578
|
|
|
579
|
+
> **Note:** For Next.js 16+, name your file `proxy.ts` and the function `proxy` instead of `middleware`.
|
|
580
|
+
|
|
574
581
|
```ts
|
|
575
582
|
export default async function middleware(request: NextRequest) {
|
|
576
583
|
// Perform logic before or after AuthKit
|
|
@@ -689,6 +696,25 @@ export default authkitMiddleware({
|
|
|
689
696
|
});
|
|
690
697
|
```
|
|
691
698
|
|
|
699
|
+
### Validate an API key
|
|
700
|
+
|
|
701
|
+
Use the `validateApiKey` function in your application's public API endpoints to parse a [Bearer Authentication](https://swagger.io/docs/specification/v3_0/authentication/bearer-authentication/) header and validate the [API key](https://workos.com/docs/authkit/api-keys) with WorkOS.
|
|
702
|
+
|
|
703
|
+
```ts
|
|
704
|
+
import { NextResponse } from 'next/server'
|
|
705
|
+
import { validateApiKey } from '@workos-inc/authkit-nextjs'
|
|
706
|
+
|
|
707
|
+
export async function GET() {
|
|
708
|
+
const { apiKey } = await validateApiKey()
|
|
709
|
+
|
|
710
|
+
if (!apiKey) {
|
|
711
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return NextResponse.json({ success: true })
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
692
718
|
### Advanced: Direct access to the WorkOS client
|
|
693
719
|
|
|
694
720
|
For advanced use cases or functionality not covered by the helper methods, you can access the underlying WorkOS client directly:
|
package/dist/esm/auth.js
CHANGED
|
@@ -8,6 +8,14 @@ import { getCookieOptions } from './cookie.js';
|
|
|
8
8
|
import { getAuthorizationUrl } from './get-authorization-url.js';
|
|
9
9
|
import { getSessionFromCookie, refreshSession, withAuth } from './session.js';
|
|
10
10
|
import { getWorkOS } from './workos.js';
|
|
11
|
+
/**
|
|
12
|
+
* A wrapper around revalidateTag to provide compatibility with previous versions.
|
|
13
|
+
* @param tag The tag to revalidate.
|
|
14
|
+
*/
|
|
15
|
+
function revalidateTagCompat(tag) {
|
|
16
|
+
const fn = revalidateTag;
|
|
17
|
+
return fn(tag, 'max');
|
|
18
|
+
}
|
|
11
19
|
export async function getSignInUrl({ organizationId, loginHint, redirectUri, prompt, state, } = {}) {
|
|
12
20
|
return getAuthorizationUrl({ organizationId, screenHint: 'sign-in', loginHint, redirectUri, prompt, state });
|
|
13
21
|
}
|
|
@@ -82,7 +90,7 @@ export async function switchToOrganization(organizationId, options = {}) {
|
|
|
82
90
|
break;
|
|
83
91
|
case 'tag':
|
|
84
92
|
for (const tag of revalidationTags) {
|
|
85
|
-
|
|
93
|
+
revalidateTagCompat(tag);
|
|
86
94
|
}
|
|
87
95
|
break;
|
|
88
96
|
}
|
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,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,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,oBAAoB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,15 +4,17 @@ import { Button } from './button.js';
|
|
|
4
4
|
import { MinMaxButton } from './min-max-button.js';
|
|
5
5
|
import { getOrganizationAction, handleSignOutAction } from '../actions.js';
|
|
6
6
|
import { useAuth } from './authkit-provider.js';
|
|
7
|
-
export function Impersonation({ side = 'bottom', ...props }) {
|
|
8
|
-
const { user, impersonator, organizationId
|
|
7
|
+
export function Impersonation({ side = 'bottom', returnTo, ...props }) {
|
|
8
|
+
const { user, impersonator, organizationId } = useAuth();
|
|
9
9
|
const [organization, setOrganization] = React.useState(null);
|
|
10
10
|
React.useEffect(() => {
|
|
11
|
-
if (!organizationId)
|
|
11
|
+
if (!organizationId || !impersonator || !user)
|
|
12
|
+
return;
|
|
13
|
+
if (organization && organization.id === organizationId)
|
|
12
14
|
return;
|
|
13
15
|
getOrganizationAction(organizationId).then(setOrganization);
|
|
14
|
-
}, [organizationId]);
|
|
15
|
-
if (
|
|
16
|
+
}, [organizationId, impersonator, user]);
|
|
17
|
+
if (!impersonator || !user)
|
|
16
18
|
return null;
|
|
17
19
|
return (React.createElement("div", { ...props, "data-workos-impersonation-root": "", style: {
|
|
18
20
|
'position': 'fixed',
|
|
@@ -53,7 +55,7 @@ export function Impersonation({ side = 'bottom', ...props }) {
|
|
|
53
55
|
} },
|
|
54
56
|
React.createElement("form", { onSubmit: async (event) => {
|
|
55
57
|
event.preventDefault();
|
|
56
|
-
await handleSignOutAction();
|
|
58
|
+
await handleSignOutAction({ returnTo });
|
|
57
59
|
}, style: {
|
|
58
60
|
display: 'flex',
|
|
59
61
|
alignItems: 'baseline',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"impersonation.js","sourceRoot":"","sources":["../../../src/components/impersonation.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"impersonation.js","sourceRoot":"","sources":["../../../src/components/impersonation.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAOhD,MAAM,UAAU,aAAa,CAAC,EAAE,IAAI,GAAG,QAAQ,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAsB;IACvF,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;IAEzD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAsB,IAAI,CAAC,CAAC;IAElF,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,cAAc,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI;YAAE,OAAO;QACtD,IAAI,YAAY,IAAI,YAAY,CAAC,EAAE,KAAK,cAAc;YAAE,OAAO;QAC/D,qBAAqB,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC9D,CAAC,EAAE,CAAC,cAAc,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;IAEzC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAExC,OAAO,CACL,gCACM,KAAK,oCACsB,EAAE,EACjC,KAAK,EAAE;YACL,UAAU,EAAE,OAAO;YACnB,OAAO,EAAE,CAAC;YACV,eAAe,EAAE,MAAM;YACvB,QAAQ,EAAE,IAAI;YAEd,2DAA2D;YAC3D,gBAAgB,EAAE,GAAG;YACrB,QAAQ,EAAE,4DAA4D;YACtE,UAAU,EAAE,uDAAuD;YACnE,QAAQ,EAAE,4CAA4C;YACtD,SAAS,EAAE,mDAAmD;YAC9D,SAAS,EAAE,+CAA+C;YAE1D,GAAG,KAAK,CAAC,KAAK;SACf;QAED,6BACE,KAAK,EAAE;gBACL,iBAAiB,EAAE,yFAAyF;gBAC5G,UAAU,EAAE,UAAU;gBACtB,OAAO,EAAE,iCAAiC;gBAC1C,cAAc,EAAE,gCAAgC;gBAChD,WAAW,EAAE;;;MAGjB;gBACI,YAAY,EAAE,yCAAyC;aACxD,GACD;QAEF,6BACE,KAAK,EAAE;gBACL,OAAO,EAAE,MAAM;gBACf,cAAc,EAAE,QAAQ;gBAExB,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,CAAC;gBACP,KAAK,EAAE,CAAC;gBACR,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;gBAC7C,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;gBAEnD,UAAU,EACR,wIAAwI;gBAC1I,QAAQ,EAAE,gCAAgC;gBAC1C,UAAU,EAAE,KAAK;aAClB;YAED,8BACE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;oBACxB,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,MAAM,mBAAmB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC1C,CAAC,EACD,KAAK,EAAE;oBACL,OAAO,EAAE,MAAM;oBACf,UAAU,EAAE,UAAU;oBACtB,WAAW,EAAE,aAAa;oBAC1B,YAAY,EAAE,aAAa;oBAE3B,QAAQ,EAAE,UAAU;oBACpB,UAAU,EAAE,uBAAuB;oBACnC,WAAW,EAAE,uBAAuB;oBAEpC,aAAa,EAAE,MAAM;oBACrB,eAAe,EAAE,eAAe;oBAChC,WAAW,EAAE,OAAO;oBACpB,WAAW,EAAE,cAAc;oBAC3B,eAAe,EAAE,cAAc;oBAC/B,gBAAgB,EAAE,cAAc;oBAEhC,UAAU,EAAE,yCAAyC;oBACrD,SAAS,EAAE,iEAAiE;oBAC5E,OAAO,EAAE,+BAA+B;oBACxC,MAAM,EAAE,+BAA+B;oBAEvC,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI;wBACpB,UAAU,EAAE,CAAC;wBACb,aAAa,EAAE,aAAa;wBAC5B,cAAc,EAAE,CAAC;wBACjB,iBAAiB,EAAE,cAAc;wBACjC,sBAAsB,EAAE,aAAa;wBACrC,uBAAuB,EAAE,aAAa;qBACvC,CAAC;oBAEF,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI;wBACvB,UAAU,EAAE,aAAa;wBACzB,aAAa,EAAE,CAAC;wBAChB,cAAc,EAAE,cAAc;wBAC9B,iBAAiB,EAAE,CAAC;wBACpB,mBAAmB,EAAE,aAAa;wBAClC,oBAAoB,EAAE,aAAa;qBACpC,CAAC;iBACH;gBAED,2BAAG,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE;;oBACxE,+BAAI,IAAI,CAAC,KAAK,CAAK;oBAAC,GAAG;oBAC5C,YAAY,KAAK,IAAI,IAAI,CACxB;;wBACa,+BAAI,YAAY,CAAC,IAAI,CAAK;wCACpC,CACJ,CACC;gBACJ,oBAAC,MAAM,IAAC,IAAI,EAAC,QAAQ,EAAC,KAAK,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE,WAAW,EAAE,aAAa,EAAE,WAEvF;gBACT,oBAAC,YAAY,IAAC,cAAc,EAAC,GAAG,IAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAgB,CACvE;YAEP,6BACE,KAAK,EAAE;oBACL,OAAO,EAAE,aAAa;oBAEtB,QAAQ,EAAE,OAAO;oBACjB,KAAK,EAAE,aAAa;oBAEpB,aAAa,EAAE,MAAM;oBACrB,eAAe,EAAE,eAAe;oBAChC,MAAM,EAAE,iCAAiC;oBACzC,YAAY,EAAE,aAAa;oBAE3B,UAAU,EAAE,yCAAyC;oBACrD,SAAS,EAAE,gEAAgE;oBAC3E,OAAO,EAAE,qBAAqB;oBAC9B,MAAM,EAAE,qBAAqB;oBAE7B,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;oBAC7C,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;iBACpD;gBAED,oBAAC,YAAY,IAAC,cAAc,EAAC,GAAG,IAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAgB,CACxE,CACF,CACF,CACP,CAAC;AACJ,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -2,7 +2,8 @@ import { getSignInUrl, getSignUpUrl, signOut, switchToOrganization } from './aut
|
|
|
2
2
|
import { handleAuth } from './authkit-callback-route.js';
|
|
3
3
|
import { authkit, authkitMiddleware } from './middleware.js';
|
|
4
4
|
import { getTokenClaims, refreshSession, saveSession, withAuth } from './session.js';
|
|
5
|
+
import { validateApiKey } from './validate-api-key.js';
|
|
5
6
|
import { getWorkOS } from './workos.js';
|
|
6
7
|
export * from './interfaces.js';
|
|
7
|
-
export { authkit, authkitMiddleware, getSignInUrl, getSignUpUrl, getWorkOS, handleAuth, refreshSession, saveSession, signOut, switchToOrganization, withAuth, getTokenClaims, };
|
|
8
|
+
export { authkit, authkitMiddleware, getSignInUrl, getSignUpUrl, getWorkOS, handleAuth, refreshSession, saveSession, signOut, switchToOrganization, withAuth, getTokenClaims, validateApiKey, };
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,cAAc,iBAAiB,CAAC;AAEhC,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,cAAc,EACd,WAAW,EACX,OAAO,EACP,oBAAoB,EACpB,QAAQ,EACR,cAAc,GACf,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,cAAc,iBAAiB,CAAC;AAEhC,OAAO,EACL,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,cAAc,EACd,WAAW,EACX,OAAO,EACP,oBAAoB,EACpB,QAAQ,EACR,cAAc,EACd,cAAc,GACf,CAAC"}
|
package/dist/esm/test-helpers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../../src/test-helpers.ts"],"names":[],"mappings":"AAAA,uBAAuB;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAO,GAAG,EAAE,EAAE,OAAO,GAAG,KAAK;IACnE,MAAM,cAAc,GAAG;QACrB,GAAG,EAAE,aAAa;QAClB,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,WAAW,EAAE,CAAC,cAAc,EAAE,cAAc,CAAC;QAC7C,YAAY,EAAE,CAAC,YAAY,CAAC;QAC5B,aAAa,EAAE,CAAC,4BAA4B,CAAC;KAC9C,CAAC;IAEF,MAAM,aAAa,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;IAExD,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAgC,CAAC,CAAC;IAEtF,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;SAC3C,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;SACpC,WAAW,EAAE;SACb,SAAS,CAAC,oBAAoB,CAAC;SAC/B,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;SACxC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,YAA2B,EAAE;IACjE,MAAM,QAAQ,GAAG;QACf,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,kBAAkB;QACzB,aAAa,EAAE,IAAI;QACnB,iBAAiB,EAAE,IAAI;QACvB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,sBAAsB;QACjC,SAAS,EAAE,sBAAsB;QACjC,YAAY,EAAE,sBAAsB;QACpC,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,EAAE;QACZ,GAAG,SAAS;KACE,CAAC;IAEjB,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC;QAC1C,GAAG,EAAE,aAAa;QAClB,MAAM,EAAE,SAAS;KAClB,CAAC,CAAC;IAEH,kCAAkC;IAClC,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CACrC;QACE,WAAW;QACX,YAAY,EAAE,mBAAmB;QACjC,IAAI,EAAE,QAAQ;KACf,EACD;QACE,QAAQ,EAAE,sBAAgC;KAC3C,CACF,CAAC;IAEF,MAAM,UAAU,GAAG,kBAAkB,IAAI,aAAa,CAAC;IACvD,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;IACpC,WAAW,CAAC,GAAG,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;AAChD,CAAC"}
|
|
1
|
+
{"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../../src/test-helpers.ts"],"names":[],"mappings":"AAAA,uBAAuB;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAO,GAAG,EAAE,EAAE,OAAO,GAAG,KAAK;IACnE,MAAM,cAAc,GAAG;QACrB,GAAG,EAAE,aAAa;QAClB,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,CAAC,QAAQ,CAAC;QACjB,WAAW,EAAE,CAAC,cAAc,EAAE,cAAc,CAAC;QAC7C,YAAY,EAAE,CAAC,YAAY,CAAC;QAC5B,aAAa,EAAE,CAAC,4BAA4B,CAAC;KAC9C,CAAC;IAEF,MAAM,aAAa,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;IAExD,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAgC,CAAC,CAAC;IAEtF,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;SAC3C,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;SACpC,WAAW,EAAE;SACb,SAAS,CAAC,oBAAoB,CAAC;SAC/B,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;SACxC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,YAA2B,EAAE;IACjE,MAAM,QAAQ,GAAG;QACf,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,kBAAkB;QACzB,aAAa,EAAE,IAAI;QACnB,iBAAiB,EAAE,IAAI;QACvB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,sBAAsB;QACjC,SAAS,EAAE,sBAAsB;QACjC,YAAY,EAAE,sBAAsB;QACpC,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,IAAI;QACZ,GAAG,SAAS;KACE,CAAC;IAEjB,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC;QAC1C,GAAG,EAAE,aAAa;QAClB,MAAM,EAAE,SAAS;KAClB,CAAC,CAAC;IAEH,kCAAkC;IAClC,MAAM,gBAAgB,GAAG,MAAM,QAAQ,CACrC;QACE,WAAW;QACX,YAAY,EAAE,mBAAmB;QACjC,IAAI,EAAE,QAAQ;KACf,EACD;QACE,QAAQ,EAAE,sBAAgC;KAC3C,CACF,CAAC;IAEF,MAAM,UAAU,GAAG,kBAAkB,IAAI,aAAa,CAAC;IACvD,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;IACpC,WAAW,CAAC,GAAG,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
interface ImpersonationProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
3
3
|
side?: 'top' | 'bottom';
|
|
4
|
+
returnTo?: string;
|
|
4
5
|
}
|
|
5
|
-
export declare function Impersonation({ side, ...props }: ImpersonationProps): React.JSX.Element | null;
|
|
6
|
+
export declare function Impersonation({ side, returnTo, ...props }: ImpersonationProps): React.JSX.Element | null;
|
|
6
7
|
export {};
|
|
@@ -2,6 +2,7 @@ import { getSignInUrl, getSignUpUrl, signOut, switchToOrganization } from './aut
|
|
|
2
2
|
import { handleAuth } from './authkit-callback-route.js';
|
|
3
3
|
import { authkit, authkitMiddleware } from './middleware.js';
|
|
4
4
|
import { getTokenClaims, refreshSession, saveSession, withAuth } from './session.js';
|
|
5
|
+
import { validateApiKey } from './validate-api-key.js';
|
|
5
6
|
import { getWorkOS } from './workos.js';
|
|
6
7
|
export * from './interfaces.js';
|
|
7
|
-
export { authkit, authkitMiddleware, getSignInUrl, getSignUpUrl, getWorkOS, handleAuth, refreshSession, saveSession, signOut, switchToOrganization, withAuth, getTokenClaims, };
|
|
8
|
+
export { authkit, authkitMiddleware, getSignInUrl, getSignUpUrl, getWorkOS, handleAuth, refreshSession, saveSession, signOut, switchToOrganization, withAuth, getTokenClaims, validateApiKey, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function validateApiKey(): Promise<import("@workos-inc/node/lib/api-keys/interfaces/validate-api-key.interface.js").ValidateApiKeyResponse>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WorkOS } from '@workos-inc/node';
|
|
2
|
-
export declare const VERSION = "2.
|
|
2
|
+
export declare const VERSION = "2.11.0";
|
|
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.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
import { getWorkOS } from './workos.js';
|
|
3
|
+
import { headers } from 'next/headers';
|
|
4
|
+
export async function validateApiKey() {
|
|
5
|
+
var _a;
|
|
6
|
+
const headersList = await headers();
|
|
7
|
+
const authorizationHeader = headersList.get('authorization');
|
|
8
|
+
if (!authorizationHeader) {
|
|
9
|
+
return { apiKey: null };
|
|
10
|
+
}
|
|
11
|
+
const value = (_a = authorizationHeader.match(/Bearer\s+(.*)/i)) === null || _a === void 0 ? void 0 : _a[1];
|
|
12
|
+
if (!value) {
|
|
13
|
+
return { apiKey: null };
|
|
14
|
+
}
|
|
15
|
+
return getWorkOS().apiKeys.validateApiKey({ value });
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=validate-api-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-api-key.js","sourceRoot":"","sources":["../../src/validate-api-key.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,cAAc;;IAClC,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;IACpC,MAAM,mBAAmB,GAAG,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7D,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,KAAK,GAAG,MAAA,mBAAmB,CAAC,KAAK,CAAC,gBAAgB,CAAC,0CAAG,CAAC,CAAC,CAAC;IAC/D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAED,OAAO,SAAS,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACvD,CAAC"}
|
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
|
+
export const VERSION = '2.11.0';
|
|
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.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Authentication and session helpers for using WorkOS & AuthKit with Next.js",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"type-check": "tsc --project tsconfig.json --noEmit"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@workos-inc/node": "^7.
|
|
38
|
+
"@workos-inc/node": "^7.72.0",
|
|
39
39
|
"iron-session": "^8.0.1",
|
|
40
40
|
"jose": "^5.2.3",
|
|
41
41
|
"path-to-regexp": "^6.2.2"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"next": "^13.5.9 || ^14.2.26 || ^15.2.3",
|
|
44
|
+
"next": "^13.5.9 || ^14.2.26 || ^15.2.3 || ^16",
|
|
45
45
|
"react": "^18.0 || ^19.0.0",
|
|
46
46
|
"react-dom": "^18.0 || ^19.0.0"
|
|
47
47
|
},
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"eslint-plugin-require-extensions": "^0.1.3",
|
|
58
58
|
"jest": "^29.7.0",
|
|
59
59
|
"jest-environment-jsdom": "^29.7.0",
|
|
60
|
-
"next": "^
|
|
60
|
+
"next": "^16.0.1",
|
|
61
61
|
"prettier": "^3.3.3",
|
|
62
62
|
"ts-jest": "^29.2.5",
|
|
63
63
|
"ts-node": "^10.9.2",
|
package/src/auth.ts
CHANGED
|
@@ -10,6 +10,16 @@ import { getAuthorizationUrl } from './get-authorization-url.js';
|
|
|
10
10
|
import type { AccessToken, SwitchToOrganizationOptions, UserInfo } from './interfaces.js';
|
|
11
11
|
import { getSessionFromCookie, refreshSession, withAuth } from './session.js';
|
|
12
12
|
import { getWorkOS } from './workos.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A wrapper around revalidateTag to provide compatibility with previous versions.
|
|
16
|
+
* @param tag The tag to revalidate.
|
|
17
|
+
*/
|
|
18
|
+
function revalidateTagCompat(tag: string): void {
|
|
19
|
+
const fn = revalidateTag as (tag: string, profile: string) => void;
|
|
20
|
+
return fn(tag, 'max');
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
export async function getSignInUrl({
|
|
14
24
|
organizationId,
|
|
15
25
|
loginHint,
|
|
@@ -111,7 +121,7 @@ export async function switchToOrganization(
|
|
|
111
121
|
break;
|
|
112
122
|
case 'tag':
|
|
113
123
|
for (const tag of revalidationTags) {
|
|
114
|
-
|
|
124
|
+
revalidateTagCompat(tag);
|
|
115
125
|
}
|
|
116
126
|
break;
|
|
117
127
|
}
|
|
@@ -27,19 +27,6 @@ describe('Impersonation', () => {
|
|
|
27
27
|
impersonator: null,
|
|
28
28
|
user: { id: '123', email: 'user@example.com' },
|
|
29
29
|
organizationId: null,
|
|
30
|
-
loading: false,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const { container } = render(<Impersonation />);
|
|
34
|
-
expect(container).toBeEmptyDOMElement();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should return null if loading', () => {
|
|
38
|
-
(useAuth as jest.Mock).mockReturnValue({
|
|
39
|
-
impersonator: { email: 'admin@example.com' },
|
|
40
|
-
user: { id: '123', email: 'user@example.com' },
|
|
41
|
-
organizationId: null,
|
|
42
|
-
loading: true,
|
|
43
30
|
});
|
|
44
31
|
|
|
45
32
|
const { container } = render(<Impersonation />);
|
|
@@ -51,7 +38,6 @@ describe('Impersonation', () => {
|
|
|
51
38
|
impersonator: { email: 'admin@example.com' },
|
|
52
39
|
user: { id: '123', email: 'user@example.com' },
|
|
53
40
|
organizationId: null,
|
|
54
|
-
loading: false,
|
|
55
41
|
});
|
|
56
42
|
|
|
57
43
|
const { container } = render(<Impersonation />);
|
|
@@ -63,7 +49,6 @@ describe('Impersonation', () => {
|
|
|
63
49
|
impersonator: { email: 'admin@example.com' },
|
|
64
50
|
user: { id: '123', email: 'user@example.com' },
|
|
65
51
|
organizationId: 'org_123',
|
|
66
|
-
loading: false,
|
|
67
52
|
});
|
|
68
53
|
|
|
69
54
|
(getOrganizationAction as jest.Mock).mockResolvedValue({
|
|
@@ -83,7 +68,6 @@ describe('Impersonation', () => {
|
|
|
83
68
|
impersonator: { email: 'admin@example.com' },
|
|
84
69
|
user: { id: '123', email: 'user@example.com' },
|
|
85
70
|
organizationId: null,
|
|
86
|
-
loading: false,
|
|
87
71
|
});
|
|
88
72
|
|
|
89
73
|
const { container } = render(<Impersonation />);
|
|
@@ -96,7 +80,6 @@ describe('Impersonation', () => {
|
|
|
96
80
|
impersonator: { email: 'admin@example.com' },
|
|
97
81
|
user: { id: '123', email: 'user@example.com' },
|
|
98
82
|
organizationId: null,
|
|
99
|
-
loading: false,
|
|
100
83
|
});
|
|
101
84
|
|
|
102
85
|
const { container } = render(<Impersonation side="top" />);
|
|
@@ -109,7 +92,6 @@ describe('Impersonation', () => {
|
|
|
109
92
|
impersonator: { email: 'admin@example.com' },
|
|
110
93
|
user: { id: '123', email: 'user@example.com' },
|
|
111
94
|
organizationId: null,
|
|
112
|
-
loading: false,
|
|
113
95
|
});
|
|
114
96
|
|
|
115
97
|
const customStyle = { backgroundColor: 'red' };
|
|
@@ -123,12 +105,146 @@ describe('Impersonation', () => {
|
|
|
123
105
|
impersonator: { email: 'admin@example.com' },
|
|
124
106
|
user: { id: '123', email: 'user@example.com' },
|
|
125
107
|
organizationId: null,
|
|
126
|
-
loading: false,
|
|
127
108
|
});
|
|
128
109
|
|
|
129
110
|
render(<Impersonation />);
|
|
130
111
|
const stopButton = await screen.findByText('Stop');
|
|
131
112
|
stopButton.click();
|
|
132
|
-
expect(handleSignOutAction).
|
|
113
|
+
expect(handleSignOutAction).toHaveBeenCalledWith({});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should pass returnTo prop to handleSignOutAction when provided', async () => {
|
|
117
|
+
(useAuth as jest.Mock).mockReturnValue({
|
|
118
|
+
impersonator: { email: 'admin@example.com' },
|
|
119
|
+
user: { id: '123', email: 'user@example.com' },
|
|
120
|
+
organizationId: null,
|
|
121
|
+
loading: false,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const returnTo = '/dashboard';
|
|
125
|
+
render(<Impersonation returnTo={returnTo} />);
|
|
126
|
+
const stopButton = await screen.findByText('Stop');
|
|
127
|
+
stopButton.click();
|
|
128
|
+
expect(handleSignOutAction).toHaveBeenCalledWith({ returnTo });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should not call getOrganizationAction when organizationId is not provided', () => {
|
|
132
|
+
(useAuth as jest.Mock).mockReturnValue({
|
|
133
|
+
impersonator: { email: 'admin@example.com' },
|
|
134
|
+
user: { id: '123', email: 'user@example.com' },
|
|
135
|
+
organizationId: null,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
render(<Impersonation />);
|
|
139
|
+
expect(getOrganizationAction).not.toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should not call getOrganizationAction when impersonator is not present', () => {
|
|
143
|
+
(useAuth as jest.Mock).mockReturnValue({
|
|
144
|
+
impersonator: null,
|
|
145
|
+
user: { id: '123', email: 'user@example.com' },
|
|
146
|
+
organizationId: 'org_123',
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
render(<Impersonation />);
|
|
150
|
+
expect(getOrganizationAction).not.toHaveBeenCalled();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should not call getOrganizationAction when user is not present', () => {
|
|
154
|
+
(useAuth as jest.Mock).mockReturnValue({
|
|
155
|
+
impersonator: { email: 'admin@example.com' },
|
|
156
|
+
user: null,
|
|
157
|
+
organizationId: 'org_123',
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
render(<Impersonation />);
|
|
161
|
+
expect(getOrganizationAction).not.toHaveBeenCalled();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should not call getOrganizationAction again when organization is already loaded with same ID', async () => {
|
|
165
|
+
const mockOrg = {
|
|
166
|
+
id: 'org_123',
|
|
167
|
+
name: 'Test Org',
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
(getOrganizationAction as jest.Mock).mockResolvedValue(mockOrg);
|
|
171
|
+
|
|
172
|
+
const { rerender } = await act(async () => {
|
|
173
|
+
(useAuth as jest.Mock).mockReturnValue({
|
|
174
|
+
impersonator: { email: 'admin@example.com' },
|
|
175
|
+
user: { id: '123', email: 'user@example.com' },
|
|
176
|
+
organizationId: 'org_123',
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return render(<Impersonation />);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Wait for the initial call to complete
|
|
183
|
+
await act(async () => {
|
|
184
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
expect(getOrganizationAction).toHaveBeenCalledTimes(1);
|
|
188
|
+
|
|
189
|
+
// Rerender with the same organizationId
|
|
190
|
+
await act(async () => {
|
|
191
|
+
(useAuth as jest.Mock).mockReturnValue({
|
|
192
|
+
impersonator: { email: 'admin@example.com' },
|
|
193
|
+
user: { id: '123', email: 'user@example.com' },
|
|
194
|
+
organizationId: 'org_123',
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
rerender(<Impersonation />);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Should still be called only once
|
|
201
|
+
expect(getOrganizationAction).toHaveBeenCalledTimes(1);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should call getOrganizationAction again when organizationId changes', async () => {
|
|
205
|
+
const mockOrg1 = {
|
|
206
|
+
id: 'org_123',
|
|
207
|
+
name: 'Test Org 1',
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const mockOrg2 = {
|
|
211
|
+
id: 'org_456',
|
|
212
|
+
name: 'Test Org 2',
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
(getOrganizationAction as jest.Mock).mockResolvedValueOnce(mockOrg1).mockResolvedValueOnce(mockOrg2);
|
|
216
|
+
|
|
217
|
+
const { rerender } = await act(async () => {
|
|
218
|
+
(useAuth as jest.Mock).mockReturnValue({
|
|
219
|
+
impersonator: { email: 'admin@example.com' },
|
|
220
|
+
user: { id: '123', email: 'user@example.com' },
|
|
221
|
+
organizationId: 'org_123',
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return render(<Impersonation />);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Wait for the initial call to complete
|
|
228
|
+
await act(async () => {
|
|
229
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(getOrganizationAction).toHaveBeenCalledTimes(1);
|
|
233
|
+
expect(getOrganizationAction).toHaveBeenCalledWith('org_123');
|
|
234
|
+
|
|
235
|
+
// Rerender with a different organizationId
|
|
236
|
+
await act(async () => {
|
|
237
|
+
(useAuth as jest.Mock).mockReturnValue({
|
|
238
|
+
impersonator: { email: 'admin@example.com' },
|
|
239
|
+
user: { id: '123', email: 'user@example.com' },
|
|
240
|
+
organizationId: 'org_456',
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
rerender(<Impersonation />);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Should be called again with the new ID
|
|
247
|
+
expect(getOrganizationAction).toHaveBeenCalledTimes(2);
|
|
248
|
+
expect(getOrganizationAction).toHaveBeenCalledWith('org_456');
|
|
133
249
|
});
|
|
134
250
|
});
|
|
@@ -9,19 +9,21 @@ import { useAuth } from './authkit-provider.js';
|
|
|
9
9
|
|
|
10
10
|
interface ImpersonationProps extends React.ComponentPropsWithoutRef<'div'> {
|
|
11
11
|
side?: 'top' | 'bottom';
|
|
12
|
+
returnTo?: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export function Impersonation({ side = 'bottom', ...props }: ImpersonationProps) {
|
|
15
|
-
const { user, impersonator, organizationId
|
|
15
|
+
export function Impersonation({ side = 'bottom', returnTo, ...props }: ImpersonationProps) {
|
|
16
|
+
const { user, impersonator, organizationId } = useAuth();
|
|
16
17
|
|
|
17
18
|
const [organization, setOrganization] = React.useState<Organization | null>(null);
|
|
18
19
|
|
|
19
20
|
React.useEffect(() => {
|
|
20
|
-
if (!organizationId) return;
|
|
21
|
+
if (!organizationId || !impersonator || !user) return;
|
|
22
|
+
if (organization && organization.id === organizationId) return;
|
|
21
23
|
getOrganizationAction(organizationId).then(setOrganization);
|
|
22
|
-
}, [organizationId]);
|
|
24
|
+
}, [organizationId, impersonator, user]);
|
|
23
25
|
|
|
24
|
-
if (
|
|
26
|
+
if (!impersonator || !user) return null;
|
|
25
27
|
|
|
26
28
|
return (
|
|
27
29
|
<div
|
|
@@ -78,7 +80,7 @@ export function Impersonation({ side = 'bottom', ...props }: ImpersonationProps)
|
|
|
78
80
|
<form
|
|
79
81
|
onSubmit={async (event) => {
|
|
80
82
|
event.preventDefault();
|
|
81
|
-
await handleSignOutAction();
|
|
83
|
+
await handleSignOutAction({ returnTo });
|
|
82
84
|
}}
|
|
83
85
|
style={{
|
|
84
86
|
display: 'flex',
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { getSignInUrl, getSignUpUrl, signOut, switchToOrganization } from './aut
|
|
|
2
2
|
import { handleAuth } from './authkit-callback-route.js';
|
|
3
3
|
import { authkit, authkitMiddleware } from './middleware.js';
|
|
4
4
|
import { getTokenClaims, refreshSession, saveSession, withAuth } from './session.js';
|
|
5
|
+
import { validateApiKey } from './validate-api-key.js';
|
|
5
6
|
import { getWorkOS } from './workos.js';
|
|
6
7
|
|
|
7
8
|
export * from './interfaces.js';
|
|
@@ -19,4 +20,5 @@ export {
|
|
|
19
20
|
switchToOrganization,
|
|
20
21
|
withAuth,
|
|
21
22
|
getTokenClaims,
|
|
23
|
+
validateApiKey,
|
|
22
24
|
};
|
package/src/test-helpers.ts
CHANGED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
import { validateApiKey } from './validate-api-key.js';
|
|
4
|
+
import { getWorkOS } from './workos.js';
|
|
5
|
+
|
|
6
|
+
// These are mocked in jest.setup.ts
|
|
7
|
+
import { headers } from 'next/headers';
|
|
8
|
+
|
|
9
|
+
const workos = getWorkOS();
|
|
10
|
+
|
|
11
|
+
describe('validate-api-key.ts', () => {
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
// Clear all mocks between tests
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
|
|
16
|
+
const nextHeaders = await headers();
|
|
17
|
+
// @ts-expect-error - _reset is part of the mock
|
|
18
|
+
nextHeaders._reset();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('validateApiKey', () => {
|
|
22
|
+
it('should return valid API key when Bearer token is present and valid', async () => {
|
|
23
|
+
const mockApiKeyResponse = {
|
|
24
|
+
apiKey: {
|
|
25
|
+
id: 'api_key_123',
|
|
26
|
+
object: 'api_key' as const,
|
|
27
|
+
name: 'Test API Key',
|
|
28
|
+
obfuscatedValue: 'sk_…7890',
|
|
29
|
+
createdAt: '2024-01-01T00:00:00Z',
|
|
30
|
+
updatedAt: '2024-01-01T00:00:00Z',
|
|
31
|
+
lastUsedAt: '2024-01-01T00:00:00Z',
|
|
32
|
+
permissions: [],
|
|
33
|
+
owner: { type: 'organization' as const, id: 'org_123' },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
jest.spyOn(workos.apiKeys, 'validateApiKey').mockResolvedValue(mockApiKeyResponse);
|
|
38
|
+
|
|
39
|
+
const nextHeaders = await headers();
|
|
40
|
+
nextHeaders.set('authorization', 'Bearer sk_test_1234567890');
|
|
41
|
+
|
|
42
|
+
const result = await validateApiKey();
|
|
43
|
+
|
|
44
|
+
expect(workos.apiKeys.validateApiKey).toHaveBeenCalledWith({
|
|
45
|
+
value: 'sk_test_1234567890',
|
|
46
|
+
});
|
|
47
|
+
expect(result).toEqual(mockApiKeyResponse);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return { apiKey: null } when no authorization header is present', async () => {
|
|
51
|
+
// Don't set any authorization header
|
|
52
|
+
const result = await validateApiKey();
|
|
53
|
+
|
|
54
|
+
expect(workos.apiKeys.validateApiKey).not.toHaveBeenCalled();
|
|
55
|
+
expect(result).toEqual({ apiKey: null });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return { apiKey: null } when authorization header is empty', async () => {
|
|
59
|
+
const nextHeaders = await headers();
|
|
60
|
+
nextHeaders.set('authorization', '');
|
|
61
|
+
|
|
62
|
+
const result = await validateApiKey();
|
|
63
|
+
|
|
64
|
+
expect(workos.apiKeys.validateApiKey).not.toHaveBeenCalled();
|
|
65
|
+
expect(result).toEqual({ apiKey: null });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return { apiKey: null } when authorization header does not start with Bearer', async () => {
|
|
69
|
+
const nextHeaders = await headers();
|
|
70
|
+
nextHeaders.set('authorization', 'Basic dXNlcjpwYXNz');
|
|
71
|
+
|
|
72
|
+
const result = await validateApiKey();
|
|
73
|
+
|
|
74
|
+
expect(workos.apiKeys.validateApiKey).not.toHaveBeenCalled();
|
|
75
|
+
expect(result).toEqual({ apiKey: null });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should return { apiKey: null } when Bearer token is missing', async () => {
|
|
79
|
+
const nextHeaders = await headers();
|
|
80
|
+
nextHeaders.set('authorization', 'Bearer');
|
|
81
|
+
|
|
82
|
+
const result = await validateApiKey();
|
|
83
|
+
|
|
84
|
+
expect(workos.apiKeys.validateApiKey).not.toHaveBeenCalled();
|
|
85
|
+
expect(result).toEqual({ apiKey: null });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should return { apiKey: null } when Bearer token is only whitespace', async () => {
|
|
89
|
+
const nextHeaders = await headers();
|
|
90
|
+
nextHeaders.set('authorization', 'Bearer ');
|
|
91
|
+
|
|
92
|
+
const result = await validateApiKey();
|
|
93
|
+
|
|
94
|
+
expect(workos.apiKeys.validateApiKey).not.toHaveBeenCalled();
|
|
95
|
+
expect(result).toEqual({ apiKey: null });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should return { apiKey: null } when WorkOS validation fails', async () => {
|
|
99
|
+
const mockResponse = { apiKey: null };
|
|
100
|
+
jest.spyOn(workos.apiKeys, 'validateApiKey').mockResolvedValue(mockResponse);
|
|
101
|
+
|
|
102
|
+
const nextHeaders = await headers();
|
|
103
|
+
nextHeaders.set('authorization', 'Bearer invalid_key');
|
|
104
|
+
|
|
105
|
+
const result = await validateApiKey();
|
|
106
|
+
|
|
107
|
+
expect(workos.apiKeys.validateApiKey).toHaveBeenCalledWith({
|
|
108
|
+
value: 'invalid_key',
|
|
109
|
+
});
|
|
110
|
+
expect(result).toEqual({ apiKey: null });
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { getWorkOS } from './workos.js';
|
|
4
|
+
import { headers } from 'next/headers';
|
|
5
|
+
|
|
6
|
+
export async function validateApiKey() {
|
|
7
|
+
const headersList = await headers();
|
|
8
|
+
const authorizationHeader = headersList.get('authorization');
|
|
9
|
+
if (!authorizationHeader) {
|
|
10
|
+
return { apiKey: null };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const value = authorizationHeader.match(/Bearer\s+(.*)/i)?.[1];
|
|
14
|
+
if (!value) {
|
|
15
|
+
return { apiKey: null };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return getWorkOS().apiKeys.validateApiKey({ value });
|
|
19
|
+
}
|
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.
|
|
5
|
+
export const VERSION = '2.11.0';
|
|
6
6
|
|
|
7
7
|
const options = {
|
|
8
8
|
apiHostname: WORKOS_API_HOSTNAME,
|