@memberjunction/auth-providers 5.42.0 → 5.44.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @memberjunction/auth-providers@5.42.0 build
2
+ > @memberjunction/auth-providers@5.44.0 build
3
3
  > tsc && tsc-alias -f
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @memberjunction/auth-providers
2
2
 
3
+ ## 5.44.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2f9b863: Add WorkOS (AuthKit) as a first-class authentication provider — end to end, server-side JWT validation and browser-side login. A deployment can now set `type: 'workos'` (server) / `AUTH_TYPE: 'workos'` (browser) and authenticate users through WorkOS just like Auth0, Okta, MSAL, Cognito, or Google.
8
+ - **Server** (`@memberjunction/auth-providers`): `WorkOSProvider` extends `BaseAuthProvider`, registered via `@RegisterClass(BaseAuthProvider, 'workos')`. Maps AuthKit JWT claims to `AuthUserInfo` (with graceful fallbacks) and validates `clientId`; issuer matching, JWKS caching, and retry/backoff are inherited. Wired into `AuthProviderFactory`.
9
+ - **Client** (`@memberjunction/ng-auth-services`): `MJWorkOSProvider` extends `MJAuthBase`, registered via `@RegisterClass(MJAuthBase, 'workos')`. Wraps the `@workos-inc/authkit-js` SDK (`createClient`/`signIn`/`signOut`/`getUser`/`getAccessToken`) behind the standardized provider contract with semantic error classification.
10
+ - **Core** (`@memberjunction/core`): `AUTH_PROVIDER_TYPES` gains `WORKOS: 'workos'`.
11
+ - **Env typing** (`@memberjunction/ng-bootstrap`): the `AUTH_TYPE` union gains `'workos'`, plus `WORKOS_CLIENTID` / `WORKOS_REDIRECT_URI` / `WORKOS_API_HOSTNAME` / `WORKOS_DEV_MODE` keys.
12
+
13
+ Includes a full end-to-end integration guide (`packages/AuthProviders/WORKOS.md`) covering the two WorkOS-specific gotchas: the required `email` JWT Template (AuthKit access tokens omit email, which MJ keys users on) and matching the enforced `aud` claim. Additive only.
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [5396d90]
18
+ - Updated dependencies [7279819]
19
+ - Updated dependencies [d44e430]
20
+ - Updated dependencies [6f74b17]
21
+ - Updated dependencies [2f9b863]
22
+ - @memberjunction/core@5.44.0
23
+ - @memberjunction/global@5.44.0
24
+
25
+ ## 5.43.0
26
+
27
+ ### Patch Changes
28
+
29
+ - Updated dependencies [40eb4e0]
30
+ - Updated dependencies [9f6aa87]
31
+ - Updated dependencies [ad8d8f1]
32
+ - Updated dependencies [a4cdfb0]
33
+ - @memberjunction/core@5.43.0
34
+ - @memberjunction/global@5.43.0
35
+
3
36
  ## 5.42.0
4
37
 
5
38
  ### Minor Changes
package/README.md CHANGED
@@ -9,7 +9,9 @@ This package gives the MJ server (and any other Node.js consumer) a uniform, plu
9
9
  - Extract a normalized `AuthUserInfo` from provider-specific claim shapes
10
10
  - Register additional providers at runtime via the MJ class-factory system
11
11
 
12
- It ships with first-class support for **Auth0**, **Microsoft Entra ID / MSAL**, **Okta**, **AWS Cognito**, and **Google Identity Platform**, and is the extension point used to plug custom providers into [`@memberjunction/server`](../MJServer/README.md).
12
+ It ships with first-class support for **Auth0**, **Microsoft Entra ID / MSAL**, **Okta**, **AWS Cognito**, **Google Identity Platform**, and **WorkOS (AuthKit)**, and is the extension point used to plug custom providers into [`@memberjunction/server`](../MJServer/README.md).
13
+
14
+ > **Integrating WorkOS?** See the dedicated end-to-end guide: **[WORKOS.md](WORKOS.md)** — it covers the browser + server setup and the two WorkOS-specific gotchas (the required `email` JWT Template and matching the `aud` claim).
13
15
 
14
16
  ## When to use this package
15
17
 
@@ -67,6 +69,7 @@ This package is a Node.js / server-side package. It depends on:
67
69
  │ ├─ @RegisterClass(BaseAuthProvider, 'okta') │
68
70
  │ ├─ @RegisterClass(BaseAuthProvider, 'cognito') │
69
71
  │ ├─ @RegisterClass(BaseAuthProvider, 'google') │
72
+ │ ├─ @RegisterClass(BaseAuthProvider, 'workos') │
70
73
  │ └─ @RegisterClass(BaseAuthProvider, 'your-custom') │
71
74
  └──────────────────────────────────────────────────────────────────┘
72
75
  ```
@@ -95,6 +98,7 @@ Each built-in provider is registered with the MJ class factory under a lowercase
95
98
  | `okta` | `OktaProvider` | `clientId`, `domain` |
96
99
  | `cognito` | `CognitoProvider` | `clientId`, `region`, `userPoolId` |
97
100
  | `google` | `GoogleProvider` | `clientId` |
101
+ | `workos` | `WorkOSProvider` | `clientId` (see [WORKOS.md](WORKOS.md) for the required `email` JWT Template + `aud`) |
98
102
 
99
103
  Every provider also requires the base fields: `name`, `type`, `issuer`, `audience`, `jwksUri`. See [`AuthProviderConfig`](../MJCore/src/generic/authTypes.ts) for the full shape.
100
104
 
@@ -124,11 +128,23 @@ module.exports = {
124
128
  audience: 'https://api.example.com',
125
129
  jwksUri: 'https://tenant.auth0.com/.well-known/jwks.json',
126
130
  },
131
+ {
132
+ name: 'workos-prod',
133
+ type: 'workos',
134
+ clientId: process.env.WORKOS_CLIENT_ID, // client_01H...
135
+ issuer: `https://api.workos.com/user_management/${process.env.WORKOS_CLIENT_ID}`,
136
+ jwksUri: `https://api.workos.com/sso/jwks/${process.env.WORKOS_CLIENT_ID}`,
137
+ audience: process.env.WORKOS_CLIENT_ID, // must match the token's `aud` — see WORKOS.md
138
+ },
127
139
  // ...add more providers here
128
140
  ],
129
141
  };
130
142
  ```
131
143
 
144
+ > **WorkOS needs two extra steps** beyond this config — an `email` JWT Template (its access tokens
145
+ > omit email, which MJ keys users on) and matching the `aud` claim. The full walkthrough is in
146
+ > **[WORKOS.md](WORKOS.md)**.
147
+
132
148
  > **Multiple audiences on the same issuer.** When two MJ apps share an Auth0 domain but use different client IDs, register both as separate entries — `AuthProviderFactory.getAllByIssuer()` returns every match so the validator can try each audience.
133
149
 
134
150
  ## Usage
package/WORKOS.md ADDED
@@ -0,0 +1,256 @@
1
+ # WorkOS (AuthKit) Integration Guide
2
+
3
+ This guide walks you through wiring **WorkOS AuthKit** into MemberJunction end to end —
4
+ browser login and server-side token validation — so users sign in through WorkOS exactly like
5
+ they would through Auth0, Okta, MSAL, Cognito, or Google.
6
+
7
+ WorkOS is consumed as an OIDC/JWT identity provider. MemberJunction does **not** replace WorkOS;
8
+ WorkOS handles authentication (hosted login, SSO, MFA, Directory Sync) and MJ handles everything
9
+ after the user is identified (entities, permissions, agents, audit).
10
+
11
+ > **TL;DR — the one thing that trips everyone up:** a WorkOS AuthKit **access token does not
12
+ > include the user's email by default**, and MemberJunction resolves users by email. You must add
13
+ > an `email` claim with a [WorkOS **JWT Template**](#step-3--add-an-email-claim-jwt-template).
14
+ > Skip this and every login will fail user lookup on the server. See
15
+ > [Step 3](#step-3--add-an-email-claim-jwt-template).
16
+
17
+ ---
18
+
19
+ ## Table of contents
20
+
21
+ 1. [How it fits together](#how-it-fits-together)
22
+ 2. [What's in this package](#whats-in-this-package)
23
+ 3. [Prerequisites](#prerequisites)
24
+ 4. [Step 1 — Configure the WorkOS dashboard](#step-1--configure-the-workos-dashboard)
25
+ 5. [Step 2 — Configure the MJ server (`mj.config.cjs`)](#step-2--configure-the-mj-server-mjconfigcjs)
26
+ 6. [Step 3 — Add an `email` claim (JWT Template)](#step-3--add-an-email-claim-jwt-template)
27
+ 7. [Step 4 — Configure the browser (MJExplorer)](#step-4--configure-the-browser-mjexplorer)
28
+ 8. [Token claims reference](#token-claims-reference)
29
+ 9. [How validation works under the hood](#how-validation-works-under-the-hood)
30
+ 10. [Troubleshooting](#troubleshooting)
31
+ 11. [Scope & non-goals](#scope--non-goals)
32
+
33
+ ---
34
+
35
+ ## How it fits together
36
+
37
+ ```
38
+ ┌─ Browser (MJExplorer) ─────────────────┐ ┌─ MJ GraphQL Server ─────────────────────┐
39
+ │ │ │ │
40
+ │ MJWorkOSProvider (extends MJAuthBase) │ │ WorkOSProvider (extends BaseAuthProvider)│
41
+ │ @RegisterClass(MJAuthBase,'workos') │ │ @RegisterClass(BaseAuthProvider,'workos')│
42
+ │ │ │ │ ▲ │
43
+ │ @workos-inc/authkit-js │ │ │ jwks-rsa + jsonwebtoken │
44
+ │ │ signIn → hosted login │ │ │ (verify signature, iss, aud) │
45
+ │ │ getUser() (email for display)│ │ │ │
46
+ │ └─ getAccessToken() ── Bearer ───┼────────┼──▶ AuthProviderFactory.getByIssuer(iss) │
47
+ │ │ │ → WorkOSProvider.extractUserInfo() │
48
+ │ │ │ → verifyUserRecord(email) │
49
+ └─────────────────────────────────────────┘ └──────────────────────────────────────────┘
50
+ ```
51
+
52
+ Both ends register under the same lowercase key, `workos`, through MJ's class factory. The server
53
+ trusts the issuer/JWKS to verify the signature; the browser SDK manages the session and silent
54
+ token refresh.
55
+
56
+ ## What's in this package
57
+
58
+ | Side | Class | File | Registers as |
59
+ |---|---|---|---|
60
+ | Server | `WorkOSProvider` | [`src/providers/WorkOSProvider.ts`](src/providers/WorkOSProvider.ts) | `@RegisterClass(BaseAuthProvider, 'workos')` |
61
+ | Browser | `MJWorkOSProvider` | `@memberjunction/ng-auth-services` → `src/lib/providers/mjexplorer-workos-provider.service.ts` | `@RegisterClass(MJAuthBase, 'workos')` |
62
+
63
+ The server provider is registered automatically (it's imported by
64
+ [`AuthProviderFactory.ts`](src/AuthProviderFactory.ts)); you only need to add a config entry.
65
+
66
+ ## Prerequisites
67
+
68
+ - A [WorkOS](https://workos.com) account with **AuthKit / User Management** enabled.
69
+ - Your **Client ID** (looks like `client_01H...`) from the WorkOS dashboard.
70
+ - The browser app must depend on `@workos-inc/authkit-js` (already declared as a peer dependency
71
+ of `@memberjunction/ng-auth-services`, and a direct dependency of MJExplorer).
72
+
73
+ ---
74
+
75
+ ## Step 1 — Configure the WorkOS dashboard
76
+
77
+ 1. In the WorkOS dashboard, go to **Authentication → AuthKit** and enable it.
78
+ 2. Under **Redirects**, add your app's redirect URI (e.g. `http://localhost:4200` for local
79
+ development, plus your production origin). This is where WorkOS returns the user after login.
80
+ 3. Set a **Logout URI** (the page WorkOS returns to after sign-out — typically the same origin).
81
+ 4. Note your **Client ID**. You'll use it in three places: the server `issuer`/`jwksUri`, the
82
+ server `clientId`, and the browser `WORKOS_CLIENTID`.
83
+
84
+ ## Step 2 — Configure the MJ server (`mj.config.cjs`)
85
+
86
+ Add a `workos` entry to `authProviders`. The `issuer` and `jwksUri` are derived from your Client
87
+ ID:
88
+
89
+ ```javascript
90
+ // mj.config.cjs
91
+ module.exports = {
92
+ authProviders: [
93
+ {
94
+ name: 'workos-prod',
95
+ type: 'workos',
96
+ clientId: process.env.WORKOS_CLIENT_ID, // client_01H...
97
+ issuer: `https://api.workos.com/user_management/${process.env.WORKOS_CLIENT_ID}`,
98
+ jwksUri: `https://api.workos.com/sso/jwks/${process.env.WORKOS_CLIENT_ID}`,
99
+ // The audience MUST match the token's `aud` claim — see the note below.
100
+ audience: process.env.WORKOS_CLIENT_ID,
101
+ },
102
+ // ...other providers can coexist; the factory routes by the token's `iss` claim
103
+ ],
104
+ };
105
+ ```
106
+
107
+ ### Getting `audience` right
108
+
109
+ MemberJunction **always enforces the `aud` claim** during verification. WorkOS sets `aud`
110
+ automatically (you cannot override it via a JWT Template — it's a reserved claim):
111
+
112
+ - **No Resource Indicators configured (default):** WorkOS uses a default audience unique to your
113
+ WorkOS environment. Decode a real access token (e.g. at [jwt.io](https://jwt.io)) and copy the
114
+ exact `aud` value into your `audience` config.
115
+ - **Resource Indicators configured:** the `aud` matches the requested resource — set `audience`
116
+ to that resource identifier.
117
+
118
+ > If `audience` does not match the token's `aud`, verification fails with
119
+ > `jwt audience invalid`. This is the second most common misconfiguration after the missing email
120
+ > claim.
121
+
122
+ ## Step 3 — Add an `email` claim (JWT Template)
123
+
124
+ **This step is required.** A WorkOS AuthKit access token carries identity/session claims
125
+ (`sub`, `sid`, `org_id`, `role`, `permissions`) but **not** the user's email. MemberJunction
126
+ looks the user up by email server-side, so you must add it.
127
+
128
+ In the WorkOS dashboard, go to **Authentication → Sessions → JWT Template** (or
129
+ **JWT Templates**) and add:
130
+
131
+ ```json
132
+ {
133
+ "email": "{{user.email}}",
134
+ "given_name": "{{user.first_name}}",
135
+ "family_name": "{{user.last_name}}"
136
+ }
137
+ ```
138
+
139
+ - `email` is what MJ matches against the `User.Email` column — **required**.
140
+ - `given_name` / `family_name` are optional but recommended: they let MJ
141
+ [auto-provision new users](../MJServer/README.md) with proper names when
142
+ `userHandling.autoCreateNewUsers` is enabled.
143
+ - WorkOS JWT Templates drop any claim whose value renders to `null`, so users missing a first/last
144
+ name simply won't have those claims — the provider handles that gracefully (it falls back to
145
+ splitting a `name` claim, or to the email).
146
+
147
+ > **Why not just read email from the ID token?** The browser SDK's `getUser()` always has the
148
+ > email (used for display), but the **server** only sees the bearer **access token**. The JWT
149
+ > Template is what puts the email into that access token.
150
+
151
+ ## Step 4 — Configure the browser (MJExplorer)
152
+
153
+ Set `AUTH_TYPE` to `workos` and provide the WorkOS environment keys. These flow into the provider
154
+ through `AuthServicesModule.forRoot(environment)`:
155
+
156
+ ```typescript
157
+ // environment.ts
158
+ export const environment = {
159
+ // ...
160
+ AUTH_TYPE: 'workos',
161
+ WORKOS_CLIENTID: 'client_01H...', // required
162
+ WORKOS_REDIRECT_URI: window.location.origin, // optional; defaults to window.location.origin
163
+ WORKOS_API_HOSTNAME: undefined, // optional; only for custom domains / proxies
164
+ WORKOS_DEV_MODE: false, // optional; localStorage session for local dev
165
+ };
166
+ ```
167
+
168
+ | Env key | Required | Purpose |
169
+ |---|---|---|
170
+ | `AUTH_TYPE` | ✅ | Must be `'workos'` to select this provider |
171
+ | `WORKOS_CLIENTID` | ✅ | Your AuthKit Client ID |
172
+ | `WORKOS_REDIRECT_URI` | — | Redirect target after login (default: `window.location.origin`) |
173
+ | `WORKOS_API_HOSTNAME` | — | Override the WorkOS API hostname (custom domains / proxy) |
174
+ | `WORKOS_DEV_MODE` | — | AuthKit dev mode — localStorage-backed session for local development |
175
+
176
+ That's it. On startup, `MJWorkOSProvider.initialize()` constructs the AuthKit client (which also
177
+ processes any pending redirect callback), establishes the session, and the rest of MJ works
178
+ unchanged — `getIdToken()` hands the access token to the GraphQL client as a Bearer token.
179
+
180
+ ---
181
+
182
+ ## Token claims reference
183
+
184
+ A WorkOS AuthKit **access token** (what `getAccessToken()` returns and the server validates),
185
+ **after** adding the recommended JWT Template:
186
+
187
+ ```jsonc
188
+ {
189
+ "iss": "https://api.workos.com/user_management/client_01H...",
190
+ "sub": "user_01HXYZ...", // WorkOS user id → AuthUserInfo.userId
191
+ "sid": "session_01H...", // session id
192
+ "jti": "01H...",
193
+ "aud": "<environment default or resource indicator>", // MJ enforces this
194
+ "org_id": "org_01H...", // present for organization-scoped sessions
195
+ "role": "admin", // organization-membership role
196
+ "permissions": ["posts:read"], // role permissions
197
+ "exp": 1717000000,
198
+ "iat": 1716996400,
199
+
200
+ // ── added by your JWT Template ──
201
+ "email": "ada@example.com", // REQUIRED for MJ user resolution
202
+ "given_name": "Ada",
203
+ "family_name": "Lovelace"
204
+ }
205
+ ```
206
+
207
+ `WorkOSProvider.extractUserInfo()` maps this to MJ's `AuthUserInfo`:
208
+
209
+ | `AuthUserInfo` field | Source claim | Fallback |
210
+ |---|---|---|
211
+ | `email` | `email` | — (undefined if no JWT Template) |
212
+ | `firstName` | `given_name` | first word of `name` |
213
+ | `lastName` | `family_name` | second word of `name`, else first word |
214
+ | `fullName` | `name` | — |
215
+ | `preferredUsername` | `preferred_username` | `email` |
216
+ | `userId` | `sub` | — |
217
+
218
+ ## How validation works under the hood
219
+
220
+ 1. The browser sends `Authorization: Bearer <accessToken>` to the MJ GraphQL API.
221
+ 2. MJServer decodes the token (without verifying) to read the `iss` claim.
222
+ 3. `AuthProviderFactory.getByIssuer(iss)` resolves the `WorkOSProvider` instance (issuer matching
223
+ is case-insensitive and trailing-slash tolerant).
224
+ 4. `jwt.verify()` validates the RS256 signature against the JWKS at
225
+ `https://api.workos.com/sso/jwks/<clientId>` (cached, with retry/backoff handled by
226
+ `BaseAuthProvider`), and checks `iss` + `aud`.
227
+ 5. `WorkOSProvider.extractUserInfo(payload)` produces the normalized identity.
228
+ 6. MJServer resolves/creates the `User` record by **email** and builds the request context.
229
+
230
+ See [`@memberjunction/auth-providers` README](README.md) and
231
+ [`@memberjunction/server`](../MJServer/README.md) for the shared validation pipeline.
232
+
233
+ ## Troubleshooting
234
+
235
+ | Symptom | Likely cause | Fix |
236
+ |---|---|---|
237
+ | Server logs "user not found" / login bounces | Access token has no `email` claim | [Add the JWT Template](#step-3--add-an-email-claim-jwt-template) |
238
+ | `jwt audience invalid` | `audience` config ≠ token `aud` | Decode a real token, copy its `aud` into `audience` ([Step 2](#getting-audience-right)) |
239
+ | `No authentication provider found for issuer` | `issuer` config doesn't match the token | Ensure `issuer` is `https://api.workos.com/user_management/<clientId>` with your real Client ID |
240
+ | `jwt expired` right after login | Clock skew or stale token | The browser SDK auto-refreshes; ensure server time is correct |
241
+ | Login redirect loops | Redirect URI not registered | Add your exact origin under WorkOS dashboard → Redirects |
242
+ | New users not auto-created | Missing `given_name`/`family_name` or `autoCreateNewUsers` off | Add name claims to the JWT Template; check `userHandling` config |
243
+
244
+ ## Scope & non-goals
245
+
246
+ - **In scope:** WorkOS **AuthKit / User Management** (OIDC/JWT bearer login + validation). This is
247
+ the same OIDC/JWT model every other MJ provider uses.
248
+ - **Out of scope (today):** WorkOS **SSO via SAML assertions** and **Directory Sync (SCIM)**
249
+ user provisioning as distinct flows. AuthKit can itself broker SSO connections behind its hosted
250
+ login, so SSO end-users still authenticate through the `workos` provider — but MJ does not parse
251
+ raw SAML assertions or run SCIM provisioning. Those would be a separate, larger effort.
252
+
253
+ ---
254
+
255
+ *Part of [`@memberjunction/auth-providers`](README.md) in the
256
+ [MemberJunction](https://github.com/MemberJunction/MJ) monorepo.*
@@ -6,6 +6,7 @@ import './providers/MSALProvider.js';
6
6
  import './providers/OktaProvider.js';
7
7
  import './providers/CognitoProvider.js';
8
8
  import './providers/GoogleProvider.js';
9
+ import './providers/WorkOSProvider.js';
9
10
  import './providers/MagicLinkProvider.js';
10
11
  /**
11
12
  * Factory and registry for managing authentication providers
@@ -1 +1 @@
1
- {"version":3,"file":"AuthProviderFactory.d.ts","sourceRoot":"","sources":["../src/AuthProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAe,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAY,aAAa,EAAc,MAAM,wBAAwB,CAAC;AAG7E,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,gCAAgC,CAAC;AACxC,OAAO,+BAA+B,CAAC;AACvC,OAAO,kCAAkC,CAAC;AAE1C;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,aAAa,CAAC,mBAAmB,CAAC;IACzE,OAAO,CAAC,SAAS,CAAyC;IAC1D;;;;;;OAMG;IACH,OAAO,CAAC,WAAW,CAA6F;IAChH;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAiG;;IAMzH;;OAEG;IACH,WAAkB,QAAQ,IAAI,mBAAmB,CAEhD;IAED;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa;IAsBhE;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAiBvC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAmBtD;;;;;;OAMG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE;IAqB/C;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIlD;;OAEG;IACH,eAAe,IAAI,aAAa,EAAE;IAIlC;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,MAAM,CAAC,0BAA0B,IAAI,MAAM,EAAE;IAW7C;;OAEG;IACH,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CASvD"}
1
+ {"version":3,"file":"AuthProviderFactory.d.ts","sourceRoot":"","sources":["../src/AuthProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAe,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAY,aAAa,EAAc,MAAM,wBAAwB,CAAC;AAG7E,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,gCAAgC,CAAC;AACxC,OAAO,+BAA+B,CAAC;AACvC,OAAO,+BAA+B,CAAC;AACvC,OAAO,kCAAkC,CAAC;AAE1C;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,aAAa,CAAC,mBAAmB,CAAC;IACzE,OAAO,CAAC,SAAS,CAAyC;IAC1D;;;;;;OAMG;IACH,OAAO,CAAC,WAAW,CAA6F;IAChH;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAiG;;IAMzH;;OAEG;IACH,WAAkB,QAAQ,IAAI,mBAAmB,CAEhD;IAED;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa;IAsBhE;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAiBvC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAmBtD;;;;;;OAMG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE;IAqB/C;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIlD;;OAEG;IACH,eAAe,IAAI,aAAa,EAAE;IAIlC;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,MAAM,CAAC,0BAA0B,IAAI,MAAM,EAAE;IAW7C;;OAEG;IACH,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CASvD"}
@@ -7,6 +7,7 @@ import './providers/MSALProvider.js';
7
7
  import './providers/OktaProvider.js';
8
8
  import './providers/CognitoProvider.js';
9
9
  import './providers/GoogleProvider.js';
10
+ import './providers/WorkOSProvider.js';
10
11
  import './providers/MagicLinkProvider.js';
11
12
  /**
12
13
  * Factory and registry for managing authentication providers
@@ -1 +1 @@
1
- {"version":3,"file":"AuthProviderFactory.js","sourceRoot":"","sources":["../src/AuthProviderFactory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AuthProviderFactory.js","sourceRoot":"","sources":["../src/AuthProviderFactory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { JwtPayload } from 'jsonwebtoken';
2
+ import { AuthProviderConfig, AuthUserInfo } from '@memberjunction/core';
3
+ import { BaseAuthProvider } from '../BaseAuthProvider.js';
4
+ /**
5
+ * WorkOS (AuthKit) authentication provider implementation.
6
+ *
7
+ * Validates JWT access tokens minted by WorkOS AuthKit / User Management. These tokens are
8
+ * signed with RS256 and verified against the per-environment JWKS endpoint
9
+ * (`https://api.workos.com/sso/jwks/<clientId>`); their `iss` claim is
10
+ * `https://api.workos.com/user_management/<clientId>`.
11
+ *
12
+ * ## Email is required — and is NOT in a WorkOS token by default
13
+ * MemberJunction resolves the signed-in user by **email**, but a WorkOS AuthKit access token
14
+ * only carries identity/session claims (`sub`, `sid`, `org_id`, `role`, `permissions`) out of
15
+ * the box — it does **not** include `email`. To use WorkOS with MJ you must add the email (and
16
+ * ideally the name) to the token via a WorkOS **JWT Template**:
17
+ *
18
+ * ```json
19
+ * {
20
+ * "email": "{{user.email}}",
21
+ * "given_name": "{{user.first_name}}",
22
+ * "family_name": "{{user.last_name}}"
23
+ * }
24
+ * ```
25
+ *
26
+ * See `WORKOS.md` in this package for the full, step-by-step integration guide.
27
+ */
28
+ export declare class WorkOSProvider extends BaseAuthProvider {
29
+ constructor(config: AuthProviderConfig);
30
+ /**
31
+ * Extracts user information from a WorkOS AuthKit JWT payload.
32
+ *
33
+ * `email`, `given_name`, and `family_name` are expected to be supplied by a WorkOS JWT
34
+ * Template (see class docs). `sub` is the stable WorkOS user id (`user_...`). We fall back
35
+ * through standard OIDC claim names so a token shaped by a custom template (e.g. one that
36
+ * only sets `name`) still resolves a usable identity.
37
+ */
38
+ extractUserInfo(payload: JwtPayload): AuthUserInfo;
39
+ /**
40
+ * Validates WorkOS-specific configuration.
41
+ *
42
+ * Beyond the base requirements (`name`, `issuer`, `audience`, `jwksUri`), WorkOS needs a
43
+ * `clientId` — it appears in both the issuer and JWKS URLs and identifies the AuthKit
44
+ * environment.
45
+ */
46
+ validateConfig(): boolean;
47
+ }
48
+ //# sourceMappingURL=WorkOSProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkOSProvider.d.ts","sourceRoot":"","sources":["../../src/providers/WorkOSProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBACa,cAAe,SAAQ,gBAAgB;gBACtC,MAAM,EAAE,kBAAkB;IAItC;;;;;;;OAOG;IACH,eAAe,CAAC,OAAO,EAAE,UAAU,GAAG,YAAY;IAoBlD;;;;;;OAMG;IACH,cAAc,IAAI,OAAO;CAM1B"}
@@ -0,0 +1,84 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { RegisterClass } from '@memberjunction/global';
11
+ import { BaseAuthProvider } from '../BaseAuthProvider.js';
12
+ /**
13
+ * WorkOS (AuthKit) authentication provider implementation.
14
+ *
15
+ * Validates JWT access tokens minted by WorkOS AuthKit / User Management. These tokens are
16
+ * signed with RS256 and verified against the per-environment JWKS endpoint
17
+ * (`https://api.workos.com/sso/jwks/<clientId>`); their `iss` claim is
18
+ * `https://api.workos.com/user_management/<clientId>`.
19
+ *
20
+ * ## Email is required — and is NOT in a WorkOS token by default
21
+ * MemberJunction resolves the signed-in user by **email**, but a WorkOS AuthKit access token
22
+ * only carries identity/session claims (`sub`, `sid`, `org_id`, `role`, `permissions`) out of
23
+ * the box — it does **not** include `email`. To use WorkOS with MJ you must add the email (and
24
+ * ideally the name) to the token via a WorkOS **JWT Template**:
25
+ *
26
+ * ```json
27
+ * {
28
+ * "email": "{{user.email}}",
29
+ * "given_name": "{{user.first_name}}",
30
+ * "family_name": "{{user.last_name}}"
31
+ * }
32
+ * ```
33
+ *
34
+ * See `WORKOS.md` in this package for the full, step-by-step integration guide.
35
+ */
36
+ let WorkOSProvider = class WorkOSProvider extends BaseAuthProvider {
37
+ constructor(config) {
38
+ super(config);
39
+ }
40
+ /**
41
+ * Extracts user information from a WorkOS AuthKit JWT payload.
42
+ *
43
+ * `email`, `given_name`, and `family_name` are expected to be supplied by a WorkOS JWT
44
+ * Template (see class docs). `sub` is the stable WorkOS user id (`user_...`). We fall back
45
+ * through standard OIDC claim names so a token shaped by a custom template (e.g. one that
46
+ * only sets `name`) still resolves a usable identity.
47
+ */
48
+ extractUserInfo(payload) {
49
+ const email = payload.email;
50
+ const fullName = payload.name;
51
+ const firstName = payload.given_name;
52
+ const lastName = payload.family_name;
53
+ // WorkOS access tokens have no `preferred_username`; email is the natural handle.
54
+ const preferredUsername = payload.preferred_username || email;
55
+ // `sub` is the WorkOS user identifier (e.g. "user_01H...").
56
+ const userId = payload.sub;
57
+ return {
58
+ email,
59
+ firstName: firstName || fullName?.split(' ')[0],
60
+ lastName: lastName || fullName?.split(' ')[1] || fullName?.split(' ')[0],
61
+ fullName,
62
+ preferredUsername,
63
+ userId
64
+ };
65
+ }
66
+ /**
67
+ * Validates WorkOS-specific configuration.
68
+ *
69
+ * Beyond the base requirements (`name`, `issuer`, `audience`, `jwksUri`), WorkOS needs a
70
+ * `clientId` — it appears in both the issuer and JWKS URLs and identifies the AuthKit
71
+ * environment.
72
+ */
73
+ validateConfig() {
74
+ const baseValid = super.validateConfig();
75
+ const hasClientId = !!this.config.clientId;
76
+ return baseValid && hasClientId;
77
+ }
78
+ };
79
+ WorkOSProvider = __decorate([
80
+ RegisterClass(BaseAuthProvider, 'workos'),
81
+ __metadata("design:paramtypes", [Object])
82
+ ], WorkOSProvider);
83
+ export { WorkOSProvider };
84
+ //# sourceMappingURL=WorkOSProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkOSProvider.js","sourceRoot":"","sources":["../../src/providers/WorkOSProvider.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,gBAAgB;IAClD,YAAY,MAA0B;QACpC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;IAED;;;;;;;OAOG;IACH,eAAe,CAAC,OAAmB;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,KAA2B,CAAC;QAClD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAA0B,CAAC;QACpD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAgC,CAAC;QAC3D,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAiC,CAAC;QAC3D,kFAAkF;QAClF,MAAM,iBAAiB,GAAG,OAAO,CAAC,kBAAwC,IAAI,KAAK,CAAC;QACpF,4DAA4D;QAC5D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAyB,CAAC;QAEjD,OAAO;YACL,KAAK;YACL,SAAS,EAAE,SAAS,IAAI,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC/C,QAAQ,EAAE,QAAQ,IAAI,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACxE,QAAQ;YACR,iBAAiB;YACjB,MAAM;SACP,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,cAAc;QACZ,MAAM,SAAS,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;QACzC,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAE3C,OAAO,SAAS,IAAI,WAAW,CAAC;IAClC,CAAC;CACF,CAAA;AA9CY,cAAc;IAD1B,aAAa,CAAC,gBAAgB,EAAE,QAAQ,CAAC;;GAC7B,cAAc,CA8C1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/auth-providers",
3
- "version": "5.42.0",
3
+ "version": "5.44.0",
4
4
  "description": "Authentication provider interfaces, base classes, and implementations for MemberJunction",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,8 +11,8 @@
11
11
  "test:watch": "vitest"
12
12
  },
13
13
  "dependencies": {
14
- "@memberjunction/core": "5.42.0",
15
- "@memberjunction/global": "5.42.0",
14
+ "@memberjunction/core": "5.44.0",
15
+ "@memberjunction/global": "5.44.0",
16
16
  "graphql": "^16.12.0",
17
17
  "jsonwebtoken": "9.0.3",
18
18
  "jwks-rsa": "^3.2.2"
@@ -9,6 +9,7 @@ import './providers/MSALProvider.js';
9
9
  import './providers/OktaProvider.js';
10
10
  import './providers/CognitoProvider.js';
11
11
  import './providers/GoogleProvider.js';
12
+ import './providers/WorkOSProvider.js';
12
13
  import './providers/MagicLinkProvider.js';
13
14
 
14
15
  /**
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Unit tests for WorkOSProvider.
3
+ *
4
+ * Covers the two pieces of logic the provider owns on top of BaseAuthProvider:
5
+ * - extractUserInfo(): mapping WorkOS AuthKit JWT claims → AuthUserInfo, including the
6
+ * fallbacks that keep a usable identity when a custom JWT Template only sets `name`.
7
+ * - validateConfig(): the WorkOS-specific requirement (clientId) on top of the base fields.
8
+ *
9
+ * It also asserts the provider registers with the class factory under the `workos` key, which
10
+ * is what makes `AuthProviderFactory.createProvider({ type: 'workos', ... })` resolve it.
11
+ */
12
+ import { describe, it, expect } from 'vitest';
13
+ import type { JwtPayload } from 'jsonwebtoken';
14
+ import { MJGlobal } from '@memberjunction/global';
15
+ import { AuthProviderConfig } from '@memberjunction/core';
16
+ import { BaseAuthProvider } from '../BaseAuthProvider';
17
+ import { WorkOSProvider } from '../providers/WorkOSProvider';
18
+
19
+ const CLIENT_ID = 'client_01HABCDEF';
20
+
21
+ function makeConfig(overrides: Partial<AuthProviderConfig> = {}): AuthProviderConfig {
22
+ return {
23
+ name: 'workos-test',
24
+ type: 'workos',
25
+ issuer: `https://api.workos.com/user_management/${CLIENT_ID}`,
26
+ audience: CLIENT_ID,
27
+ jwksUri: `https://api.workos.com/sso/jwks/${CLIENT_ID}`,
28
+ clientId: CLIENT_ID,
29
+ ...overrides,
30
+ };
31
+ }
32
+
33
+ function makeProvider(overrides: Partial<AuthProviderConfig> = {}): WorkOSProvider {
34
+ return new WorkOSProvider(makeConfig(overrides));
35
+ }
36
+
37
+ describe('WorkOSProvider', () => {
38
+ describe('extractUserInfo', () => {
39
+ it('maps a token shaped by the recommended JWT Template (email + given/family name)', () => {
40
+ const provider = makeProvider();
41
+ const payload: JwtPayload = {
42
+ sub: 'user_01HXYZ',
43
+ email: 'ada@example.com',
44
+ given_name: 'Ada',
45
+ family_name: 'Lovelace',
46
+ };
47
+
48
+ const info = provider.extractUserInfo(payload);
49
+
50
+ expect(info.email).toBe('ada@example.com');
51
+ expect(info.firstName).toBe('Ada');
52
+ expect(info.lastName).toBe('Lovelace');
53
+ // No preferred_username on WorkOS tokens — falls back to email.
54
+ expect(info.preferredUsername).toBe('ada@example.com');
55
+ // `sub` is surfaced as the provider user id.
56
+ expect(info.userId).toBe('user_01HXYZ');
57
+ });
58
+
59
+ it('derives first/last name from a single `name` claim when given/family are absent', () => {
60
+ const provider = makeProvider();
61
+ const payload: JwtPayload = {
62
+ sub: 'user_01HXYZ',
63
+ email: 'grace.hopper@example.com',
64
+ name: 'Grace Hopper',
65
+ };
66
+
67
+ const info = provider.extractUserInfo(payload);
68
+
69
+ expect(info.fullName).toBe('Grace Hopper');
70
+ expect(info.firstName).toBe('Grace');
71
+ expect(info.lastName).toBe('Hopper');
72
+ });
73
+
74
+ it('falls back to the single name token for both first and last when name has one word', () => {
75
+ const provider = makeProvider();
76
+ const payload: JwtPayload = { sub: 'user_1', email: 'cher@example.com', name: 'Cher' };
77
+
78
+ const info = provider.extractUserInfo(payload);
79
+
80
+ expect(info.firstName).toBe('Cher');
81
+ expect(info.lastName).toBe('Cher');
82
+ });
83
+
84
+ it('prefers explicit given_name/family_name over the `name` split', () => {
85
+ const provider = makeProvider();
86
+ const payload: JwtPayload = {
87
+ sub: 'user_2',
88
+ email: 'jean.luc@example.com',
89
+ name: 'Wrong Split',
90
+ given_name: 'Jean-Luc',
91
+ family_name: 'Picard',
92
+ };
93
+
94
+ const info = provider.extractUserInfo(payload);
95
+
96
+ expect(info.firstName).toBe('Jean-Luc');
97
+ expect(info.lastName).toBe('Picard');
98
+ });
99
+
100
+ it('honors an explicit preferred_username when present', () => {
101
+ const provider = makeProvider();
102
+ const payload: JwtPayload = {
103
+ sub: 'user_3',
104
+ email: 'alan@example.com',
105
+ preferred_username: 'aturing',
106
+ };
107
+
108
+ const info = provider.extractUserInfo(payload);
109
+
110
+ expect(info.preferredUsername).toBe('aturing');
111
+ });
112
+
113
+ it('returns undefined email (rather than throwing) when no JWT Template added it', () => {
114
+ // This is the default WorkOS access token shape — identity/session claims only.
115
+ const provider = makeProvider();
116
+ const payload: JwtPayload = {
117
+ sub: 'user_4',
118
+ sid: 'session_01H',
119
+ org_id: 'org_01H',
120
+ role: 'admin',
121
+ };
122
+
123
+ const info = provider.extractUserInfo(payload);
124
+
125
+ expect(info.email).toBeUndefined();
126
+ expect(info.userId).toBe('user_4');
127
+ expect(info.preferredUsername).toBeUndefined();
128
+ });
129
+ });
130
+
131
+ describe('validateConfig', () => {
132
+ it('is valid with the full WorkOS config (base fields + clientId)', () => {
133
+ expect(makeProvider().validateConfig()).toBe(true);
134
+ });
135
+
136
+ it('is invalid without a clientId', () => {
137
+ expect(makeProvider({ clientId: undefined }).validateConfig()).toBe(false);
138
+ });
139
+
140
+ it('is invalid when a base field (audience) is missing', () => {
141
+ expect(makeProvider({ audience: '' }).validateConfig()).toBe(false);
142
+ });
143
+
144
+ it('exposes clientId on the instance for the OAuth proxy path', () => {
145
+ expect(makeProvider().clientId).toBe(CLIENT_ID);
146
+ });
147
+ });
148
+
149
+ describe('class-factory registration', () => {
150
+ it('is registered under the lowercase `workos` key and instantiable via the factory', () => {
151
+ const instance = MJGlobal.Instance.ClassFactory.CreateInstance<BaseAuthProvider>(
152
+ BaseAuthProvider,
153
+ 'workos',
154
+ makeConfig(),
155
+ );
156
+
157
+ expect(instance).toBeInstanceOf(WorkOSProvider);
158
+ });
159
+ });
160
+
161
+ describe('issuer matching (inherited)', () => {
162
+ it('matches its configured issuer case-insensitively and ignores a trailing slash', () => {
163
+ const provider = makeProvider();
164
+ expect(provider.matchesIssuer(`https://api.workos.com/user_management/${CLIENT_ID}`)).toBe(true);
165
+ expect(provider.matchesIssuer(`https://api.workos.com/user_management/${CLIENT_ID}/`)).toBe(true);
166
+ expect(provider.matchesIssuer('https://api.workos.com/user_management/other')).toBe(false);
167
+ });
168
+ });
169
+ });
@@ -0,0 +1,78 @@
1
+ import { JwtPayload } from 'jsonwebtoken';
2
+ import { RegisterClass } from '@memberjunction/global';
3
+ import { AuthProviderConfig, AuthUserInfo } from '@memberjunction/core';
4
+ import { BaseAuthProvider } from '../BaseAuthProvider.js';
5
+
6
+
7
+ /**
8
+ * WorkOS (AuthKit) authentication provider implementation.
9
+ *
10
+ * Validates JWT access tokens minted by WorkOS AuthKit / User Management. These tokens are
11
+ * signed with RS256 and verified against the per-environment JWKS endpoint
12
+ * (`https://api.workos.com/sso/jwks/<clientId>`); their `iss` claim is
13
+ * `https://api.workos.com/user_management/<clientId>`.
14
+ *
15
+ * ## Email is required — and is NOT in a WorkOS token by default
16
+ * MemberJunction resolves the signed-in user by **email**, but a WorkOS AuthKit access token
17
+ * only carries identity/session claims (`sub`, `sid`, `org_id`, `role`, `permissions`) out of
18
+ * the box — it does **not** include `email`. To use WorkOS with MJ you must add the email (and
19
+ * ideally the name) to the token via a WorkOS **JWT Template**:
20
+ *
21
+ * ```json
22
+ * {
23
+ * "email": "{{user.email}}",
24
+ * "given_name": "{{user.first_name}}",
25
+ * "family_name": "{{user.last_name}}"
26
+ * }
27
+ * ```
28
+ *
29
+ * See `WORKOS.md` in this package for the full, step-by-step integration guide.
30
+ */
31
+ @RegisterClass(BaseAuthProvider, 'workos')
32
+ export class WorkOSProvider extends BaseAuthProvider {
33
+ constructor(config: AuthProviderConfig) {
34
+ super(config);
35
+ }
36
+
37
+ /**
38
+ * Extracts user information from a WorkOS AuthKit JWT payload.
39
+ *
40
+ * `email`, `given_name`, and `family_name` are expected to be supplied by a WorkOS JWT
41
+ * Template (see class docs). `sub` is the stable WorkOS user id (`user_...`). We fall back
42
+ * through standard OIDC claim names so a token shaped by a custom template (e.g. one that
43
+ * only sets `name`) still resolves a usable identity.
44
+ */
45
+ extractUserInfo(payload: JwtPayload): AuthUserInfo {
46
+ const email = payload.email as string | undefined;
47
+ const fullName = payload.name as string | undefined;
48
+ const firstName = payload.given_name as string | undefined;
49
+ const lastName = payload.family_name as string | undefined;
50
+ // WorkOS access tokens have no `preferred_username`; email is the natural handle.
51
+ const preferredUsername = payload.preferred_username as string | undefined || email;
52
+ // `sub` is the WorkOS user identifier (e.g. "user_01H...").
53
+ const userId = payload.sub as string | undefined;
54
+
55
+ return {
56
+ email,
57
+ firstName: firstName || fullName?.split(' ')[0],
58
+ lastName: lastName || fullName?.split(' ')[1] || fullName?.split(' ')[0],
59
+ fullName,
60
+ preferredUsername,
61
+ userId
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Validates WorkOS-specific configuration.
67
+ *
68
+ * Beyond the base requirements (`name`, `issuer`, `audience`, `jwksUri`), WorkOS needs a
69
+ * `clientId` — it appears in both the issuer and JWKS URLs and identifies the AuthKit
70
+ * environment.
71
+ */
72
+ validateConfig(): boolean {
73
+ const baseValid = super.validateConfig();
74
+ const hasClientId = !!this.config.clientId;
75
+
76
+ return baseValid && hasClientId;
77
+ }
78
+ }