@workos-inc/authkit-nextjs 4.0.1 → 4.1.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 CHANGED
@@ -452,14 +452,52 @@ export default function MyComponent() {
452
452
  }
453
453
  ```
454
454
 
455
- ### Get the enabled flags for the logged in user
455
+ ### Evaluate feature flags
456
456
 
457
- For situations where you need access to the authenticated user's currently active feature flags, use `withAuth` to retrieve the flags from the WorkOS session.
457
+ There are two ways to evaluate feature flags with AuthKit Next.js:
458
+
459
+ - Use the `feature_flags` access token claim when you want a simple session-scoped list of enabled flags for the signed-in user.
460
+ - Use the Feature Flags runtime client when you want server-side evaluation without storing every active flag in the user's session cookie.
461
+
462
+ #### Option 1: Use the `feature_flags` claim
463
+
464
+ For situations where you need access to the authenticated user's currently active feature flags and your environment includes the `feature_flags` access token claim, use `withAuth` to retrieve the flags from the WorkOS session.
458
465
 
459
466
  ```jsx
460
467
  const { featureFlags } = await withAuth();
461
468
  ```
462
469
 
470
+ This is convenient for small flag sets because the flags are available with the user's session. Flag changes appear the next time the user logs in or the session is refreshed.
471
+
472
+ #### Option 2: Use the runtime client
473
+
474
+ Use the runtime client when your application has many feature flags, when the `feature_flags` claim makes the access token too large, or when you need server-side flag evaluation that stays in sync independently of the user's session. The runtime client keeps flag configuration in memory and syncs changes in the background, so create one shared instance per server process rather than one client per request.
475
+
476
+ ```tsx
477
+ import { getFeatureFlagsRuntimeClient, withAuth } from '@workos-inc/authkit-nextjs';
478
+
479
+ const featureFlags = getFeatureFlagsRuntimeClient();
480
+
481
+ export default async function DashboardPage() {
482
+ const { user, organizationId } = await withAuth({ ensureSignedIn: true });
483
+
484
+ try {
485
+ await featureFlags.waitUntilReady({ timeoutMs: 5000 });
486
+ } catch (error) {
487
+ console.error('Feature flags client failed to initialize:', error);
488
+ }
489
+
490
+ const enabled = featureFlags.isEnabled('advanced-analytics', {
491
+ userId: user.id,
492
+ organizationId,
493
+ });
494
+
495
+ return enabled ? <AdvancedAnalytics /> : <BasicAnalytics />;
496
+ }
497
+ ```
498
+
499
+ The `getFeatureFlagsRuntimeClient` helper returns the same runtime client for every call in the current server process. Options passed to `getFeatureFlagsRuntimeClient(options)` are only used when the client is created for the first time.
500
+
463
501
  ### Requiring auth
464
502
 
465
503
  For pages where a signed-in user is mandatory, you can use the `ensureSignedIn` option:
@@ -0,0 +1,11 @@
1
+ import { lazy } from './utils.js';
2
+ import { getWorkOS } from './workos.js';
3
+ /**
4
+ * Returns a shared WorkOS Feature Flags runtime client.
5
+ *
6
+ * The runtime client keeps feature flag state in sync in the background, so it
7
+ * should be created once per server process instead of once per request.
8
+ * Options are only used when the client is created for the first time.
9
+ */
10
+ export const getFeatureFlagsRuntimeClient = lazy((options) => getWorkOS().featureFlags.createRuntimeClient(options));
11
+ //# sourceMappingURL=feature-flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-flags.js","sourceRoot":"","sources":["../../src/feature-flags.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAC9C,CAAC,OAA8B,EAA6B,EAAE,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,mBAAmB,CAAC,OAAO,CAAC,CACrH,CAAC"}
package/dist/esm/index.js CHANGED
@@ -5,7 +5,8 @@ import { authkit, authkitMiddleware, authkitProxy } from './middleware.js';
5
5
  export { applyResponseHeaders, handleAuthkitHeaders, handleAuthkitProxy, partitionAuthkitHeaders, isAuthkitRequestHeader, AUTHKIT_REQUEST_HEADERS, } from './middleware-helpers.js';
6
6
  import { getTokenClaims, refreshSession, saveSession, withAuth } from './session.js';
7
7
  import { validateApiKey } from './validate-api-key.js';
8
+ import { getFeatureFlagsRuntimeClient } from './feature-flags.js';
8
9
  import { getWorkOS } from './workos.js';
9
10
  export * from './interfaces.js';
10
- export { AuthKitError, TokenRefreshError, authkit, authkitMiddleware, authkitProxy, getSignInUrl, getSignUpUrl, getTokenClaims, getWorkOS, handleAuth, refreshSession, saveSession, signOut, switchToOrganization, validateApiKey, withAuth, };
11
+ export { AuthKitError, TokenRefreshError, authkit, authkitMiddleware, authkitProxy, getSignInUrl, getSignUpUrl, getFeatureFlagsRuntimeClient, getTokenClaims, getWorkOS, handleAuth, refreshSession, saveSession, signOut, switchToOrganization, validateApiKey, withAuth, };
11
12
  //# sourceMappingURL=index.js.map
@@ -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,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,GAKxB,MAAM,yBAAyB,CAAC;AACjC,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,YAAY,EACZ,iBAAiB,EACjB,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,SAAS,EACT,UAAU,EACV,cAAc,EACd,WAAW,EACX,OAAO,EACP,oBAAoB,EACpB,cAAc,EACd,QAAQ,GACT,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,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,GAKxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,cAAc,iBAAiB,CAAC;AAEhC,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,4BAA4B,EAC5B,cAAc,EACd,SAAS,EACT,UAAU,EACV,cAAc,EACd,WAAW,EACX,OAAO,EACP,oBAAoB,EACpB,cAAc,EACd,QAAQ,GACT,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { FeatureFlagsRuntimeClient, RuntimeClientOptions } from '@workos-inc/node';
2
+ /**
3
+ * Returns a shared WorkOS Feature Flags runtime client.
4
+ *
5
+ * The runtime client keeps feature flag state in sync in the background, so it
6
+ * should be created once per server process instead of once per request.
7
+ * Options are only used when the client is created for the first time.
8
+ */
9
+ export declare const getFeatureFlagsRuntimeClient: (options?: RuntimeClientOptions | undefined) => FeatureFlagsRuntimeClient;
@@ -5,6 +5,7 @@ import { authkit, authkitMiddleware, authkitProxy } from './middleware.js';
5
5
  export { applyResponseHeaders, handleAuthkitHeaders, handleAuthkitProxy, partitionAuthkitHeaders, isAuthkitRequestHeader, AUTHKIT_REQUEST_HEADERS, type AuthkitHeadersResult, type AuthkitRedirectStatus, type AuthkitRequestHeader, type HandleAuthkitHeadersOptions, } from './middleware-helpers.js';
6
6
  import { getTokenClaims, refreshSession, saveSession, withAuth } from './session.js';
7
7
  import { validateApiKey } from './validate-api-key.js';
8
+ import { getFeatureFlagsRuntimeClient } from './feature-flags.js';
8
9
  import { getWorkOS } from './workos.js';
9
10
  export * from './interfaces.js';
10
- export { AuthKitError, TokenRefreshError, authkit, authkitMiddleware, authkitProxy, getSignInUrl, getSignUpUrl, getTokenClaims, getWorkOS, handleAuth, refreshSession, saveSession, signOut, switchToOrganization, validateApiKey, withAuth, };
11
+ export { AuthKitError, TokenRefreshError, authkit, authkitMiddleware, authkitProxy, getSignInUrl, getSignUpUrl, getFeatureFlagsRuntimeClient, getTokenClaims, getWorkOS, handleAuth, refreshSession, saveSession, signOut, switchToOrganization, validateApiKey, withAuth, };
@@ -17,4 +17,4 @@ export declare function errorResponseWithFallback(errorBody: {
17
17
  * @param fn - The function to be called once.
18
18
  * @returns A function that can only be called once.
19
19
  */
20
- export declare function lazy<T>(fn: () => T): () => T;
20
+ export declare function lazy<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => TResult): (...args: TArgs) => TResult;
package/dist/esm/utils.js CHANGED
@@ -36,9 +36,9 @@ export function errorResponseWithFallback(errorBody) {
36
36
  export function lazy(fn) {
37
37
  let called = false;
38
38
  let result;
39
- return () => {
39
+ return (...args) => {
40
40
  if (!called) {
41
- result = fn();
41
+ result = fn(...args);
42
42
  called = true;
43
43
  }
44
44
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAgB;IACxD,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,yDAAyD,CAAC,CAAC;IACxF,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,WAAmB,EAAE,OAAiB;IACzE,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;IAClE,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAExC,mEAAmE;IACnE,iCAAiC;IACjC,OAAO,YAAY,EAAE,QAAQ;QAC3B,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,CAAC;QACjD,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,SAA8D;IACtG,mEAAmE;IACnE,iCAAiC;IACjC,OAAO,YAAY,EAAE,IAAI;QACvB,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QAC/C,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;YACtC,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CAAC,CAAC;AACT,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,IAAI,CAAI,EAAW;IACjC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,MAAS,CAAC;IACd,OAAO,GAAG,EAAE;QACV,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,EAAE,EAAE,CAAC;YACd,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAgB;IACxD,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,yDAAyD,CAAC,CAAC;IACxF,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,WAAmB,EAAE,OAAiB;IACzE,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;IAClE,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAExC,mEAAmE;IACnE,iCAAiC;IACjC,OAAO,YAAY,EAAE,QAAQ;QAC3B,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,CAAC;QACjD,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,SAA8D;IACtG,mEAAmE;IACnE,iCAAiC;IACjC,OAAO,YAAY,EAAE,IAAI;QACvB,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QAC/C,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;YACtC,MAAM,EAAE,GAAG;YACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CAAC,CAAC;AACT,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,IAAI,CAAmC,EAA+B;IACpF,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,MAAe,CAAC;IACpB,OAAO,CAAC,GAAG,IAAW,EAAE,EAAE;QACxB,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YACrB,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workos-inc/authkit-nextjs",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Authentication and session helpers for using WorkOS & AuthKit with Next.js",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -0,0 +1,28 @@
1
+ import { getWorkOS } from './workos.js';
2
+
3
+ describe('feature flags', () => {
4
+ afterEach(() => {
5
+ vi.restoreAllMocks();
6
+ vi.resetModules();
7
+ });
8
+
9
+ it('memoizes the feature flags runtime client', async () => {
10
+ const runtimeClient = {
11
+ close: vi.fn(),
12
+ getAllFlags: vi.fn(),
13
+ getFlag: vi.fn(),
14
+ getStats: vi.fn(),
15
+ isEnabled: vi.fn(),
16
+ waitUntilReady: vi.fn(),
17
+ };
18
+ const createRuntimeClient = vi
19
+ .spyOn(getWorkOS().featureFlags, 'createRuntimeClient')
20
+ .mockReturnValue(runtimeClient as never);
21
+ const { getFeatureFlagsRuntimeClient } = await import('./feature-flags.js');
22
+
23
+ expect(getFeatureFlagsRuntimeClient({ pollingIntervalMs: 5000 })).toBe(runtimeClient);
24
+ expect(getFeatureFlagsRuntimeClient({ pollingIntervalMs: 30000 })).toBe(runtimeClient);
25
+ expect(createRuntimeClient).toHaveBeenCalledTimes(1);
26
+ expect(createRuntimeClient).toHaveBeenCalledWith({ pollingIntervalMs: 5000 });
27
+ });
28
+ });
@@ -0,0 +1,14 @@
1
+ import type { FeatureFlagsRuntimeClient, RuntimeClientOptions } from '@workos-inc/node';
2
+ import { lazy } from './utils.js';
3
+ import { getWorkOS } from './workos.js';
4
+
5
+ /**
6
+ * Returns a shared WorkOS Feature Flags runtime client.
7
+ *
8
+ * The runtime client keeps feature flag state in sync in the background, so it
9
+ * should be created once per server process instead of once per request.
10
+ * Options are only used when the client is created for the first time.
11
+ */
12
+ export const getFeatureFlagsRuntimeClient = lazy(
13
+ (options?: RuntimeClientOptions): FeatureFlagsRuntimeClient => getWorkOS().featureFlags.createRuntimeClient(options),
14
+ );
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ export {
16
16
  } from './middleware-helpers.js';
17
17
  import { getTokenClaims, refreshSession, saveSession, withAuth } from './session.js';
18
18
  import { validateApiKey } from './validate-api-key.js';
19
+ import { getFeatureFlagsRuntimeClient } from './feature-flags.js';
19
20
  import { getWorkOS } from './workos.js';
20
21
 
21
22
  export * from './interfaces.js';
@@ -28,6 +29,7 @@ export {
28
29
  authkitProxy,
29
30
  getSignInUrl,
30
31
  getSignUpUrl,
32
+ getFeatureFlagsRuntimeClient,
31
33
  getTokenClaims,
32
34
  getWorkOS,
33
35
  handleAuth,
package/src/utils.ts CHANGED
@@ -38,12 +38,12 @@ export function errorResponseWithFallback(errorBody: { error: { message: string;
38
38
  * @param fn - The function to be called once.
39
39
  * @returns A function that can only be called once.
40
40
  */
41
- export function lazy<T>(fn: () => T): () => T {
41
+ export function lazy<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => TResult): (...args: TArgs) => TResult {
42
42
  let called = false;
43
- let result: T;
44
- return () => {
43
+ let result: TResult;
44
+ return (...args: TArgs) => {
45
45
  if (!called) {
46
- result = fn();
46
+ result = fn(...args);
47
47
  called = true;
48
48
  }
49
49
  return result;