@enterprisestandard/core 0.0.7-beta.20260124.2 → 0.0.7

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +1375 -1131
  2. package/dist/index.js +2 -2
  3. package/package.json +3 -3
package/dist/index.d.ts CHANGED
@@ -1,524 +1,832 @@
1
- import { StandardSchemaV1 as StandardSchemaV15 } from "@standard-schema/spec";
2
- import { StandardSchemaV1 } from "@standard-schema/spec";
3
- /**
4
- * SCIM 2.0 User Resource
5
- * @see https://datatracker.ietf.org/doc/html/rfc7643#section-4.1
6
- */
7
1
  /**
8
- * SCIM Name sub-attribute
2
+ * Base user with simple, developer-friendly attributes.
3
+ * Extended by User (SSO) and EnterpriseUser (SCIM).
9
4
  */
10
- interface Name {
11
- /**
12
- * The full name, including all middle names, titles, and suffixes as appropriate
13
- */
14
- formatted?: string;
5
+ interface BaseUser {
15
6
  /**
16
- * The family name of the User, or last name
7
+ * Unique identifier for the user
17
8
  */
18
- familyName?: string;
9
+ id?: string;
19
10
  /**
20
- * The given name of the User, or first name
11
+ * REQUIRED. Unique identifier for login
21
12
  */
22
- givenName?: string;
13
+ userName: string;
23
14
  /**
24
- * The middle name(s) of the User
15
+ * REQUIRED. Simple display name
25
16
  */
26
- middleName?: string;
17
+ name: string;
27
18
  /**
28
- * The honorific prefix(es) of the User, or title
19
+ * REQUIRED. Primary email address
29
20
  */
30
- honorificPrefix?: string;
21
+ email: string;
31
22
  /**
32
- * The honorific suffix(es) of the User, or suffix
23
+ * URL to user's avatar/profile picture
33
24
  */
34
- honorificSuffix?: string;
25
+ avatarUrl?: string;
35
26
  }
36
27
  /**
37
- * SCIM Email sub-attribute
28
+ * Magic link data stored in the store.
29
+ *
30
+ * @template TExtended - Type-safe custom data that consumers can add to magic links
38
31
  */
39
- interface Email {
32
+ type MagicLink<TExtended = {}> = {
40
33
  /**
41
- * The email address value
34
+ * The magic link token (unique identifier)
42
35
  */
43
- value: string;
36
+ token: string;
44
37
  /**
45
- * A human-readable name, primarily used for display purposes
38
+ * User information associated with this magic link
46
39
  */
47
- display?: string;
40
+ user: BaseUser;
48
41
  /**
49
- * A label indicating the attribute's function (e.g., "work" or "home")
42
+ * Timestamp when the magic link was created
50
43
  */
51
- type?: string;
44
+ createdAt: Date;
52
45
  /**
53
- * A Boolean value indicating the 'primary' or preferred attribute value
46
+ * Timestamp when the magic link expires
54
47
  */
55
- primary?: boolean;
56
- }
57
- /**
58
- * SCIM Phone Number sub-attribute
59
- */
60
- interface PhoneNumber {
48
+ expiresAt: Date;
61
49
  /**
62
- * The phone number value
50
+ * Allow consumers to add runtime data to magic links
63
51
  */
64
- value: string;
52
+ [key: string]: unknown;
53
+ } & TExtended;
54
+ /**
55
+ * Abstract interface for magic link storage backends.
56
+ *
57
+ * Consumers can implement this interface to use different storage backends:
58
+ * - In-memory (for development/testing)
59
+ * - Redis (for production with fast lookups and automatic expiration)
60
+ * - Database (PostgreSQL, MySQL, etc.)
61
+ *
62
+ * @template TExtended - Type-safe custom data that consumers can add to magic links
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * // Custom magic link data
67
+ * type MyMagicLinkData = {
68
+ * source: string;
69
+ * metadata: Record<string, unknown>;
70
+ * };
71
+ *
72
+ * // Implement custom store
73
+ * class RedisMagicLinkStore implements MagicLinkStore<MyMagicLinkData> {
74
+ * async create(token: string, user: BaseUser, expiresAt: Date): Promise<void> {
75
+ * const magicLink: MagicLink<MyMagicLinkData> = {
76
+ * token,
77
+ * user,
78
+ * createdAt: new Date(),
79
+ * expiresAt,
80
+ * source: 'api',
81
+ * metadata: {},
82
+ * };
83
+ * const ttl = Math.floor((expiresAt.getTime() - Date.now()) / 1000);
84
+ * await redis.setex(`magic-link:${token}`, ttl, JSON.stringify(magicLink));
85
+ * }
86
+ * // ... other methods
87
+ * }
88
+ * ```
89
+ */
90
+ interface MagicLinkStore<TExtended = {}> {
65
91
  /**
66
- * A human-readable name, primarily used for display purposes
92
+ * Create a new magic link in the store.
93
+ *
94
+ * @param token - The magic link token (unique identifier)
95
+ * @param user - The user information to associate with this magic link
96
+ * @param expiresAt - When the magic link expires
97
+ * @throws Error if magic link with same token already exists
67
98
  */
68
- display?: string;
99
+ create(token: string, user: BaseUser, expiresAt: Date): Promise<void>;
69
100
  /**
70
- * A label indicating the attribute's function (e.g., "work", "home", "mobile")
101
+ * Retrieve a magic link by its token.
102
+ *
103
+ * @param token - The magic link token
104
+ * @returns The magic link if found and not expired, null otherwise
71
105
  */
72
- type?: string;
106
+ get(token: string): Promise<MagicLink<TExtended> | null>;
73
107
  /**
74
- * A Boolean value indicating the 'primary' or preferred attribute value
108
+ * Delete a magic link by its token.
109
+ *
110
+ * Used after a magic link has been consumed (one-time use).
111
+ *
112
+ * @param token - The magic link token to delete
75
113
  */
76
- primary?: boolean;
114
+ delete(token: string): Promise<void>;
77
115
  }
78
116
  /**
79
- * SCIM Address sub-attribute
117
+ * In-memory magic link store implementation using Maps.
118
+ *
119
+ * Suitable for:
120
+ * - Development and testing
121
+ * - Single-server deployments
122
+ * - Applications without high availability requirements
123
+ *
124
+ * NOT suitable for:
125
+ * - Multi-server deployments (magic links not shared)
126
+ * - High availability scenarios (magic links lost on restart)
127
+ * - Production applications with distributed architecture
128
+ *
129
+ * For production, implement MagicLinkStore with Redis or a database.
130
+ *
131
+ * @template TExtended - Type-safe custom data that consumers can add to magic links
80
132
  */
81
- interface Address {
82
- /**
83
- * The full mailing address, formatted for display
84
- */
85
- formatted?: string;
86
- /**
87
- * The full street address component
88
- */
89
- streetAddress?: string;
90
- /**
91
- * The city or locality component
92
- */
93
- locality?: string;
133
+ declare class InMemoryMagicLinkStore<TExtended = {}> implements MagicLinkStore<TExtended> {
134
+ private magicLinks;
135
+ create(token: string, user: BaseUser, expiresAt: Date): Promise<void>;
136
+ get(token: string): Promise<MagicLink<TExtended> | null>;
137
+ delete(token: string): Promise<void>;
138
+ }
139
+ /**
140
+ * Session management for tracking user sessions and enabling backchannel logout.
141
+ *
142
+ * Session stores are optional - the package works with JWT cookies alone.
143
+ * Sessions are only required for backchannel logout functionality.
144
+ *
145
+ * ## Session Validation Strategies
146
+ *
147
+ * When using a session store, you can configure when sessions are validated:
148
+ *
149
+ * ### 'always' (default)
150
+ * Validates session on every authenticated request.
151
+ * - **Security**: Maximum - immediate session revocation
152
+ * - **Performance**: InMemory ~0.00005ms, Redis ~1-2ms per request
153
+ * - **Backchannel Logout**: Takes effect immediately
154
+ * - **Use when**: Security is paramount, using InMemory or Redis backend
155
+ *
156
+ * ### 'refresh-only'
157
+ * Validates session only during token refresh (typically every 5-15 minutes).
158
+ * - **Security**: Good - 5-15 minute revocation window
159
+ * - **Performance**: 99% reduction in session lookups
160
+ * - **Backchannel Logout**: Takes effect within token TTL (5-15 min)
161
+ * - **Use when**: Performance is critical AND delayed revocation is acceptable
162
+ * - **WARNING**: Compromised sessions remain valid until next refresh
163
+ *
164
+ * ### 'disabled'
165
+ * Never validates sessions against the store.
166
+ * - **Security**: None - backchannel logout doesn't work
167
+ * - **Performance**: No overhead
168
+ * - **Use when**: Cookie-only mode without session store
169
+ * - **WARNING**: Do not use with sessionStore configured
170
+ *
171
+ * ## Performance Characteristics
172
+ *
173
+ * | Backend | Lookup Time | Impact on Request | Recommendation |
174
+ * |--------------|-------------|-------------------|------------------------|
175
+ * | InMemory | <0.00005ms | Negligible | Use 'always' |
176
+ * | Redis | 1-2ms | 2-4% increase | Use 'always' or test |
177
+ * | Database | 5-20ms | 10-40% increase | Use Redis cache layer |
178
+ *
179
+ * ## Example Usage
180
+ *
181
+ * ```typescript
182
+ * import { sso, InMemorySessionStore } from '@enterprisestandard/react/server';
183
+ *
184
+ * // Maximum security (default)
185
+ * const secure = sso({
186
+ * // ... other config
187
+ * sessionStore: new InMemorySessionStore(),
188
+ * session_validation: 'always', // Immediate revocation
189
+ * });
190
+ *
191
+ * // High performance
192
+ * const fast = sso({
193
+ * // ... other config
194
+ * sessionStore: new InMemorySessionStore(),
195
+ * session_validation: {
196
+ * strategy: 'refresh-only' // 5-15 min revocation delay
197
+ * }
198
+ * });
199
+ * ```
200
+ */
201
+ /**
202
+ * Core session data tracked for each authenticated user session.
203
+ *
204
+ * @template TExtended - Type-safe custom data that consumers can add to sessions
205
+ */
206
+ type Session<TExtended = {}> = {
94
207
  /**
95
- * The state or region component
208
+ * Session ID from the Identity Provider (from `sid` claim in ID token).
209
+ * This is the unique identifier for the session.
96
210
  */
97
- region?: string;
211
+ sid: string;
98
212
  /**
99
- * The zip code or postal code component
213
+ * Subject identifier (user ID) from the Identity Provider.
214
+ * From the `sub` claim in the ID token.
100
215
  */
101
- postalCode?: string;
216
+ sub: string;
102
217
  /**
103
- * The country name component
218
+ * Timestamp when the session was created.
104
219
  */
105
- country?: string;
220
+ createdAt: Date;
106
221
  /**
107
- * A label indicating the attribute's function (e.g., "work" or "home")
222
+ * Timestamp of the last activity in this session.
223
+ * Can be updated to track session activity.
108
224
  */
109
- type?: string;
225
+ lastActivityAt: Date;
110
226
  /**
111
- * A Boolean value indicating the 'primary' or preferred attribute value
227
+ * Allow consumers to add runtime data to sessions.
112
228
  */
113
- primary?: boolean;
114
- }
229
+ [key: string]: unknown;
230
+ } & TExtended;
115
231
  /**
116
- * SCIM Group reference (used within User resources)
232
+ * Abstract interface for session storage backends.
233
+ *
234
+ * Consumers can implement this interface to use different storage backends:
235
+ * - Redis
236
+ * - Database (PostgreSQL, MySQL, etc.)
237
+ * - Distributed cache
238
+ * - Custom solutions
239
+ *
240
+ * @template TExtended - Type-safe custom data that consumers can add to sessions
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * // Custom session data
245
+ * type MySessionData = {
246
+ * ipAddress: string;
247
+ * userAgent: string;
248
+ * };
249
+ *
250
+ * // Implement custom store
251
+ * class RedisSessionStore implements SessionStore<MySessionData> {
252
+ * async create(session: Session<MySessionData>): Promise<void> {
253
+ * await redis.set(`session:${session.sid}`, JSON.stringify(session));
254
+ * }
255
+ * // ... other methods
256
+ * }
257
+ * ```
117
258
  */
118
- interface Group {
259
+ interface SessionStore<TExtended = {}> {
119
260
  /**
120
- * The identifier of the User's group
261
+ * Create a new session in the store.
262
+ *
263
+ * @param session - The session data to store
264
+ * @throws Error if session with same sid already exists
121
265
  */
122
- value: string;
266
+ create(session: Session<TExtended>): Promise<void>;
123
267
  /**
124
- * The URI of the corresponding 'Group' resource
268
+ * Retrieve a session by its IdP session ID (sid).
269
+ *
270
+ * @param sid - The session.sid from the Identity Provider
271
+ * @returns The session if found, null otherwise
125
272
  */
126
- $ref?: string;
273
+ get(sid: string): Promise<Session<TExtended> | null>;
127
274
  /**
128
- * A human-readable name, primarily used for display purposes
275
+ * Update an existing session with partial data.
276
+ *
277
+ * Commonly used to update lastActivityAt or add custom fields.
278
+ *
279
+ * @param sid - The session.sid to update
280
+ * @param data - Partial session data to merge
281
+ * @throws Error if session not found
129
282
  */
130
- display?: string;
283
+ update(sid: string, data: Partial<Session<TExtended>>): Promise<void>;
131
284
  /**
132
- * A label indicating the attribute's function (e.g., "direct" or "indirect")
133
- */
134
- type?: string;
285
+ * Delete a session by its IdP session ID (sid).
286
+ *
287
+ * Used for both normal logout and backchannel logout flows.
288
+ *
289
+ * @param sid - The session.sid to delete
290
+ */
291
+ delete(sid: string): Promise<void>;
135
292
  }
136
293
  /**
137
- * SCIM Group Member reference
294
+ * In-memory session store implementation using Maps.
295
+ *
296
+ * Suitable for:
297
+ * - Development and testing
298
+ * - Single-server deployments
299
+ * - Applications without high availability requirements
300
+ *
301
+ * NOT suitable for:
302
+ * - Multi-server deployments (sessions not shared)
303
+ * - High availability scenarios (sessions lost on restart)
304
+ * - Production applications with distributed architecture
305
+ *
306
+ * For production, implement SessionStore with Redis or a database.
307
+ *
308
+ * @template TExtended - Type-safe custom data that consumers can add to sessions
138
309
  */
139
- interface GroupMember {
140
- /**
141
- * The identifier of the member (User or Group)
142
- */
143
- value: string;
144
- /**
145
- * The URI of the corresponding member resource
146
- */
147
- $ref?: string;
148
- /**
149
- * A human-readable name of the member
150
- */
151
- display?: string;
152
- /**
153
- * The type of the member (e.g., "User" or "Group")
154
- */
155
- type?: "User" | "Group";
310
+ declare class InMemorySessionStore<TExtended = {}> implements SessionStore<TExtended> {
311
+ private sessions;
312
+ create(session: Session<TExtended>): Promise<void>;
313
+ get(sid: string): Promise<Session<TExtended> | null>;
314
+ update(sid: string, data: Partial<Session<TExtended>>): Promise<void>;
315
+ delete(sid: string): Promise<void>;
156
316
  }
317
+ import { StandardSchemaV1 } from "@standard-schema/spec";
157
318
  /**
158
- * SCIM 2.0 Group Resource
159
- * @see https://datatracker.ietf.org/doc/html/rfc7643#section-4.2
319
+ * OIDC Code Flow Callback URL Parameters
320
+ * @see https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
160
321
  */
161
- interface GroupResource {
322
+ interface OidcCallbackParams {
162
323
  /**
163
- * REQUIRED. The schemas attribute
324
+ * REQUIRED. The authorization code returned from the authorization server.
164
325
  */
165
- schemas?: string[];
326
+ code: string;
166
327
  /**
167
- * Unique identifier for the Group, assigned by the service provider
328
+ * REQUIRED if the "state" parameter was present in the client authorization request.
329
+ * The exact value received from the client.
168
330
  */
169
- id?: string;
331
+ state?: string;
170
332
  /**
171
- * External identifier from the provisioning client
333
+ * RECOMMENDED. The session state value. Clients should use this to verify the session state.
172
334
  */
173
- externalId?: string;
335
+ session_state?: string;
174
336
  /**
175
- * Resource metadata
337
+ * OAuth 2.0 error code if the authorization request failed.
176
338
  */
177
- meta?: {
178
- resourceType?: string;
179
- created?: string;
180
- lastModified?: string;
181
- location?: string;
182
- version?: string;
183
- };
339
+ error?: string;
184
340
  /**
185
- * REQUIRED. A human-readable name for the Group
341
+ * Human-readable ASCII text providing additional information for the error.
186
342
  */
187
- displayName: string;
343
+ error_description?: string;
188
344
  /**
189
- * A list of members of the Group
345
+ * A URI identifying a human-readable web page with information about the error.
190
346
  */
191
- members?: GroupMember[];
347
+ error_uri?: string;
348
+ /**
349
+ * The "iss" (issuer) parameter identifies the principal that issued the response.
350
+ * This is typically used in the implicit flow.
351
+ */
352
+ iss?: string;
192
353
  }
193
354
  /**
194
- * SCIM Role
355
+ * Creates a StandardSchemaV1 for validating OIDC callback URL parameters.
356
+ * @param vendor - The name of the vendor creating this schema
357
+ * @returns A StandardSchemaV1 instance for OIDC callback parameters
195
358
  */
196
- interface Role {
197
- /**
198
- * The value of the role
199
- */
200
- value: string;
201
- /**
202
- * A human-readable name, primarily used for display purposes
203
- */
204
- display?: string;
205
- /**
206
- * A label indicating the attribute's function
207
- */
208
- type?: string;
209
- /**
210
- * A Boolean value indicating the 'primary' or preferred attribute value
211
- */
212
- primary?: boolean;
359
+ declare function oidcCallbackSchema(vendor: string): StandardSchemaV1<Record<string, unknown>, OidcCallbackParams>;
360
+ /**
361
+ * Token Response from IdP
362
+ */
363
+ interface TokenResponse {
364
+ access_token: string;
365
+ id_token: string;
366
+ refresh_token?: string;
367
+ token_type: string;
368
+ expires_in?: number;
369
+ scope?: string;
370
+ refresh_expires_in?: number;
371
+ session_state?: string;
372
+ expires?: string;
213
373
  }
214
374
  /**
215
- * SCIM X509 Certificate
375
+ * Creates a StandardSchemaV1 for validating OIDC Token Responses.
376
+ * @param vendor - The name of the vendor creating this schema
377
+ * @returns A StandardSchemaV1 instance for Token Response validation
216
378
  */
217
- interface X509Certificate {
218
- /**
219
- * The value of the X.509 certificate
220
- */
221
- value: string;
222
- /**
223
- * A human-readable name, primarily used for display purposes
224
- */
225
- display?: string;
226
- /**
227
- * A label indicating the attribute's function
228
- */
229
- type?: string;
230
- /**
231
- * A Boolean value indicating the 'primary' or preferred attribute value
232
- */
233
- primary?: boolean;
379
+ declare function tokenResponseSchema(vendor: string): StandardSchemaV1<Record<string, unknown>, TokenResponse>;
380
+ /**
381
+ * ID Token Claims
382
+ */
383
+ interface IdTokenClaims {
384
+ iss?: string;
385
+ aud?: string;
386
+ exp?: number;
387
+ iat?: number;
388
+ sub?: string;
389
+ sid?: string;
390
+ name?: string;
391
+ email?: string;
392
+ preferred_username?: string;
393
+ picture?: string;
394
+ [key: string]: unknown;
234
395
  }
235
396
  /**
236
- * SCIM Enterprise User Extension
237
- * @see https://datatracker.ietf.org/doc/html/rfc7643#section-4.3
397
+ * Creates a StandardSchemaV1 for validating ID Token Claims.
398
+ * @param vendor - The name of the vendor creating this schema
399
+ * @returns A StandardSchemaV1 instance for ID Token Claims validation
238
400
  */
239
- interface EnterpriseExtension {
240
- /**
241
- * Numeric or alphanumeric identifier assigned to a person
242
- */
243
- employeeNumber?: string;
244
- /**
245
- * Identifies the name of a cost center
246
- */
247
- costCenter?: string;
248
- /**
249
- * Identifies the name of an organization
250
- */
251
- organization?: string;
252
- /**
253
- * Identifies the name of a division
254
- */
255
- division?: string;
256
- /**
257
- * Identifies the name of a department
258
- */
259
- department?: string;
401
+ declare function idTokenClaimsSchema(vendor: string): StandardSchemaV1<Record<string, unknown>, IdTokenClaims>;
402
+ /**
403
+ * Primary user type for SSO/OIDC applications.
404
+ * Extends BaseUser with SSO-specific data.
405
+ */
406
+ interface User extends BaseUser {
260
407
  /**
261
- * The user's manager
408
+ * SSO/OIDC authentication data
262
409
  */
263
- manager?: {
410
+ sso: {
264
411
  /**
265
- * The "id" of the SCIM resource representing the User's manager
412
+ * ID Token claims from the identity provider
266
413
  */
267
- value?: string;
414
+ profile: IdTokenClaims;
268
415
  /**
269
- * The URI of the SCIM resource representing the User's manager
416
+ * Tenant/organization information
270
417
  */
271
- $ref?: string;
418
+ tenant: {
419
+ id: string;
420
+ name: string;
421
+ };
272
422
  /**
273
- * The displayName of the User's manager
423
+ * OAuth scopes granted
274
424
  */
275
- displayName?: string;
425
+ scope?: string;
426
+ /**
427
+ * Token type (typically "Bearer")
428
+ */
429
+ tokenType: string;
430
+ /**
431
+ * Session state from the identity provider
432
+ */
433
+ sessionState?: string;
434
+ /**
435
+ * Token expiration time
436
+ */
437
+ expires: Date;
276
438
  };
277
439
  }
278
440
  /**
279
- * SCIM User Resource
441
+ * Stored user data with required id and tracking metadata.
442
+ *
443
+ * Extends the SSO User type with:
444
+ * - Required `id` (the `sub` claim from the IdP)
445
+ * - Timestamps for tracking when users were first seen and last updated
446
+ * - Optional custom extended data
447
+ *
448
+ * @template TExtended - Type-safe custom data that consumers can add to users
280
449
  */
281
- interface User {
282
- /**
283
- * REQUIRED. Unique identifier for the User, typically from the provider
284
- */
285
- id?: string;
286
- /**
287
- * REQUIRED. A unique identifier for a SCIM resource as defined by the service provider
288
- */
289
- externalId?: string;
450
+ type StoredUser<TExtended = {}> = User & {
290
451
  /**
291
- * Resource metadata
452
+ * Required unique identifier (the `sub` claim from the IdP).
453
+ * This is the primary key for user storage.
292
454
  */
293
- meta?: {
294
- resourceType?: string;
295
- created?: string;
296
- lastModified?: string;
297
- location?: string;
298
- version?: string;
299
- };
455
+ id: string;
300
456
  /**
301
- * REQUIRED. Unique identifier for the User, typically used for login
457
+ * Timestamp when the user was first stored.
302
458
  */
303
- userName: string;
459
+ createdAt: Date;
304
460
  /**
305
- * The components of the user's name
461
+ * Timestamp when the user was last updated (e.g., on re-login).
306
462
  */
307
- name?: Name;
308
- /**
309
- * The name of the User, suitable for display to end-users
310
- */
311
- displayName?: string;
312
- /**
313
- * The casual way to address the user
314
- */
315
- nickName?: string;
463
+ updatedAt: Date;
464
+ } & TExtended;
465
+ /**
466
+ * Abstract interface for user storage backends.
467
+ *
468
+ * Consumers can implement this interface to use different storage backends:
469
+ * - In-memory (for development/testing)
470
+ * - Redis (for production with fast lookups)
471
+ * - Database (PostgreSQL, MySQL, etc.)
472
+ *
473
+ * @template TExtended - Type-safe custom data that consumers can add to users
474
+ *
475
+ * @example
476
+ * ```typescript
477
+ * // Custom user data
478
+ * type MyUserData = {
479
+ * department: string;
480
+ * roles: string[];
481
+ * };
482
+ *
483
+ * // Implement custom store
484
+ * class RedisUserStore implements UserStore<MyUserData> {
485
+ * async get(sub: string): Promise<StoredUser<MyUserData> | null> {
486
+ * const data = await redis.get(`user:${sub}`);
487
+ * return data ? JSON.parse(data) : null;
488
+ * }
489
+ * // ... other methods
490
+ * }
491
+ * ```
492
+ */
493
+ interface UserStore<TExtended = {}> {
316
494
  /**
317
- * A fully qualified URL pointing to a page representing the User's online profile
495
+ * Retrieve a user by their subject identifier (sub).
496
+ *
497
+ * @param sub - The user's unique identifier from the IdP
498
+ * @returns The user if found, null otherwise
318
499
  */
319
- profileUrl?: string;
500
+ get(sub: string): Promise<StoredUser<TExtended> | null>;
320
501
  /**
321
- * The user's title, such as "Vice President"
502
+ * Retrieve a user by their email address.
503
+ *
504
+ * @param email - The user's email address
505
+ * @returns The user if found, null otherwise
322
506
  */
323
- title?: string;
507
+ getByEmail(email: string): Promise<StoredUser<TExtended> | null>;
324
508
  /**
325
- * Used to identify the relationship between the organization and the user
509
+ * Retrieve a user by their username.
510
+ *
511
+ * @param userName - The user's username
512
+ * @returns The user if found, null otherwise
326
513
  */
327
- userType?: string;
514
+ getByUserName(userName: string): Promise<StoredUser<TExtended> | null>;
328
515
  /**
329
- * Indicates the User's preferred written or spoken language
516
+ * Create or update a user in the store.
517
+ *
518
+ * If a user with the same `id` (sub) exists, it will be updated.
519
+ * Otherwise, a new user will be created.
520
+ *
521
+ * @param user - The user data to store
330
522
  */
331
- preferredLanguage?: string;
523
+ upsert(user: StoredUser<TExtended>): Promise<void>;
332
524
  /**
333
- * Used to indicate the User's default location for purposes of localizing items such as currency
525
+ * Delete a user by their subject identifier (sub).
526
+ *
527
+ * @param sub - The user's unique identifier to delete
334
528
  */
335
- locale?: string;
529
+ delete(sub: string): Promise<void>;
530
+ }
531
+ /**
532
+ * In-memory user store implementation using Maps.
533
+ *
534
+ * Suitable for:
535
+ * - Development and testing
536
+ * - Single-server deployments
537
+ * - Applications without high availability requirements
538
+ *
539
+ * NOT suitable for:
540
+ * - Multi-server deployments (users not shared)
541
+ * - High availability scenarios (users lost on restart)
542
+ * - Production applications with distributed architecture
543
+ *
544
+ * For production, implement UserStore with Redis or a database.
545
+ *
546
+ * @template TExtended - Type-safe custom data that consumers can add to users
547
+ */
548
+ declare class InMemoryUserStore<TExtended = {}> implements UserStore<TExtended> {
549
+ /** Primary storage: sub -> user */
550
+ private users;
551
+ /** Secondary index: email -> sub */
552
+ private emailIndex;
553
+ /** Secondary index: userName -> sub */
554
+ private userNameIndex;
555
+ get(sub: string): Promise<StoredUser<TExtended> | null>;
556
+ getByEmail(email: string): Promise<StoredUser<TExtended> | null>;
557
+ getByUserName(userName: string): Promise<StoredUser<TExtended> | null>;
558
+ upsert(user: StoredUser<TExtended>): Promise<void>;
559
+ delete(sub: string): Promise<void>;
560
+ }
561
+ import { StandardSchemaV1 as StandardSchemaV13 } from "@standard-schema/spec";
562
+ import { StandardSchemaV1 as StandardSchemaV12 } from "@standard-schema/spec";
563
+ /**
564
+ * JWT Assertion Claims for OAuth2 JWT Bearer Grant (RFC 7523) and OAuth2 Access Tokens
565
+ * @see https://datatracker.ietf.org/doc/html/rfc7523
566
+ * @see https://datatracker.ietf.org/doc/html/rfc9068
567
+ */
568
+ interface JWTAssertionClaims {
336
569
  /**
337
- * The User's time zone in the "Olson" time zone database format
570
+ * REQUIRED. Issuer - the workload identity (e.g., SPIFFE ID) or authorization server
338
571
  */
339
- timezone?: string;
572
+ iss: string;
340
573
  /**
341
- * A Boolean value indicating the User's administrative status
574
+ * REQUIRED. Subject - the workload identity or service account
342
575
  */
343
- active?: boolean;
576
+ sub: string;
344
577
  /**
345
- * The User's cleartext password
578
+ * OPTIONAL. Audience - may be a string or array of strings
579
+ * Note: Required for JWT assertions, but may be absent in OAuth2 access tokens
346
580
  */
347
- password?: string;
581
+ aud?: string | string[];
348
582
  /**
349
- * Email addresses for the user
583
+ * REQUIRED. Expiration time (Unix timestamp)
350
584
  */
351
- emails?: Email[];
585
+ exp: number;
352
586
  /**
353
- * Phone numbers for the User
587
+ * REQUIRED. Issued at time (Unix timestamp)
354
588
  */
355
- phoneNumbers?: PhoneNumber[];
589
+ iat: number;
356
590
  /**
357
- * Instant messaging addresses for the User
591
+ * OPTIONAL. JWT ID - unique identifier for this token
592
+ * Note: Required for JWT assertions, optional for access tokens
358
593
  */
359
- ims?: Array<{
360
- value: string;
361
- display?: string;
362
- type?: string;
363
- primary?: boolean;
364
- }>;
594
+ jti?: string;
365
595
  /**
366
- * URLs of photos of the User
596
+ * OPTIONAL. Requested OAuth scopes (space-delimited)
367
597
  */
368
- photos?: Array<{
369
- value: string;
370
- display?: string;
371
- type?: string;
372
- primary?: boolean;
373
- }>;
598
+ scope?: string;
374
599
  /**
375
- * Physical mailing addresses for this User
600
+ * Allow additional claims for extensibility
376
601
  */
377
- addresses?: Address[];
602
+ [key: string]: unknown;
603
+ }
604
+ /**
605
+ * Creates a StandardSchemaV1 for validating JWT Assertion Claims.
606
+ * @param vendor - The name of the vendor creating this schema
607
+ * @returns A StandardSchemaV1 instance for JWT Assertion Claims validation
608
+ */
609
+ declare function jwtAssertionClaimsSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, JWTAssertionClaims>;
610
+ /**
611
+ * Workload Token Response from OAuth2 token endpoint
612
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
613
+ */
614
+ interface WorkloadTokenResponse {
378
615
  /**
379
- * A list of groups to which the user belongs
616
+ * REQUIRED. The access token issued by the authorization server.
380
617
  */
381
- groups?: Group[];
618
+ access_token: string;
382
619
  /**
383
- * A list of entitlements for the User
620
+ * REQUIRED. The type of the token (typically "Bearer").
384
621
  */
385
- entitlements?: Array<{
386
- value: string;
387
- display?: string;
388
- type?: string;
389
- primary?: boolean;
390
- }>;
622
+ token_type: string;
391
623
  /**
392
- * A list of roles for the User
624
+ * RECOMMENDED. The lifetime in seconds of the access token.
393
625
  */
394
- roles?: Role[];
626
+ expires_in?: number;
395
627
  /**
396
- * A list of certificates issued to the User
628
+ * OPTIONAL. The scope of the access token.
397
629
  */
398
- x509Certificates?: X509Certificate[];
630
+ scope?: string;
399
631
  /**
400
- * Enterprise User Extension
632
+ * OPTIONAL. The refresh token (rarely used for workload identities).
401
633
  */
402
- "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"?: EnterpriseExtension;
634
+ refresh_token?: string;
403
635
  /**
404
- * REQUIRED. The schemas attribute is an array of Strings which allows introspection of the supported schema version
636
+ * OPTIONAL. The expiration time as an ISO 8601 string.
405
637
  */
406
- schemas?: string[];
638
+ expires?: string;
407
639
  }
408
640
  /**
409
- * Creates a StandardSchemaV1 for validating SCIM User resources.
410
- * @param vendor - The name of the vendor creating this schema
411
- * @returns A StandardSchemaV1 instance for SCIM User resources
412
- */
413
- declare function userSchema(vendor: string): StandardSchemaV1<Record<string, unknown>, User>;
414
- /**
415
- * Creates a StandardSchemaV1 for validating SCIM Group resources.
641
+ * Creates a StandardSchemaV1 for validating Workload Token Responses.
416
642
  * @param vendor - The name of the vendor creating this schema
417
- * @returns A StandardSchemaV1 instance for SCIM Group resources
643
+ * @returns A StandardSchemaV1 instance for Workload Token Response validation
418
644
  */
419
- declare function groupResourceSchema(vendor: string): StandardSchemaV1<Record<string, unknown>, GroupResource>;
645
+ declare function workloadTokenResponseSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, WorkloadTokenResponse>;
420
646
  /**
421
- * Stored group data with required id and tracking metadata.
422
- *
423
- * @template TExtended - Type-safe custom data that consumers can add to groups
647
+ * Token Validation Result
424
648
  */
425
- type StoredGroup<TExtended = {}> = {
426
- /**
427
- * Required unique identifier for the group.
428
- * This is the primary key for group storage.
429
- */
430
- id: string;
431
- /**
432
- * Required human-readable name for the group.
433
- */
434
- displayName: string;
649
+ interface TokenValidationResult {
435
650
  /**
436
- * Optional external identifier from provisioning client.
651
+ * Whether the token is valid
437
652
  */
438
- externalId?: string;
653
+ valid: boolean;
439
654
  /**
440
- * List of members in the group.
655
+ * The decoded and validated claims (if valid)
441
656
  */
442
- members?: GroupMember[];
657
+ claims?: JWTAssertionClaims;
443
658
  /**
444
- * Timestamp when the group was first stored.
659
+ * Error message (if invalid)
445
660
  */
446
- createdAt: Date;
661
+ error?: string;
447
662
  /**
448
- * Timestamp when the group was last updated.
663
+ * Token expiration time (if valid)
449
664
  */
450
- updatedAt: Date;
451
- } & TExtended;
665
+ expiresAt?: Date;
666
+ }
452
667
  /**
453
- * Abstract interface for group storage backends.
668
+ * Token caching for workload identity authentication.
454
669
  *
455
- * Consumers can implement this interface to use different storage backends:
456
- * - In-memory (for development/testing)
457
- * - Redis (for production with fast lookups)
458
- * - Database (PostgreSQL, MySQL, etc.)
670
+ * Token stores are optional but recommended for performance - they enable:
671
+ * - Token caching to avoid repeated token acquisition
672
+ * - Automatic token refresh before expiration
673
+ * - Reduced load on authorization servers
459
674
  *
460
- * @template TExtended - Type-safe custom data that consumers can add to groups
461
- */
462
- interface GroupStore<TExtended = {}> {
463
- /**
464
- * Retrieve a group by its unique identifier.
465
- *
466
- * @param id - The group's unique identifier
467
- * @returns The group if found, null otherwise
675
+ * ## Token Caching Strategy
676
+ *
677
+ * Unlike session stores for user authentication, workload token stores cache
678
+ * short-lived access tokens (typically 5 minutes) for service-to-service calls.
679
+ *
680
+ * **Default Behavior:**
681
+ * - Tokens cached with 5-minute TTL
682
+ * - Auto-refresh 60 seconds before expiration
683
+ * - Expired tokens automatically removed
684
+ *
685
+ * ## Performance Characteristics
686
+ *
687
+ * | Backend | Lookup Time | Use Case |
688
+ * |--------------|-------------|----------------------------|
689
+ * | InMemory | <0.00005ms | Single-server deployments |
690
+ * | Redis | 1-2ms | Multi-server deployments |
691
+ * | Database | 5-20ms | Persistent token storage |
692
+ *
693
+ * ## Example Usage
694
+ *
695
+ * ```typescript
696
+ * import { workload, InMemoryWorkloadTokenStore } from '@enterprisestandard/react/server';
697
+ *
698
+ * // With token caching
699
+ * const workloadAuth = workload({
700
+ * // ... other config
701
+ * tokenStore: new InMemoryWorkloadTokenStore(),
702
+ * auto_refresh: true, // Refresh before expiry
703
+ * });
704
+ *
705
+ * // Without token caching (fetch new token each time)
706
+ * const workloadAuth = workload({
707
+ * // ... config without tokenStore
708
+ * });
709
+ * ```
710
+ */
711
+ /**
712
+ * Cached workload token data.
713
+ *
714
+ * @template TExtended - Type-safe custom data that consumers can add to cached tokens
715
+ */
716
+ type CachedWorkloadToken<TExtended = object> = {
717
+ /**
718
+ * Workload identifier (typically SPIFFE ID).
719
+ * Used as the primary key for token lookup.
468
720
  */
469
- get(id: string): Promise<StoredGroup<TExtended> | null>;
721
+ workload_id: string;
470
722
  /**
471
- * Retrieve a group by its external identifier.
472
- *
473
- * @param externalId - The external identifier from the provisioning client
474
- * @returns The group if found, null otherwise
723
+ * OAuth2 access token (Bearer token)
475
724
  */
476
- getByExternalId(externalId: string): Promise<StoredGroup<TExtended> | null>;
725
+ access_token: string;
477
726
  /**
478
- * Retrieve a group by its display name.
479
- *
480
- * @param displayName - The group's display name
481
- * @returns The group if found, null otherwise
727
+ * Token type (always "Bearer" for OAuth2)
482
728
  */
483
- getByDisplayName(displayName: string): Promise<StoredGroup<TExtended> | null>;
729
+ token_type: string;
484
730
  /**
485
- * List all groups in the store.
486
- *
487
- * @returns Array of all stored groups
731
+ * OAuth2 scopes granted for this token
488
732
  */
489
- list(): Promise<StoredGroup<TExtended>[]>;
733
+ scope?: string;
490
734
  /**
491
- * Create or update a group in the store.
735
+ * Timestamp when the token expires.
736
+ * Used for automatic cleanup and refresh logic.
737
+ */
738
+ expires_at: Date;
739
+ /**
740
+ * Timestamp when the token was created/cached.
741
+ */
742
+ created_at: Date;
743
+ /**
744
+ * Optional refresh token (rarely used for workload identities)
745
+ */
746
+ refresh_token?: string;
747
+ } & TExtended;
748
+ /**
749
+ * Abstract interface for workload token storage backends.
750
+ *
751
+ * Consumers can implement this interface to use different storage backends:
752
+ * - Redis (recommended for multi-server deployments)
753
+ * - Database (PostgreSQL, MySQL, etc. for persistence)
754
+ * - Distributed cache (Memcached, etc.)
755
+ * - Custom solutions
756
+ *
757
+ * @template TExtended - Type-safe custom data that consumers can add to cached tokens
758
+ *
759
+ * @example
760
+ * ```typescript
761
+ * // Custom token cache data
762
+ * type MyTokenData = {
763
+ * environment: string;
764
+ * region: string;
765
+ * };
766
+ *
767
+ * // Implement custom store with Redis
768
+ * class RedisWorkloadTokenStore implements WorkloadTokenStore<MyTokenData> {
769
+ * async set(token: CachedWorkloadToken<MyTokenData>): Promise<void> {
770
+ * const ttl = Math.floor((token.expires_at.getTime() - Date.now()) / 1000);
771
+ * await redis.setex(
772
+ * `workload:token:${token.workload_id}`,
773
+ * ttl,
774
+ * JSON.stringify(token)
775
+ * );
776
+ * }
777
+ *
778
+ * async get(workload_id: string): Promise<CachedWorkloadToken<MyTokenData> | null> {
779
+ * const data = await redis.get(`workload:token:${workload_id}`);
780
+ * if (!data) return null;
781
+ * const token = JSON.parse(data);
782
+ * // Convert date strings back to Date objects
783
+ * token.expires_at = new Date(token.expires_at);
784
+ * token.created_at = new Date(token.created_at);
785
+ * return token;
786
+ * }
787
+ *
788
+ * // ... other methods
789
+ * }
790
+ * ```
791
+ */
792
+ interface WorkloadTokenStore<TExtended = object> {
793
+ /**
794
+ * Store or update a workload token in the cache.
492
795
  *
493
- * If a group with the same `id` exists, it will be updated.
494
- * Otherwise, a new group will be created.
796
+ * @param token - The token data to cache
797
+ */
798
+ set(token: CachedWorkloadToken<TExtended>): Promise<void>;
799
+ /**
800
+ * Retrieve a cached token by workload ID.
495
801
  *
496
- * @param group - The group data to store
802
+ * @param workload_id - The workload identifier (e.g., SPIFFE ID)
803
+ * @returns The cached token if found and not expired, null otherwise
497
804
  */
498
- upsert(group: StoredGroup<TExtended>): Promise<void>;
805
+ get(workload_id: string): Promise<CachedWorkloadToken<TExtended> | null>;
499
806
  /**
500
- * Delete a group by its unique identifier.
807
+ * Delete a cached token by workload ID.
501
808
  *
502
- * @param id - The group's unique identifier to delete
809
+ * Used when explicitly revoking tokens or clearing cache.
810
+ *
811
+ * @param workload_id - The workload identifier to remove
503
812
  */
504
- delete(id: string): Promise<void>;
813
+ delete(workload_id: string): Promise<void>;
505
814
  /**
506
- * Add a member to a group.
815
+ * Check if a valid (non-expired) token exists for a workload.
507
816
  *
508
- * @param groupId - The group's unique identifier
509
- * @param member - The member to add
817
+ * @param workload_id - The workload identifier to check
818
+ * @returns true if a valid token exists, false otherwise
510
819
  */
511
- addMember(groupId: string, member: GroupMember): Promise<void>;
820
+ isValid(workload_id: string): Promise<boolean>;
512
821
  /**
513
- * Remove a member from a group.
822
+ * Remove all expired tokens from the cache.
514
823
  *
515
- * @param groupId - The group's unique identifier
516
- * @param memberId - The member's value/id to remove
824
+ * Should be called periodically to prevent memory leaks.
517
825
  */
518
- removeMember(groupId: string, memberId: string): Promise<void>;
826
+ cleanup(): Promise<void>;
519
827
  }
520
828
  /**
521
- * In-memory group store implementation using Maps.
829
+ * In-memory workload token store implementation using Maps.
522
830
  *
523
831
  * Suitable for:
524
832
  * - Development and testing
@@ -526,790 +834,898 @@ interface GroupStore<TExtended = {}> {
526
834
  * - Applications without high availability requirements
527
835
  *
528
836
  * NOT suitable for:
529
- * - Multi-server deployments (groups not shared)
530
- * - High availability scenarios (groups lost on restart)
837
+ * - Multi-server deployments (tokens not shared across instances)
838
+ * - High availability scenarios (tokens lost on restart)
531
839
  * - Production applications with distributed architecture
532
840
  *
533
- * For production, implement GroupStore with Redis or a database.
841
+ * For production multi-server deployments, implement WorkloadTokenStore with Redis.
534
842
  *
535
- * @template TExtended - Type-safe custom data that consumers can add to groups
843
+ * @template TExtended - Type-safe custom data that consumers can add to cached tokens
536
844
  */
537
- declare class InMemoryGroupStore<TExtended = {}> implements GroupStore<TExtended> {
538
- /** Primary storage: id -> group */
539
- private groups;
540
- /** Secondary index: externalId -> id */
541
- private externalIdIndex;
542
- /** Secondary index: displayName (lowercase) -> id */
543
- private displayNameIndex;
544
- get(id: string): Promise<StoredGroup<TExtended> | null>;
545
- getByExternalId(externalId: string): Promise<StoredGroup<TExtended> | null>;
546
- getByDisplayName(displayName: string): Promise<StoredGroup<TExtended> | null>;
547
- list(): Promise<StoredGroup<TExtended>[]>;
548
- upsert(group: StoredGroup<TExtended>): Promise<void>;
549
- delete(id: string): Promise<void>;
550
- addMember(groupId: string, member: GroupMember): Promise<void>;
551
- removeMember(groupId: string, memberId: string): Promise<void>;
845
+ declare class InMemoryWorkloadTokenStore<TExtended = object> implements WorkloadTokenStore<TExtended> {
846
+ private tokens;
847
+ set(token: CachedWorkloadToken<TExtended>): Promise<void>;
848
+ get(workload_id: string): Promise<CachedWorkloadToken<TExtended> | null>;
849
+ delete(workload_id: string): Promise<void>;
850
+ isValid(workload_id: string): Promise<boolean>;
851
+ cleanup(): Promise<void>;
552
852
  }
553
853
  /**
554
- * Base user with simple, developer-friendly attributes.
555
- * Extended by User (SSO) and EnterpriseUser (SCIM).
854
+ * Common fields shared across all workload authentication modes
556
855
  */
557
- interface BaseUser {
856
+ type WorkloadConfigBase = {
558
857
  /**
559
- * Unique identifier for the user
858
+ * OAuth2 token endpoint URL
859
+ * REQUIRED for client role (token acquisition)
560
860
  */
561
- id?: string;
861
+ tokenUrl?: string;
562
862
  /**
563
- * REQUIRED. Unique identifier for login
863
+ * JWKS endpoint URL for public key retrieval
864
+ * REQUIRED for server role (token validation)
564
865
  */
565
- userName: string;
866
+ jwksUri?: string;
566
867
  /**
567
- * REQUIRED. Simple display name
868
+ * Expected token issuer URL for validation
869
+ * RECOMMENDED for server role (token validation)
568
870
  */
569
- name: string;
871
+ issuer?: string;
570
872
  /**
571
- * REQUIRED. Primary email address
873
+ * Target audience for tokens
572
874
  */
573
- email: string;
875
+ audience?: string;
574
876
  /**
575
- * URL to user's avatar/profile picture
877
+ * Default OAuth2 scopes (space-delimited)
576
878
  */
577
- avatarUrl?: string;
578
- }
579
- import { StandardSchemaV1 as StandardSchemaV12 } from "@standard-schema/spec";
580
- /**
581
- * OIDC Code Flow Callback URL Parameters
582
- * @see https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
583
- */
584
- interface OidcCallbackParams {
879
+ scope?: string;
585
880
  /**
586
- * REQUIRED. The authorization code returned from the authorization server.
881
+ * JWT assertion/token lifetime in seconds
882
+ * @default 300 (5 minutes)
587
883
  */
588
- code: string;
884
+ tokenLifetime?: number;
589
885
  /**
590
- * REQUIRED if the "state" parameter was present in the client authorization request.
591
- * The exact value received from the client.
886
+ * Refresh threshold in seconds (refresh token this many seconds before expiry)
887
+ * @default 60
592
888
  */
593
- state?: string;
889
+ refreshThreshold?: number;
594
890
  /**
595
- * RECOMMENDED. The session state value. Clients should use this to verify the session state.
891
+ * Optional token store for caching access tokens
596
892
  */
597
- session_state?: string;
893
+ tokenStore?: WorkloadTokenStore;
598
894
  /**
599
- * OAuth 2.0 error code if the authorization request failed.
895
+ * Automatically refresh tokens before expiration
896
+ * @default true
600
897
  */
601
- error?: string;
898
+ autoRefresh?: boolean;
602
899
  /**
603
- * Human-readable ASCII text providing additional information for the error.
900
+ * Optional RFC 7009 token revocation endpoint
604
901
  */
605
- error_description?: string;
902
+ revocationEndpoint?: string;
606
903
  /**
607
- * A URI identifying a human-readable web page with information about the error.
904
+ * Optional handler defaults (merged with per-call overrides in `handler`)
608
905
  */
609
- error_uri?: string;
906
+ validateUrl?: string;
907
+ jwksUrl?: string;
908
+ refreshUrl?: string;
909
+ validators?: WorkloadValidators;
910
+ };
911
+ type WorkloadValidators = {
912
+ jwtAssertionClaims: StandardSchemaV13<unknown, JWTAssertionClaims>;
913
+ tokenResponse: StandardSchemaV13<unknown, WorkloadTokenResponse>;
914
+ };
915
+ /**
916
+ * JWT Bearer Grant (RFC 7523) Configuration
917
+ *
918
+ * Used for SPIFFE-style workload identities where services have their own
919
+ * cryptographic identity and sign their own JWT assertions.
920
+ *
921
+ * @example
922
+ * ```json
923
+ * {
924
+ * "token_url": "https://auth.example.com/oauth/token",
925
+ * "jwks_uri": "https://auth.example.com/.well-known/jwks.json",
926
+ * "workload_id": "spiffe://trust-domain/ns/service",
927
+ * "audience": "https://auth.example.com/oauth/token",
928
+ * "private_key": "-----BEGIN PRIVATE KEY-----...",
929
+ * "algorithm": "RS256"
930
+ * }
931
+ * ```
932
+ */
933
+ type JwtBearerWorkloadConfig = WorkloadConfigBase & {
610
934
  /**
611
- * The "iss" (issuer) parameter identifies the principal that issued the response.
612
- * This is typically used in the implicit flow.
935
+ * Workload identifier (e.g., SPIFFE ID: spiffe://trust-domain/namespace/service)
936
+ * REQUIRED for JWT Bearer Grant mode
613
937
  */
614
- iss?: string;
615
- }
938
+ workloadId: string;
939
+ /**
940
+ * PEM-encoded private key for signing JWT assertions
941
+ * REQUIRED for client role in JWT Bearer Grant mode
942
+ */
943
+ privateKey: string;
944
+ /**
945
+ * Key ID (kid) to include in JWT header for key rotation support
946
+ */
947
+ keyId?: string;
948
+ /**
949
+ * JWT signing algorithm
950
+ * @default 'RS256'
951
+ */
952
+ algorithm?: "RS256" | "RS384" | "RS512" | "ES256" | "ES384" | "ES512";
953
+ };
616
954
  /**
617
- * Creates a StandardSchemaV1 for validating OIDC callback URL parameters.
618
- * @param vendor - The name of the vendor creating this schema
619
- * @returns A StandardSchemaV1 instance for OIDC callback parameters
955
+ * OAuth2 Client Credentials Configuration
956
+ *
957
+ * Standard OAuth2 Client Credentials Grant (RFC 6749 Section 4.4).
958
+ * Used with identity providers like Keycloak for service-to-service authentication.
959
+ *
960
+ * @example
961
+ * ```json
962
+ * {
963
+ * "token_url": "https://sso.example.com/realms/myrealm/protocol/openid-connect/token",
964
+ * "jwks_uri": "https://sso.example.com/realms/myrealm/protocol/openid-connect/certs",
965
+ * "client_id": "my-service",
966
+ * "client_secret": "secret-from-idp",
967
+ * "issuer": "https://sso.example.com/realms/myrealm",
968
+ * "scope": "api:read api:write"
969
+ * }
970
+ * ```
620
971
  */
621
- declare function oidcCallbackSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, OidcCallbackParams>;
972
+ type ClientCredentialsWorkloadConfig = WorkloadConfigBase & {
973
+ /**
974
+ * OAuth2 client identifier registered with the authorization server
975
+ * REQUIRED for Client Credentials mode
976
+ */
977
+ clientId: string;
978
+ /**
979
+ * OAuth2 client secret
980
+ * REQUIRED for Client Credentials mode
981
+ */
982
+ clientSecret: string;
983
+ };
622
984
  /**
623
- * Token Response from IdP
985
+ * Server-Only Workload Configuration
986
+ *
987
+ * Used when a service only needs to validate incoming workload tokens,
988
+ * not acquire tokens for outbound calls. Requires only jwks_uri for
989
+ * public key retrieval.
990
+ *
991
+ * Note: jwksUri and issuer are optional in TypeScript (since they come from vault)
992
+ * but are required at runtime and validated by standardSchema validators.
993
+ *
994
+ * @example
995
+ * ```json
996
+ * {
997
+ * "jwks_uri": "https://sso.example.com/realms/myrealm/protocol/openid-connect/certs",
998
+ * "issuer": "https://sso.example.com/realms/myrealm"
999
+ * }
1000
+ * ```
624
1001
  */
625
- interface TokenResponse {
626
- access_token: string;
627
- id_token: string;
628
- refresh_token?: string;
629
- token_type: string;
630
- expires_in?: number;
1002
+ type ServerOnlyWorkloadConfig = WorkloadConfigBase & {
1003
+ /**
1004
+ * JWKS endpoint URL for public key retrieval
1005
+ * REQUIRED at runtime (validated by standardSchema), optional in TypeScript (comes from vault)
1006
+ */
1007
+ jwksUri?: string;
1008
+ };
1009
+ /**
1010
+ * Workload Identity Authentication Configuration
1011
+ *
1012
+ * Union type supporting multiple authentication modes. The mode is automatically
1013
+ * detected based on which fields are present:
1014
+ *
1015
+ * - **JWT Bearer Grant**: Requires `workload_id` + `private_key`
1016
+ * - **Client Credentials**: Requires `client_id` + `client_secret`
1017
+ * - **Server-Only**: Only `jwks_uri` (and optionally `issuer`) for token validation
1018
+ *
1019
+ * The developer uses the same API regardless of mode - the library handles the details.
1020
+ */
1021
+ type WorkloadConfig = JwtBearerWorkloadConfig | ClientCredentialsWorkloadConfig | ServerOnlyWorkloadConfig;
1022
+ /**
1023
+ * Workload Identity extracted from validated tokens
1024
+ */
1025
+ type WorkloadIdentity = {
1026
+ /**
1027
+ * Workload identifier (for JWT Bearer Grant tokens)
1028
+ */
1029
+ workloadId?: string;
1030
+ /**
1031
+ * Client identifier (for OAuth2 Client Credentials tokens)
1032
+ */
1033
+ clientId?: string;
1034
+ /**
1035
+ * Granted scopes
1036
+ */
631
1037
  scope?: string;
632
- refresh_expires_in?: number;
633
- session_state?: string;
634
- expires?: string;
635
- }
1038
+ /**
1039
+ * Full JWT claims from the token
1040
+ */
1041
+ claims: JWTAssertionClaims;
1042
+ };
636
1043
  /**
637
- * Creates a StandardSchemaV1 for validating OIDC Token Responses.
638
- * @param vendor - The name of the vendor creating this schema
639
- * @returns A StandardSchemaV1 instance for Token Response validation
1044
+ * Workload Identity Authentication Interface
640
1045
  */
641
- declare function tokenResponseSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, TokenResponse>;
1046
+ type Workload = {
1047
+ config: WorkloadConfig;
1048
+ getToken: (scope?: string) => Promise<string>;
1049
+ refreshToken: (scope?: string) => Promise<WorkloadTokenResponse>;
1050
+ generateJWTAssertion: (scope?: string) => Promise<string>;
1051
+ revokeToken: (token: string) => Promise<void>;
1052
+ validateToken: (token: string) => Promise<TokenValidationResult>;
1053
+ getWorkloadIdentity: (request: Request) => Promise<WorkloadIdentity | undefined>;
1054
+ parseJWT: (token: string) => Promise<JWTAssertionClaims>;
1055
+ handler: (request: Request) => Promise<Response>;
1056
+ };
1057
+ declare function workload(validators: WorkloadValidators, fromVault?: Partial<WorkloadConfig>, fromCode?: Partial<WorkloadConfig>): Workload | undefined;
1058
+ type CIAMConfig<
1059
+ TMagicLinkData = {},
1060
+ TUserData = {}
1061
+ > = {
1062
+ /**
1063
+ * Magic link store for storing magic link tokens
1064
+ */
1065
+ magicLinkStore?: MagicLinkStore<TMagicLinkData>;
1066
+ /**
1067
+ * Default TTL for magic links in seconds
1068
+ * @default 3600 (1 hour)
1069
+ */
1070
+ magicLinkTtl?: number;
1071
+ /**
1072
+ * URL path for generating magic links (POST endpoint)
1073
+ */
1074
+ magicLinkUrl?: string;
1075
+ /**
1076
+ * URL path for validating magic links (GET endpoint)
1077
+ */
1078
+ magicLinkLoginUrl?: string;
1079
+ /**
1080
+ * Landing URL after successful magic link authentication
1081
+ */
1082
+ landingUrl?: string;
1083
+ /**
1084
+ * Error URL for failed magic link authentication
1085
+ */
1086
+ errorUrl?: string;
1087
+ /**
1088
+ * Cookie prefix for CIAM cookies
1089
+ * @default 'es.ciam'
1090
+ */
1091
+ cookiesPrefix?: string;
1092
+ /**
1093
+ * Cookie path
1094
+ * @default '/'
1095
+ */
1096
+ cookiesPath?: string;
1097
+ /**
1098
+ * Secure cookies (HTTPS only)
1099
+ * @default true
1100
+ */
1101
+ cookiesSecure?: boolean;
1102
+ /**
1103
+ * SameSite cookie attribute
1104
+ * @default 'Strict'
1105
+ */
1106
+ cookiesSameSite?: "Strict" | "Lax";
1107
+ /**
1108
+ * Optional identifier for CIAM cookies (used in prefix).
1109
+ * When set, cookiesPrefix defaults to `es.ciam.${ciamId}`.
1110
+ */
1111
+ ciamId?: string;
1112
+ /**
1113
+ * Signing key for CIAM JWT tokens.
1114
+ *
1115
+ * Generate using one of these methods:
1116
+ * - OpenSSL: openssl rand -base64 64
1117
+ * - Node.js: node -e "console.log(require('crypto').randomBytes(64).toString('base64'))"
1118
+ * - Python: python3 -c "import secrets; print(secrets.token_urlsafe(64))"
1119
+ *
1120
+ * Store in Vault under ciam.signingKey or provide in code config.
1121
+ * REQUIRED - will throw error during initialization if missing.
1122
+ */
1123
+ signingKey?: string;
1124
+ /**
1125
+ * Server-side session store for CIAM sessions (required for backchannel logout).
1126
+ */
1127
+ sessionStore?: SessionStore;
1128
+ /**
1129
+ * Session validation strategy.
1130
+ * @default 'always'
1131
+ */
1132
+ sessionValidation?: "always" | "refresh-only" | "disabled";
1133
+ /**
1134
+ * Logout URL (GET endpoint)
1135
+ */
1136
+ logoutUrl?: string;
1137
+ /**
1138
+ * Back-channel logout URL (POST endpoint)
1139
+ */
1140
+ logoutBackChannelUrl?: string;
1141
+ /**
1142
+ * Optional user store for persisting user profiles from magic link authentication.
1143
+ * When configured, users are automatically stored/updated on each login.
1144
+ */
1145
+ userStore?: UserStore<TUserData>;
1146
+ /**
1147
+ * Enable Just-In-Time (JIT) user provisioning.
1148
+ * When enabled, new users are automatically created in the userStore on their first login.
1149
+ * When disabled (default), only existing users in the userStore are updated on login.
1150
+ * Requires userStore to be configured.
1151
+ * @default false
1152
+ */
1153
+ enableJitUserProvisioning?: boolean;
1154
+ /**
1155
+ * Session expiration time in seconds (for cookies)
1156
+ * @default 86400 (24 hours)
1157
+ */
1158
+ sessionTtl?: number;
1159
+ };
1160
+ type CIAM<
1161
+ TMagicLinkData = {},
1162
+ TUserData = {}
1163
+ > = {
1164
+ config: CIAMConfig<TMagicLinkData, TUserData>;
1165
+ getUser: (request: Request) => Promise<User | undefined>;
1166
+ getRequiredUser: (request: Request) => Promise<User>;
1167
+ logout: (request: Request) => Promise<Response>;
1168
+ logoutBackChannel: (request: Request) => Promise<Response>;
1169
+ handler: (request: Request) => Promise<Response>;
1170
+ };
1171
+ declare function ciam<
1172
+ TMagicLinkData = {},
1173
+ TUserData = {}
1174
+ >(fromVault?: Partial<CIAMConfig<TMagicLinkData, TUserData>>, fromCode?: Partial<CIAMConfig<TMagicLinkData, TUserData>>, workload?: {
1175
+ getWorkloadIdentity: (request: Request) => Promise<WorkloadIdentity | undefined>;
1176
+ }): CIAM<TMagicLinkData, TUserData> | undefined;
1177
+ import { StandardSchemaV1 as StandardSchemaV15 } from "@standard-schema/spec";
1178
+ import { StandardSchemaV1 as StandardSchemaV14 } from "@standard-schema/spec";
642
1179
  /**
643
- * ID Token Claims
1180
+ * SCIM 2.0 User Resource
1181
+ * @see https://datatracker.ietf.org/doc/html/rfc7643#section-4.1
644
1182
  */
645
- interface IdTokenClaims {
646
- iss?: string;
647
- aud?: string;
648
- exp?: number;
649
- iat?: number;
650
- sub?: string;
651
- sid?: string;
652
- name?: string;
653
- email?: string;
654
- preferred_username?: string;
655
- picture?: string;
656
- [key: string]: unknown;
657
- }
658
1183
  /**
659
- * Creates a StandardSchemaV1 for validating ID Token Claims.
660
- * @param vendor - The name of the vendor creating this schema
661
- * @returns A StandardSchemaV1 instance for ID Token Claims validation
1184
+ * SCIM Name sub-attribute
662
1185
  */
663
- declare function idTokenClaimsSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, IdTokenClaims>;
1186
+ interface Name {
1187
+ /**
1188
+ * The full name, including all middle names, titles, and suffixes as appropriate
1189
+ */
1190
+ formatted?: string;
1191
+ /**
1192
+ * The family name of the User, or last name
1193
+ */
1194
+ familyName?: string;
1195
+ /**
1196
+ * The given name of the User, or first name
1197
+ */
1198
+ givenName?: string;
1199
+ /**
1200
+ * The middle name(s) of the User
1201
+ */
1202
+ middleName?: string;
1203
+ /**
1204
+ * The honorific prefix(es) of the User, or title
1205
+ */
1206
+ honorificPrefix?: string;
1207
+ /**
1208
+ * The honorific suffix(es) of the User, or suffix
1209
+ */
1210
+ honorificSuffix?: string;
1211
+ }
664
1212
  /**
665
- * Primary user type for SSO/OIDC applications.
666
- * Extends BaseUser with SSO-specific data.
1213
+ * SCIM Email sub-attribute
667
1214
  */
668
- interface User2 extends BaseUser {
1215
+ interface Email {
1216
+ /**
1217
+ * The email address value
1218
+ */
1219
+ value: string;
1220
+ /**
1221
+ * A human-readable name, primarily used for display purposes
1222
+ */
1223
+ display?: string;
1224
+ /**
1225
+ * A label indicating the attribute's function (e.g., "work" or "home")
1226
+ */
1227
+ type?: string;
669
1228
  /**
670
- * SSO/OIDC authentication data
1229
+ * A Boolean value indicating the 'primary' or preferred attribute value
671
1230
  */
672
- sso: {
673
- /**
674
- * ID Token claims from the identity provider
675
- */
676
- profile: IdTokenClaims;
677
- /**
678
- * Tenant/organization information
679
- */
680
- tenant: {
681
- id: string;
682
- name: string;
683
- };
684
- /**
685
- * OAuth scopes granted
686
- */
687
- scope?: string;
688
- /**
689
- * Token type (typically "Bearer")
690
- */
691
- tokenType: string;
692
- /**
693
- * Session state from the identity provider
694
- */
695
- sessionState?: string;
696
- /**
697
- * Token expiration time
698
- */
699
- expires: Date;
700
- };
1231
+ primary?: boolean;
701
1232
  }
702
1233
  /**
703
- * Stored user data with required id and tracking metadata.
704
- *
705
- * Extends the SSO User type with:
706
- * - Required `id` (the `sub` claim from the IdP)
707
- * - Timestamps for tracking when users were first seen and last updated
708
- * - Optional custom extended data
709
- *
710
- * @template TExtended - Type-safe custom data that consumers can add to users
1234
+ * SCIM Phone Number sub-attribute
711
1235
  */
712
- type StoredUser<TExtended = {}> = User2 & {
1236
+ interface PhoneNumber {
713
1237
  /**
714
- * Required unique identifier (the `sub` claim from the IdP).
715
- * This is the primary key for user storage.
1238
+ * The phone number value
716
1239
  */
717
- id: string;
1240
+ value: string;
718
1241
  /**
719
- * Timestamp when the user was first stored.
1242
+ * A human-readable name, primarily used for display purposes
720
1243
  */
721
- createdAt: Date;
1244
+ display?: string;
722
1245
  /**
723
- * Timestamp when the user was last updated (e.g., on re-login).
1246
+ * A label indicating the attribute's function (e.g., "work", "home", "mobile")
724
1247
  */
725
- updatedAt: Date;
726
- } & TExtended;
1248
+ type?: string;
1249
+ /**
1250
+ * A Boolean value indicating the 'primary' or preferred attribute value
1251
+ */
1252
+ primary?: boolean;
1253
+ }
727
1254
  /**
728
- * Abstract interface for user storage backends.
729
- *
730
- * Consumers can implement this interface to use different storage backends:
731
- * - In-memory (for development/testing)
732
- * - Redis (for production with fast lookups)
733
- * - Database (PostgreSQL, MySQL, etc.)
734
- *
735
- * @template TExtended - Type-safe custom data that consumers can add to users
736
- *
737
- * @example
738
- * ```typescript
739
- * // Custom user data
740
- * type MyUserData = {
741
- * department: string;
742
- * roles: string[];
743
- * };
744
- *
745
- * // Implement custom store
746
- * class RedisUserStore implements UserStore<MyUserData> {
747
- * async get(sub: string): Promise<StoredUser<MyUserData> | null> {
748
- * const data = await redis.get(`user:${sub}`);
749
- * return data ? JSON.parse(data) : null;
750
- * }
751
- * // ... other methods
752
- * }
753
- * ```
1255
+ * SCIM Address sub-attribute
754
1256
  */
755
- interface UserStore<TExtended = {}> {
1257
+ interface Address {
756
1258
  /**
757
- * Retrieve a user by their subject identifier (sub).
758
- *
759
- * @param sub - The user's unique identifier from the IdP
760
- * @returns The user if found, null otherwise
1259
+ * The full mailing address, formatted for display
761
1260
  */
762
- get(sub: string): Promise<StoredUser<TExtended> | null>;
1261
+ formatted?: string;
763
1262
  /**
764
- * Retrieve a user by their email address.
765
- *
766
- * @param email - The user's email address
767
- * @returns The user if found, null otherwise
1263
+ * The full street address component
768
1264
  */
769
- getByEmail(email: string): Promise<StoredUser<TExtended> | null>;
1265
+ streetAddress?: string;
770
1266
  /**
771
- * Retrieve a user by their username.
772
- *
773
- * @param userName - The user's username
774
- * @returns The user if found, null otherwise
1267
+ * The city or locality component
775
1268
  */
776
- getByUserName(userName: string): Promise<StoredUser<TExtended> | null>;
1269
+ locality?: string;
777
1270
  /**
778
- * Create or update a user in the store.
779
- *
780
- * If a user with the same `id` (sub) exists, it will be updated.
781
- * Otherwise, a new user will be created.
782
- *
783
- * @param user - The user data to store
1271
+ * The state or region component
784
1272
  */
785
- upsert(user: StoredUser<TExtended>): Promise<void>;
1273
+ region?: string;
786
1274
  /**
787
- * Delete a user by their subject identifier (sub).
788
- *
789
- * @param sub - The user's unique identifier to delete
1275
+ * The zip code or postal code component
790
1276
  */
791
- delete(sub: string): Promise<void>;
1277
+ postalCode?: string;
1278
+ /**
1279
+ * The country name component
1280
+ */
1281
+ country?: string;
1282
+ /**
1283
+ * A label indicating the attribute's function (e.g., "work" or "home")
1284
+ */
1285
+ type?: string;
1286
+ /**
1287
+ * A Boolean value indicating the 'primary' or preferred attribute value
1288
+ */
1289
+ primary?: boolean;
792
1290
  }
793
1291
  /**
794
- * In-memory user store implementation using Maps.
795
- *
796
- * Suitable for:
797
- * - Development and testing
798
- * - Single-server deployments
799
- * - Applications without high availability requirements
800
- *
801
- * NOT suitable for:
802
- * - Multi-server deployments (users not shared)
803
- * - High availability scenarios (users lost on restart)
804
- * - Production applications with distributed architecture
805
- *
806
- * For production, implement UserStore with Redis or a database.
807
- *
808
- * @template TExtended - Type-safe custom data that consumers can add to users
1292
+ * SCIM Group reference (used within User resources)
809
1293
  */
810
- declare class InMemoryUserStore<TExtended = {}> implements UserStore<TExtended> {
811
- /** Primary storage: sub -> user */
812
- private users;
813
- /** Secondary index: email -> sub */
814
- private emailIndex;
815
- /** Secondary index: userName -> sub */
816
- private userNameIndex;
817
- get(sub: string): Promise<StoredUser<TExtended> | null>;
818
- getByEmail(email: string): Promise<StoredUser<TExtended> | null>;
819
- getByUserName(userName: string): Promise<StoredUser<TExtended> | null>;
820
- upsert(user: StoredUser<TExtended>): Promise<void>;
821
- delete(sub: string): Promise<void>;
1294
+ interface Group {
1295
+ /**
1296
+ * The identifier of the User's group
1297
+ */
1298
+ value: string;
1299
+ /**
1300
+ * The URI of the corresponding 'Group' resource
1301
+ */
1302
+ $ref?: string;
1303
+ /**
1304
+ * A human-readable name, primarily used for display purposes
1305
+ */
1306
+ display?: string;
1307
+ /**
1308
+ * A label indicating the attribute's function (e.g., "direct" or "indirect")
1309
+ */
1310
+ type?: string;
822
1311
  }
823
- import { StandardSchemaV1 as StandardSchemaV14 } from "@standard-schema/spec";
824
- import { StandardSchemaV1 as StandardSchemaV13 } from "@standard-schema/spec";
825
1312
  /**
826
- * JWT Assertion Claims for OAuth2 JWT Bearer Grant (RFC 7523) and OAuth2 Access Tokens
827
- * @see https://datatracker.ietf.org/doc/html/rfc7523
828
- * @see https://datatracker.ietf.org/doc/html/rfc9068
1313
+ * SCIM Group Member reference
829
1314
  */
830
- interface JWTAssertionClaims {
1315
+ interface GroupMember {
831
1316
  /**
832
- * REQUIRED. Issuer - the workload identity (e.g., SPIFFE ID) or authorization server
1317
+ * The identifier of the member (User or Group)
833
1318
  */
834
- iss: string;
1319
+ value: string;
835
1320
  /**
836
- * REQUIRED. Subject - the workload identity or service account
1321
+ * The URI of the corresponding member resource
837
1322
  */
838
- sub: string;
1323
+ $ref?: string;
839
1324
  /**
840
- * OPTIONAL. Audience - may be a string or array of strings
841
- * Note: Required for JWT assertions, but may be absent in OAuth2 access tokens
1325
+ * A human-readable name of the member
842
1326
  */
843
- aud?: string | string[];
1327
+ display?: string;
844
1328
  /**
845
- * REQUIRED. Expiration time (Unix timestamp)
1329
+ * The type of the member (e.g., "User" or "Group")
846
1330
  */
847
- exp: number;
1331
+ type?: "User" | "Group";
1332
+ }
1333
+ /**
1334
+ * SCIM 2.0 Group Resource
1335
+ * @see https://datatracker.ietf.org/doc/html/rfc7643#section-4.2
1336
+ */
1337
+ interface GroupResource {
848
1338
  /**
849
- * REQUIRED. Issued at time (Unix timestamp)
1339
+ * REQUIRED. The schemas attribute
850
1340
  */
851
- iat: number;
1341
+ schemas?: string[];
852
1342
  /**
853
- * OPTIONAL. JWT ID - unique identifier for this token
854
- * Note: Required for JWT assertions, optional for access tokens
1343
+ * Unique identifier for the Group, assigned by the service provider
1344
+ */
1345
+ id?: string;
1346
+ /**
1347
+ * External identifier from the provisioning client
1348
+ */
1349
+ externalId?: string;
1350
+ /**
1351
+ * Resource metadata
1352
+ */
1353
+ meta?: {
1354
+ resourceType?: string;
1355
+ created?: string;
1356
+ lastModified?: string;
1357
+ location?: string;
1358
+ version?: string;
1359
+ };
1360
+ /**
1361
+ * REQUIRED. A human-readable name for the Group
1362
+ */
1363
+ displayName: string;
1364
+ /**
1365
+ * A list of members of the Group
1366
+ */
1367
+ members?: GroupMember[];
1368
+ }
1369
+ /**
1370
+ * SCIM Role
1371
+ */
1372
+ interface Role {
1373
+ /**
1374
+ * The value of the role
1375
+ */
1376
+ value: string;
1377
+ /**
1378
+ * A human-readable name, primarily used for display purposes
855
1379
  */
856
- jti?: string;
1380
+ display?: string;
857
1381
  /**
858
- * OPTIONAL. Requested OAuth scopes (space-delimited)
1382
+ * A label indicating the attribute's function
859
1383
  */
860
- scope?: string;
1384
+ type?: string;
861
1385
  /**
862
- * Allow additional claims for extensibility
1386
+ * A Boolean value indicating the 'primary' or preferred attribute value
863
1387
  */
864
- [key: string]: unknown;
1388
+ primary?: boolean;
865
1389
  }
866
1390
  /**
867
- * Creates a StandardSchemaV1 for validating JWT Assertion Claims.
868
- * @param vendor - The name of the vendor creating this schema
869
- * @returns A StandardSchemaV1 instance for JWT Assertion Claims validation
870
- */
871
- declare function jwtAssertionClaimsSchema(vendor: string): StandardSchemaV13<Record<string, unknown>, JWTAssertionClaims>;
872
- /**
873
- * Workload Token Response from OAuth2 token endpoint
874
- * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
1391
+ * SCIM X509 Certificate
875
1392
  */
876
- interface WorkloadTokenResponse {
1393
+ interface X509Certificate {
877
1394
  /**
878
- * REQUIRED. The access token issued by the authorization server.
1395
+ * The value of the X.509 certificate
879
1396
  */
880
- access_token: string;
1397
+ value: string;
881
1398
  /**
882
- * REQUIRED. The type of the token (typically "Bearer").
1399
+ * A human-readable name, primarily used for display purposes
883
1400
  */
884
- token_type: string;
1401
+ display?: string;
885
1402
  /**
886
- * RECOMMENDED. The lifetime in seconds of the access token.
1403
+ * A label indicating the attribute's function
887
1404
  */
888
- expires_in?: number;
1405
+ type?: string;
889
1406
  /**
890
- * OPTIONAL. The scope of the access token.
1407
+ * A Boolean value indicating the 'primary' or preferred attribute value
891
1408
  */
892
- scope?: string;
1409
+ primary?: boolean;
1410
+ }
1411
+ /**
1412
+ * SCIM Enterprise User Extension
1413
+ * @see https://datatracker.ietf.org/doc/html/rfc7643#section-4.3
1414
+ */
1415
+ interface EnterpriseExtension {
893
1416
  /**
894
- * OPTIONAL. The refresh token (rarely used for workload identities).
1417
+ * Numeric or alphanumeric identifier assigned to a person
895
1418
  */
896
- refresh_token?: string;
1419
+ employeeNumber?: string;
897
1420
  /**
898
- * OPTIONAL. The expiration time as an ISO 8601 string.
1421
+ * Identifies the name of a cost center
899
1422
  */
900
- expires?: string;
901
- }
902
- /**
903
- * Creates a StandardSchemaV1 for validating Workload Token Responses.
904
- * @param vendor - The name of the vendor creating this schema
905
- * @returns A StandardSchemaV1 instance for Workload Token Response validation
906
- */
907
- declare function workloadTokenResponseSchema(vendor: string): StandardSchemaV13<Record<string, unknown>, WorkloadTokenResponse>;
908
- /**
909
- * Token Validation Result
910
- */
911
- interface TokenValidationResult {
1423
+ costCenter?: string;
912
1424
  /**
913
- * Whether the token is valid
1425
+ * Identifies the name of an organization
914
1426
  */
915
- valid: boolean;
1427
+ organization?: string;
916
1428
  /**
917
- * The decoded and validated claims (if valid)
1429
+ * Identifies the name of a division
918
1430
  */
919
- claims?: JWTAssertionClaims;
1431
+ division?: string;
920
1432
  /**
921
- * Error message (if invalid)
1433
+ * Identifies the name of a department
922
1434
  */
923
- error?: string;
1435
+ department?: string;
924
1436
  /**
925
- * Token expiration time (if valid)
1437
+ * The user's manager
926
1438
  */
927
- expiresAt?: Date;
1439
+ manager?: {
1440
+ /**
1441
+ * The "id" of the SCIM resource representing the User's manager
1442
+ */
1443
+ value?: string;
1444
+ /**
1445
+ * The URI of the SCIM resource representing the User's manager
1446
+ */
1447
+ $ref?: string;
1448
+ /**
1449
+ * The displayName of the User's manager
1450
+ */
1451
+ displayName?: string;
1452
+ };
928
1453
  }
929
1454
  /**
930
- * Token caching for workload identity authentication.
931
- *
932
- * Token stores are optional but recommended for performance - they enable:
933
- * - Token caching to avoid repeated token acquisition
934
- * - Automatic token refresh before expiration
935
- * - Reduced load on authorization servers
936
- *
937
- * ## Token Caching Strategy
938
- *
939
- * Unlike session stores for user authentication, workload token stores cache
940
- * short-lived access tokens (typically 5 minutes) for service-to-service calls.
941
- *
942
- * **Default Behavior:**
943
- * - Tokens cached with 5-minute TTL
944
- * - Auto-refresh 60 seconds before expiration
945
- * - Expired tokens automatically removed
946
- *
947
- * ## Performance Characteristics
948
- *
949
- * | Backend | Lookup Time | Use Case |
950
- * |--------------|-------------|----------------------------|
951
- * | InMemory | <0.00005ms | Single-server deployments |
952
- * | Redis | 1-2ms | Multi-server deployments |
953
- * | Database | 5-20ms | Persistent token storage |
954
- *
955
- * ## Example Usage
956
- *
957
- * ```typescript
958
- * import { workload, InMemoryWorkloadTokenStore } from '@enterprisestandard/react/server';
959
- *
960
- * // With token caching
961
- * const workloadAuth = workload({
962
- * // ... other config
963
- * tokenStore: new InMemoryWorkloadTokenStore(),
964
- * auto_refresh: true, // Refresh before expiry
965
- * });
966
- *
967
- * // Without token caching (fetch new token each time)
968
- * const workloadAuth = workload({
969
- * // ... config without tokenStore
970
- * });
971
- * ```
972
- */
973
- /**
974
- * Cached workload token data.
975
- *
976
- * @template TExtended - Type-safe custom data that consumers can add to cached tokens
1455
+ * SCIM User Resource
977
1456
  */
978
- type CachedWorkloadToken<TExtended = object> = {
1457
+ interface User2 {
979
1458
  /**
980
- * Workload identifier (typically SPIFFE ID).
981
- * Used as the primary key for token lookup.
1459
+ * REQUIRED. Unique identifier for the User, typically from the provider
982
1460
  */
983
- workload_id: string;
1461
+ id?: string;
984
1462
  /**
985
- * OAuth2 access token (Bearer token)
1463
+ * REQUIRED. A unique identifier for a SCIM resource as defined by the service provider
986
1464
  */
987
- access_token: string;
1465
+ externalId?: string;
988
1466
  /**
989
- * Token type (always "Bearer" for OAuth2)
1467
+ * Resource metadata
990
1468
  */
991
- token_type: string;
1469
+ meta?: {
1470
+ resourceType?: string;
1471
+ created?: string;
1472
+ lastModified?: string;
1473
+ location?: string;
1474
+ version?: string;
1475
+ };
992
1476
  /**
993
- * OAuth2 scopes granted for this token
1477
+ * REQUIRED. Unique identifier for the User, typically used for login
994
1478
  */
995
- scope?: string;
1479
+ userName: string;
996
1480
  /**
997
- * Timestamp when the token expires.
998
- * Used for automatic cleanup and refresh logic.
1481
+ * The components of the user's name
999
1482
  */
1000
- expires_at: Date;
1483
+ name?: Name;
1001
1484
  /**
1002
- * Timestamp when the token was created/cached.
1485
+ * The name of the User, suitable for display to end-users
1003
1486
  */
1004
- created_at: Date;
1487
+ displayName?: string;
1005
1488
  /**
1006
- * Optional refresh token (rarely used for workload identities)
1489
+ * The casual way to address the user
1007
1490
  */
1008
- refresh_token?: string;
1009
- } & TExtended;
1010
- /**
1011
- * Abstract interface for workload token storage backends.
1012
- *
1013
- * Consumers can implement this interface to use different storage backends:
1014
- * - Redis (recommended for multi-server deployments)
1015
- * - Database (PostgreSQL, MySQL, etc. for persistence)
1016
- * - Distributed cache (Memcached, etc.)
1017
- * - Custom solutions
1018
- *
1019
- * @template TExtended - Type-safe custom data that consumers can add to cached tokens
1020
- *
1021
- * @example
1022
- * ```typescript
1023
- * // Custom token cache data
1024
- * type MyTokenData = {
1025
- * environment: string;
1026
- * region: string;
1027
- * };
1028
- *
1029
- * // Implement custom store with Redis
1030
- * class RedisWorkloadTokenStore implements WorkloadTokenStore<MyTokenData> {
1031
- * async set(token: CachedWorkloadToken<MyTokenData>): Promise<void> {
1032
- * const ttl = Math.floor((token.expires_at.getTime() - Date.now()) / 1000);
1033
- * await redis.setex(
1034
- * `workload:token:${token.workload_id}`,
1035
- * ttl,
1036
- * JSON.stringify(token)
1037
- * );
1038
- * }
1039
- *
1040
- * async get(workload_id: string): Promise<CachedWorkloadToken<MyTokenData> | null> {
1041
- * const data = await redis.get(`workload:token:${workload_id}`);
1042
- * if (!data) return null;
1043
- * const token = JSON.parse(data);
1044
- * // Convert date strings back to Date objects
1045
- * token.expires_at = new Date(token.expires_at);
1046
- * token.created_at = new Date(token.created_at);
1047
- * return token;
1048
- * }
1049
- *
1050
- * // ... other methods
1051
- * }
1052
- * ```
1053
- */
1054
- interface WorkloadTokenStore<TExtended = object> {
1491
+ nickName?: string;
1055
1492
  /**
1056
- * Store or update a workload token in the cache.
1057
- *
1058
- * @param token - The token data to cache
1493
+ * A fully qualified URL pointing to a page representing the User's online profile
1059
1494
  */
1060
- set(token: CachedWorkloadToken<TExtended>): Promise<void>;
1495
+ profileUrl?: string;
1061
1496
  /**
1062
- * Retrieve a cached token by workload ID.
1063
- *
1064
- * @param workload_id - The workload identifier (e.g., SPIFFE ID)
1065
- * @returns The cached token if found and not expired, null otherwise
1497
+ * The user's title, such as "Vice President"
1066
1498
  */
1067
- get(workload_id: string): Promise<CachedWorkloadToken<TExtended> | null>;
1499
+ title?: string;
1068
1500
  /**
1069
- * Delete a cached token by workload ID.
1070
- *
1071
- * Used when explicitly revoking tokens or clearing cache.
1072
- *
1073
- * @param workload_id - The workload identifier to remove
1501
+ * Used to identify the relationship between the organization and the user
1074
1502
  */
1075
- delete(workload_id: string): Promise<void>;
1503
+ userType?: string;
1076
1504
  /**
1077
- * Check if a valid (non-expired) token exists for a workload.
1078
- *
1079
- * @param workload_id - The workload identifier to check
1080
- * @returns true if a valid token exists, false otherwise
1505
+ * Indicates the User's preferred written or spoken language
1506
+ */
1507
+ preferredLanguage?: string;
1508
+ /**
1509
+ * Used to indicate the User's default location for purposes of localizing items such as currency
1510
+ */
1511
+ locale?: string;
1512
+ /**
1513
+ * The User's time zone in the "Olson" time zone database format
1081
1514
  */
1082
- isValid(workload_id: string): Promise<boolean>;
1515
+ timezone?: string;
1083
1516
  /**
1084
- * Remove all expired tokens from the cache.
1085
- *
1086
- * Should be called periodically to prevent memory leaks.
1517
+ * A Boolean value indicating the User's administrative status
1087
1518
  */
1088
- cleanup(): Promise<void>;
1089
- }
1090
- /**
1091
- * In-memory workload token store implementation using Maps.
1092
- *
1093
- * Suitable for:
1094
- * - Development and testing
1095
- * - Single-server deployments
1096
- * - Applications without high availability requirements
1097
- *
1098
- * NOT suitable for:
1099
- * - Multi-server deployments (tokens not shared across instances)
1100
- * - High availability scenarios (tokens lost on restart)
1101
- * - Production applications with distributed architecture
1102
- *
1103
- * For production multi-server deployments, implement WorkloadTokenStore with Redis.
1104
- *
1105
- * @template TExtended - Type-safe custom data that consumers can add to cached tokens
1106
- */
1107
- declare class InMemoryWorkloadTokenStore<TExtended = object> implements WorkloadTokenStore<TExtended> {
1108
- private tokens;
1109
- set(token: CachedWorkloadToken<TExtended>): Promise<void>;
1110
- get(workload_id: string): Promise<CachedWorkloadToken<TExtended> | null>;
1111
- delete(workload_id: string): Promise<void>;
1112
- isValid(workload_id: string): Promise<boolean>;
1113
- cleanup(): Promise<void>;
1114
- }
1115
- /**
1116
- * Common fields shared across all workload authentication modes
1117
- */
1118
- type WorkloadConfigBase = {
1519
+ active?: boolean;
1119
1520
  /**
1120
- * OAuth2 token endpoint URL
1121
- * REQUIRED for client role (token acquisition)
1521
+ * The User's cleartext password
1122
1522
  */
1123
- tokenUrl?: string;
1523
+ password?: string;
1124
1524
  /**
1125
- * JWKS endpoint URL for public key retrieval
1126
- * REQUIRED for server role (token validation)
1525
+ * Email addresses for the user
1127
1526
  */
1128
- jwksUri?: string;
1527
+ emails?: Email[];
1129
1528
  /**
1130
- * Expected token issuer URL for validation
1131
- * RECOMMENDED for server role (token validation)
1529
+ * Phone numbers for the User
1132
1530
  */
1133
- issuer?: string;
1531
+ phoneNumbers?: PhoneNumber[];
1134
1532
  /**
1135
- * Target audience for tokens
1533
+ * Instant messaging addresses for the User
1136
1534
  */
1137
- audience?: string;
1535
+ ims?: Array<{
1536
+ value: string;
1537
+ display?: string;
1538
+ type?: string;
1539
+ primary?: boolean;
1540
+ }>;
1138
1541
  /**
1139
- * Default OAuth2 scopes (space-delimited)
1542
+ * URLs of photos of the User
1140
1543
  */
1141
- scope?: string;
1544
+ photos?: Array<{
1545
+ value: string;
1546
+ display?: string;
1547
+ type?: string;
1548
+ primary?: boolean;
1549
+ }>;
1142
1550
  /**
1143
- * JWT assertion/token lifetime in seconds
1144
- * @default 300 (5 minutes)
1551
+ * Physical mailing addresses for this User
1145
1552
  */
1146
- tokenLifetime?: number;
1553
+ addresses?: Address[];
1147
1554
  /**
1148
- * Refresh threshold in seconds (refresh token this many seconds before expiry)
1149
- * @default 60
1555
+ * A list of groups to which the user belongs
1150
1556
  */
1151
- refreshThreshold?: number;
1557
+ groups?: Group[];
1152
1558
  /**
1153
- * Optional token store for caching access tokens
1559
+ * A list of entitlements for the User
1154
1560
  */
1155
- tokenStore?: WorkloadTokenStore;
1561
+ entitlements?: Array<{
1562
+ value: string;
1563
+ display?: string;
1564
+ type?: string;
1565
+ primary?: boolean;
1566
+ }>;
1156
1567
  /**
1157
- * Automatically refresh tokens before expiration
1158
- * @default true
1568
+ * A list of roles for the User
1159
1569
  */
1160
- autoRefresh?: boolean;
1570
+ roles?: Role[];
1161
1571
  /**
1162
- * Optional RFC 7009 token revocation endpoint
1572
+ * A list of certificates issued to the User
1163
1573
  */
1164
- revocationEndpoint?: string;
1574
+ x509Certificates?: X509Certificate[];
1165
1575
  /**
1166
- * Optional handler defaults (merged with per-call overrides in `handler`)
1576
+ * Enterprise User Extension
1167
1577
  */
1168
- validateUrl?: string;
1169
- jwksUrl?: string;
1170
- refreshUrl?: string;
1171
- validators?: WorkloadValidators;
1172
- };
1173
- type WorkloadValidators = {
1174
- jwtAssertionClaims: StandardSchemaV14<unknown, JWTAssertionClaims>;
1175
- tokenResponse: StandardSchemaV14<unknown, WorkloadTokenResponse>;
1176
- };
1578
+ "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"?: EnterpriseExtension;
1579
+ /**
1580
+ * REQUIRED. The schemas attribute is an array of Strings which allows introspection of the supported schema version
1581
+ */
1582
+ schemas?: string[];
1583
+ }
1177
1584
  /**
1178
- * JWT Bearer Grant (RFC 7523) Configuration
1179
- *
1180
- * Used for SPIFFE-style workload identities where services have their own
1181
- * cryptographic identity and sign their own JWT assertions.
1585
+ * Creates a StandardSchemaV1 for validating SCIM User resources.
1586
+ * @param vendor - The name of the vendor creating this schema
1587
+ * @returns A StandardSchemaV1 instance for SCIM User resources
1588
+ */
1589
+ declare function userSchema(vendor: string): StandardSchemaV14<Record<string, unknown>, User2>;
1590
+ /**
1591
+ * Creates a StandardSchemaV1 for validating SCIM Group resources.
1592
+ * @param vendor - The name of the vendor creating this schema
1593
+ * @returns A StandardSchemaV1 instance for SCIM Group resources
1594
+ */
1595
+ declare function groupResourceSchema(vendor: string): StandardSchemaV14<Record<string, unknown>, GroupResource>;
1596
+ /**
1597
+ * Stored group data with required id and tracking metadata.
1182
1598
  *
1183
- * @example
1184
- * ```json
1185
- * {
1186
- * "token_url": "https://auth.example.com/oauth/token",
1187
- * "jwks_uri": "https://auth.example.com/.well-known/jwks.json",
1188
- * "workload_id": "spiffe://trust-domain/ns/service",
1189
- * "audience": "https://auth.example.com/oauth/token",
1190
- * "private_key": "-----BEGIN PRIVATE KEY-----...",
1191
- * "algorithm": "RS256"
1192
- * }
1193
- * ```
1599
+ * @template TExtended - Type-safe custom data that consumers can add to groups
1194
1600
  */
1195
- type JwtBearerWorkloadConfig = WorkloadConfigBase & {
1601
+ type StoredGroup<TExtended = {}> = {
1196
1602
  /**
1197
- * Workload identifier (e.g., SPIFFE ID: spiffe://trust-domain/namespace/service)
1198
- * REQUIRED for JWT Bearer Grant mode
1603
+ * Required unique identifier for the group.
1604
+ * This is the primary key for group storage.
1199
1605
  */
1200
- workloadId: string;
1606
+ id: string;
1201
1607
  /**
1202
- * PEM-encoded private key for signing JWT assertions
1203
- * REQUIRED for client role in JWT Bearer Grant mode
1608
+ * Required human-readable name for the group.
1204
1609
  */
1205
- privateKey: string;
1610
+ displayName: string;
1206
1611
  /**
1207
- * Key ID (kid) to include in JWT header for key rotation support
1612
+ * Optional external identifier from provisioning client.
1208
1613
  */
1209
- keyId?: string;
1614
+ externalId?: string;
1210
1615
  /**
1211
- * JWT signing algorithm
1212
- * @default 'RS256'
1616
+ * List of members in the group.
1213
1617
  */
1214
- algorithm?: "RS256" | "RS384" | "RS512" | "ES256" | "ES384" | "ES512";
1215
- };
1216
- /**
1217
- * OAuth2 Client Credentials Configuration
1218
- *
1219
- * Standard OAuth2 Client Credentials Grant (RFC 6749 Section 4.4).
1220
- * Used with identity providers like Keycloak for service-to-service authentication.
1221
- *
1222
- * @example
1223
- * ```json
1224
- * {
1225
- * "token_url": "https://sso.example.com/realms/myrealm/protocol/openid-connect/token",
1226
- * "jwks_uri": "https://sso.example.com/realms/myrealm/protocol/openid-connect/certs",
1227
- * "client_id": "my-service",
1228
- * "client_secret": "secret-from-idp",
1229
- * "issuer": "https://sso.example.com/realms/myrealm",
1230
- * "scope": "api:read api:write"
1231
- * }
1232
- * ```
1233
- */
1234
- type ClientCredentialsWorkloadConfig = WorkloadConfigBase & {
1618
+ members?: GroupMember[];
1235
1619
  /**
1236
- * OAuth2 client identifier registered with the authorization server
1237
- * REQUIRED for Client Credentials mode
1620
+ * Timestamp when the group was first stored.
1238
1621
  */
1239
- clientId: string;
1622
+ createdAt: Date;
1240
1623
  /**
1241
- * OAuth2 client secret
1242
- * REQUIRED for Client Credentials mode
1624
+ * Timestamp when the group was last updated.
1243
1625
  */
1244
- clientSecret: string;
1245
- };
1246
- /**
1247
- * Server-Only Workload Configuration
1248
- *
1249
- * Used when a service only needs to validate incoming workload tokens,
1250
- * not acquire tokens for outbound calls. Requires only jwks_uri for
1251
- * public key retrieval.
1252
- *
1253
- * @example
1254
- * ```json
1255
- * {
1256
- * "jwks_uri": "https://sso.example.com/realms/myrealm/protocol/openid-connect/certs",
1257
- * "issuer": "https://sso.example.com/realms/myrealm"
1258
- * }
1259
- * ```
1260
- */
1261
- type ServerOnlyWorkloadConfig = WorkloadConfigBase & {
1262
- jwksUri: string;
1263
- };
1626
+ updatedAt: Date;
1627
+ } & TExtended;
1264
1628
  /**
1265
- * Workload Identity Authentication Configuration
1266
- *
1267
- * Union type supporting multiple authentication modes. The mode is automatically
1268
- * detected based on which fields are present:
1629
+ * Abstract interface for group storage backends.
1269
1630
  *
1270
- * - **JWT Bearer Grant**: Requires `workload_id` + `private_key`
1271
- * - **Client Credentials**: Requires `client_id` + `client_secret`
1272
- * - **Server-Only**: Only `jwks_uri` (and optionally `issuer`) for token validation
1631
+ * Consumers can implement this interface to use different storage backends:
1632
+ * - In-memory (for development/testing)
1633
+ * - Redis (for production with fast lookups)
1634
+ * - Database (PostgreSQL, MySQL, etc.)
1273
1635
  *
1274
- * The developer uses the same API regardless of mode - the library handles the details.
1275
- */
1276
- type WorkloadConfig = JwtBearerWorkloadConfig | ClientCredentialsWorkloadConfig | ServerOnlyWorkloadConfig;
1277
- /**
1278
- * Workload Identity extracted from validated tokens
1636
+ * @template TExtended - Type-safe custom data that consumers can add to groups
1279
1637
  */
1280
- type WorkloadIdentity = {
1638
+ interface GroupStore<TExtended = {}> {
1639
+ /**
1640
+ * Retrieve a group by its unique identifier.
1641
+ *
1642
+ * @param id - The group's unique identifier
1643
+ * @returns The group if found, null otherwise
1644
+ */
1645
+ get(id: string): Promise<StoredGroup<TExtended> | null>;
1646
+ /**
1647
+ * Retrieve a group by its external identifier.
1648
+ *
1649
+ * @param externalId - The external identifier from the provisioning client
1650
+ * @returns The group if found, null otherwise
1651
+ */
1652
+ getByExternalId(externalId: string): Promise<StoredGroup<TExtended> | null>;
1653
+ /**
1654
+ * Retrieve a group by its display name.
1655
+ *
1656
+ * @param displayName - The group's display name
1657
+ * @returns The group if found, null otherwise
1658
+ */
1659
+ getByDisplayName(displayName: string): Promise<StoredGroup<TExtended> | null>;
1660
+ /**
1661
+ * List all groups in the store.
1662
+ *
1663
+ * @returns Array of all stored groups
1664
+ */
1665
+ list(): Promise<StoredGroup<TExtended>[]>;
1281
1666
  /**
1282
- * Workload identifier (for JWT Bearer Grant tokens)
1667
+ * Create or update a group in the store.
1668
+ *
1669
+ * If a group with the same `id` exists, it will be updated.
1670
+ * Otherwise, a new group will be created.
1671
+ *
1672
+ * @param group - The group data to store
1283
1673
  */
1284
- workloadId?: string;
1674
+ upsert(group: StoredGroup<TExtended>): Promise<void>;
1285
1675
  /**
1286
- * Client identifier (for OAuth2 Client Credentials tokens)
1676
+ * Delete a group by its unique identifier.
1677
+ *
1678
+ * @param id - The group's unique identifier to delete
1287
1679
  */
1288
- clientId?: string;
1680
+ delete(id: string): Promise<void>;
1289
1681
  /**
1290
- * Granted scopes
1682
+ * Add a member to a group.
1683
+ *
1684
+ * @param groupId - The group's unique identifier
1685
+ * @param member - The member to add
1291
1686
  */
1292
- scope?: string;
1687
+ addMember(groupId: string, member: GroupMember): Promise<void>;
1293
1688
  /**
1294
- * Full JWT claims from the token
1689
+ * Remove a member from a group.
1690
+ *
1691
+ * @param groupId - The group's unique identifier
1692
+ * @param memberId - The member's value/id to remove
1295
1693
  */
1296
- claims: JWTAssertionClaims;
1297
- };
1694
+ removeMember(groupId: string, memberId: string): Promise<void>;
1695
+ }
1298
1696
  /**
1299
- * Workload Identity Authentication Interface
1697
+ * In-memory group store implementation using Maps.
1698
+ *
1699
+ * Suitable for:
1700
+ * - Development and testing
1701
+ * - Single-server deployments
1702
+ * - Applications without high availability requirements
1703
+ *
1704
+ * NOT suitable for:
1705
+ * - Multi-server deployments (groups not shared)
1706
+ * - High availability scenarios (groups lost on restart)
1707
+ * - Production applications with distributed architecture
1708
+ *
1709
+ * For production, implement GroupStore with Redis or a database.
1710
+ *
1711
+ * @template TExtended - Type-safe custom data that consumers can add to groups
1300
1712
  */
1301
- type Workload = {
1302
- config: WorkloadConfig;
1303
- getToken: (scope?: string) => Promise<string>;
1304
- refreshToken: (scope?: string) => Promise<WorkloadTokenResponse>;
1305
- generateJWTAssertion: (scope?: string) => Promise<string>;
1306
- revokeToken: (token: string) => Promise<void>;
1307
- validateToken: (token: string) => Promise<TokenValidationResult>;
1308
- getWorkloadIdentity: (request: Request) => Promise<WorkloadIdentity | undefined>;
1309
- parseJWT: (token: string) => Promise<JWTAssertionClaims>;
1310
- handler: (request: Request) => Promise<Response>;
1311
- };
1312
- declare function workload(validators: WorkloadValidators, fromVault?: Partial<WorkloadConfig>, fromCode?: Partial<WorkloadConfig>): Workload | undefined;
1713
+ declare class InMemoryGroupStore<TExtended = {}> implements GroupStore<TExtended> {
1714
+ /** Primary storage: id -> group */
1715
+ private groups;
1716
+ /** Secondary index: externalId -> id */
1717
+ private externalIdIndex;
1718
+ /** Secondary index: displayName (lowercase) -> id */
1719
+ private displayNameIndex;
1720
+ get(id: string): Promise<StoredGroup<TExtended> | null>;
1721
+ getByExternalId(externalId: string): Promise<StoredGroup<TExtended> | null>;
1722
+ getByDisplayName(displayName: string): Promise<StoredGroup<TExtended> | null>;
1723
+ list(): Promise<StoredGroup<TExtended>[]>;
1724
+ upsert(group: StoredGroup<TExtended>): Promise<void>;
1725
+ delete(id: string): Promise<void>;
1726
+ addMember(groupId: string, member: GroupMember): Promise<void>;
1727
+ removeMember(groupId: string, memberId: string): Promise<void>;
1728
+ }
1313
1729
  /**
1314
1730
  * SCIM Error response structure
1315
1731
  */
@@ -1382,7 +1798,7 @@ type IAMConfig = {
1382
1798
  groupsUrl?: string;
1383
1799
  };
1384
1800
  type IAMValidators = {
1385
- user: StandardSchemaV15<unknown, User>;
1801
+ user: StandardSchemaV15<unknown, User2>;
1386
1802
  group: StandardSchemaV15<unknown, GroupResource>;
1387
1803
  };
1388
1804
  /**
@@ -1473,7 +1889,7 @@ type IAM = IAMConfig & {
1473
1889
  * Create a new user/account in the external IAM provider
1474
1890
  * Only available when `url` is configured.
1475
1891
  */
1476
- createUser?: (user: User, options?: CreateUserOptions) => Promise<ScimResult<User>>;
1892
+ createUser?: (user: User2, options?: CreateUserOptions) => Promise<ScimResult<User2>>;
1477
1893
  /**
1478
1894
  * Get the configured external SCIM base URL
1479
1895
  */
@@ -1511,184 +1927,6 @@ type IAM = IAMConfig & {
1511
1927
  */
1512
1928
  declare function iam(validators: IAMValidators, workload?: Workload, fromVault?: Partial<IAMConfig>, fromCode?: Partial<IAMConfig>): IAM | undefined;
1513
1929
  import { StandardSchemaV1 as StandardSchemaV16 } from "@standard-schema/spec";
1514
- /**
1515
- * Session management for tracking user sessions and enabling backchannel logout.
1516
- *
1517
- * Session stores are optional - the package works with JWT cookies alone.
1518
- * Sessions are only required for backchannel logout functionality.
1519
- *
1520
- * ## Session Validation Strategies
1521
- *
1522
- * When using a session store, you can configure when sessions are validated:
1523
- *
1524
- * ### 'always' (default)
1525
- * Validates session on every authenticated request.
1526
- * - **Security**: Maximum - immediate session revocation
1527
- * - **Performance**: InMemory ~0.00005ms, Redis ~1-2ms per request
1528
- * - **Backchannel Logout**: Takes effect immediately
1529
- * - **Use when**: Security is paramount, using InMemory or Redis backend
1530
- *
1531
- * ### 'refresh-only'
1532
- * Validates session only during token refresh (typically every 5-15 minutes).
1533
- * - **Security**: Good - 5-15 minute revocation window
1534
- * - **Performance**: 99% reduction in session lookups
1535
- * - **Backchannel Logout**: Takes effect within token TTL (5-15 min)
1536
- * - **Use when**: Performance is critical AND delayed revocation is acceptable
1537
- * - **WARNING**: Compromised sessions remain valid until next refresh
1538
- *
1539
- * ### 'disabled'
1540
- * Never validates sessions against the store.
1541
- * - **Security**: None - backchannel logout doesn't work
1542
- * - **Performance**: No overhead
1543
- * - **Use when**: Cookie-only mode without session store
1544
- * - **WARNING**: Do not use with sessionStore configured
1545
- *
1546
- * ## Performance Characteristics
1547
- *
1548
- * | Backend | Lookup Time | Impact on Request | Recommendation |
1549
- * |--------------|-------------|-------------------|------------------------|
1550
- * | InMemory | <0.00005ms | Negligible | Use 'always' |
1551
- * | Redis | 1-2ms | 2-4% increase | Use 'always' or test |
1552
- * | Database | 5-20ms | 10-40% increase | Use Redis cache layer |
1553
- *
1554
- * ## Example Usage
1555
- *
1556
- * ```typescript
1557
- * import { sso, InMemorySessionStore } from '@enterprisestandard/react/server';
1558
- *
1559
- * // Maximum security (default)
1560
- * const secure = sso({
1561
- * // ... other config
1562
- * sessionStore: new InMemorySessionStore(),
1563
- * session_validation: 'always', // Immediate revocation
1564
- * });
1565
- *
1566
- * // High performance
1567
- * const fast = sso({
1568
- * // ... other config
1569
- * sessionStore: new InMemorySessionStore(),
1570
- * session_validation: {
1571
- * strategy: 'refresh-only' // 5-15 min revocation delay
1572
- * }
1573
- * });
1574
- * ```
1575
- */
1576
- /**
1577
- * Core session data tracked for each authenticated user session.
1578
- *
1579
- * @template TExtended - Type-safe custom data that consumers can add to sessions
1580
- */
1581
- type Session<TExtended = {}> = {
1582
- /**
1583
- * Session ID from the Identity Provider (from `sid` claim in ID token).
1584
- * This is the unique identifier for the session.
1585
- */
1586
- sid: string;
1587
- /**
1588
- * Subject identifier (user ID) from the Identity Provider.
1589
- * From the `sub` claim in the ID token.
1590
- */
1591
- sub: string;
1592
- /**
1593
- * Timestamp when the session was created.
1594
- */
1595
- createdAt: Date;
1596
- /**
1597
- * Timestamp of the last activity in this session.
1598
- * Can be updated to track session activity.
1599
- */
1600
- lastActivityAt: Date;
1601
- /**
1602
- * Allow consumers to add runtime data to sessions.
1603
- */
1604
- [key: string]: unknown;
1605
- } & TExtended;
1606
- /**
1607
- * Abstract interface for session storage backends.
1608
- *
1609
- * Consumers can implement this interface to use different storage backends:
1610
- * - Redis
1611
- * - Database (PostgreSQL, MySQL, etc.)
1612
- * - Distributed cache
1613
- * - Custom solutions
1614
- *
1615
- * @template TExtended - Type-safe custom data that consumers can add to sessions
1616
- *
1617
- * @example
1618
- * ```typescript
1619
- * // Custom session data
1620
- * type MySessionData = {
1621
- * ipAddress: string;
1622
- * userAgent: string;
1623
- * };
1624
- *
1625
- * // Implement custom store
1626
- * class RedisSessionStore implements SessionStore<MySessionData> {
1627
- * async create(session: Session<MySessionData>): Promise<void> {
1628
- * await redis.set(`session:${session.sid}`, JSON.stringify(session));
1629
- * }
1630
- * // ... other methods
1631
- * }
1632
- * ```
1633
- */
1634
- interface SessionStore<TExtended = {}> {
1635
- /**
1636
- * Create a new session in the store.
1637
- *
1638
- * @param session - The session data to store
1639
- * @throws Error if session with same sid already exists
1640
- */
1641
- create(session: Session<TExtended>): Promise<void>;
1642
- /**
1643
- * Retrieve a session by its IdP session ID (sid).
1644
- *
1645
- * @param sid - The session.sid from the Identity Provider
1646
- * @returns The session if found, null otherwise
1647
- */
1648
- get(sid: string): Promise<Session<TExtended> | null>;
1649
- /**
1650
- * Update an existing session with partial data.
1651
- *
1652
- * Commonly used to update lastActivityAt or add custom fields.
1653
- *
1654
- * @param sid - The session.sid to update
1655
- * @param data - Partial session data to merge
1656
- * @throws Error if session not found
1657
- */
1658
- update(sid: string, data: Partial<Session<TExtended>>): Promise<void>;
1659
- /**
1660
- * Delete a session by its IdP session ID (sid).
1661
- *
1662
- * Used for both normal logout and backchannel logout flows.
1663
- *
1664
- * @param sid - The session.sid to delete
1665
- */
1666
- delete(sid: string): Promise<void>;
1667
- }
1668
- /**
1669
- * In-memory session store implementation using Maps.
1670
- *
1671
- * Suitable for:
1672
- * - Development and testing
1673
- * - Single-server deployments
1674
- * - Applications without high availability requirements
1675
- *
1676
- * NOT suitable for:
1677
- * - Multi-server deployments (sessions not shared)
1678
- * - High availability scenarios (sessions lost on restart)
1679
- * - Production applications with distributed architecture
1680
- *
1681
- * For production, implement SessionStore with Redis or a database.
1682
- *
1683
- * @template TExtended - Type-safe custom data that consumers can add to sessions
1684
- */
1685
- declare class InMemorySessionStore<TExtended = {}> implements SessionStore<TExtended> {
1686
- private sessions;
1687
- create(session: Session<TExtended>): Promise<void>;
1688
- get(sid: string): Promise<Session<TExtended> | null>;
1689
- update(sid: string, data: Partial<Session<TExtended>>): Promise<void>;
1690
- delete(sid: string): Promise<void>;
1691
- }
1692
1930
  type SSOConfig<
1693
1931
  TSessionData = {},
1694
1932
  TUserData = {}
@@ -1750,11 +1988,12 @@ type SSO<
1750
1988
  TUserData = {}
1751
1989
  > = {
1752
1990
  config: SSOConfig<TSessionData, TUserData>;
1753
- getUser: (request: Request) => Promise<User2 | undefined>;
1754
- getRequiredUser: (request: Request) => Promise<User2>;
1991
+ getUser: (request: Request) => Promise<User | undefined>;
1992
+ getRequiredUser: (request: Request) => Promise<User>;
1755
1993
  getJwt: (request: Request) => Promise<string | undefined>;
1756
1994
  initiateLogin: (config: LoginConfig, requestUrl?: string) => Promise<Response>;
1757
1995
  logout: (request: Request, config?: LoginConfig) => Promise<Response>;
1996
+ logoutBackChannel: (request: Request) => Promise<Response>;
1758
1997
  callbackHandler: (request: Request) => Promise<Response>;
1759
1998
  handler: (request: Request) => Promise<Response>;
1760
1999
  };
@@ -1852,7 +2091,7 @@ type TenantValidators = {
1852
2091
  /**
1853
2092
  * Configuration for tenant management
1854
2093
  */
1855
- type TenantConfig = {};
2094
+ type TenantConfig = Record<string, never>;
1856
2095
  /**
1857
2096
  * Tenant service interface
1858
2097
  */
@@ -1950,7 +2189,7 @@ type StoredTenant<TExtended = {}> = {
1950
2189
  * Serialized Enterprise Standard configuration.
1951
2190
  * This is a JSON-serializable version of the ESConfig with non-serializable items excluded.
1952
2191
  */
1953
- config?: any;
2192
+ config?: unknown;
1954
2193
  } & TExtended;
1955
2194
  /**
1956
2195
  * Abstract interface for tenant storage backends.
@@ -2068,12 +2307,15 @@ type DevConfig = {
2068
2307
  type VaultConfig = LfvConfig | AzureConfig | AwsConfig | GcpConfig | OpenBaoConfig | DevConfig;
2069
2308
  declare function vault(config?: VaultConfig): Vault;
2070
2309
  /**
2071
- * Helper gets the user from the Request using the supplied EnterpriseStandard or the default instance
2310
+ * Helper gets the user from the Request using the supplied EnterpriseStandard or the default instance.
2311
+ * Checks SSO first, then CIAM if SSO returns undefined.
2072
2312
  */
2073
- declare function getUser2(request: Request, es?: EnterpriseStandard): Promise<User2 | undefined>;
2074
- declare function getRequiredUser2(request: Request, es?: EnterpriseStandard): Promise<User2>;
2313
+ declare function getUser2(request: Request, es?: EnterpriseStandard): Promise<User | undefined>;
2314
+ declare function getRequiredUser2(request: Request, es?: EnterpriseStandard): Promise<User>;
2075
2315
  declare function initiateLogin2(config: LoginConfig, es?: EnterpriseStandard): Promise<Response>;
2076
2316
  declare function callback(request: Request, es?: EnterpriseStandard): Promise<Response>;
2317
+ declare function logout(request: Request, es?: EnterpriseStandard): Promise<Response>;
2318
+ declare function logoutBackChannel2(request: Request, es?: EnterpriseStandard): Promise<Response>;
2077
2319
  import { StandardSchemaV1 as StandardSchemaV18 } from "@standard-schema/spec";
2078
2320
  /**
2079
2321
  * Parse and validate a tenant creation request.
@@ -2264,7 +2506,7 @@ declare function getDefaultInstance(): EnterpriseStandard | undefined;
2264
2506
  * @param message - The message to include in the response.
2265
2507
  * @returns A 400 Response with the issues if it does, otherwise null.
2266
2508
  */
2267
- declare function validationFailureResponse(issues: any, message: string): Response;
2509
+ declare function validationFailureResponse(issues: unknown, message: string): Response;
2268
2510
  /**
2269
2511
  * Serializes an ESConfig or EnterpriseStandard instance to a JSON-serializable format
2270
2512
  * by removing non-serializable properties like stores, validators, and functions.
@@ -2275,7 +2517,7 @@ declare function validationFailureResponse(issues: any, message: string): Respon
2275
2517
  * @param configOrES - The ESConfig object or EnterpriseStandard instance to serialize
2276
2518
  * @returns A JSON-serializable version of the config
2277
2519
  */
2278
- declare function serializeESConfig(configOrES: any): any;
2520
+ declare function serializeESConfig(configOrES: unknown): unknown;
2279
2521
  /**
2280
2522
  * Get the workload identity from an incoming request.
2281
2523
  * Returns undefined if no valid workload token is present.
@@ -2406,9 +2648,10 @@ type EnterpriseStandard = {
2406
2648
  iam?: IAM;
2407
2649
  workload?: Workload;
2408
2650
  tenants?: Tenant;
2651
+ ciam?: CIAM;
2409
2652
  /**
2410
2653
  * Framework-agnostic request handler that routes requests to the appropriate
2411
- * standard handler (SSO, IAM, or Workload) based on the configured URLs.
2654
+ * standard handler (SSO, IAM, Workload, or CIAM) based on the configured URLs.
2412
2655
  */
2413
2656
  handler: (request: Request) => Promise<Response>;
2414
2657
  };
@@ -2419,6 +2662,7 @@ type ESConfig = {
2419
2662
  iam?: IAMConfig;
2420
2663
  workload?: WorkloadConfig;
2421
2664
  tenant?: TenantConfig;
2665
+ ciam?: CIAMConfig;
2422
2666
  validators?: ESValidators;
2423
2667
  };
2424
2668
  type ESValidators = {
@@ -2428,4 +2672,4 @@ type ESValidators = {
2428
2672
  tenant: TenantValidators;
2429
2673
  };
2430
2674
  declare function enterpriseStandard(appId: string, initConfig?: ESConfig): Promise<EnterpriseStandard>;
2431
- export { workloadTokenResponseSchema, workloadHandler, workload, vault, validationFailureResponse, validateWorkloadToken, userSchema, tokenResponseSchema, tenant as tenantManagement, sso, serializeESConfig, sendTenantWebhook2 as sendTenantWebhook, revokeWorkloadToken, parseTenantRequest2 as parseTenantRequest, oidcCallbackSchema, jwtAssertionClaimsSchema, initiateLogin2 as initiateLogin, idTokenClaimsSchema, iam, groupResourceSchema, getWorkloadToken, getWorkload, getUser2 as getUser, getRequiredUser2 as getRequiredUser, getDefaultInstance, enterpriseStandard, callback, X509Certificate, WorkloadValidators, WorkloadTokenStore, WorkloadTokenResponse, WorkloadIdentity, WorkloadConfig, Workload, Vault, UsersInboundHandlerConfig, UserStore, User2 as User, TokenValidationResult, TokenResponse, TenantWebhookPayload, TenantValidators, TenantStore, TenantStatus, TenantRequestError, TenantConfig, Tenant, StoredUser, StoredTenant, StoredGroup, SessionStore, ServerOnlyWorkloadConfig, User as ScimUser, ScimResult, ScimListResponse, ScimError, SSOValidators, SSOHandlerConfig, SSOConfig, SSO, Role, PhoneNumber, OidcCallbackParams, Name, JwtBearerWorkloadConfig, JWTAssertionClaims, InMemoryWorkloadTokenStore, InMemoryUserStore, InMemoryTenantStore, InMemorySessionStore, InMemoryGroupStore, IdTokenClaims, IAMValidators, IAMUsersInbound, IAMHandlerConfig, IAMGroupsOutbound, IAMGroupsInbound, IAMConfig, IAM, GroupsInboundHandlerConfig, GroupStore, GroupResource, GroupMember, Group, EnvironmentType, EnterpriseUser, EnterpriseStandard, EnterpriseExtension, Email, ESValidators, CreateUserOptions, CreateTenantResponse, CreateTenantRequest, CreateGroupOptions, ClientCredentialsWorkloadConfig, CachedWorkloadToken, BaseUser, Address };
2675
+ export { workloadTokenResponseSchema, workloadHandler, workload, vault, validationFailureResponse, validateWorkloadToken, userSchema, tokenResponseSchema, tenant as tenantManagement, sso, serializeESConfig, sendTenantWebhook2 as sendTenantWebhook, revokeWorkloadToken, parseTenantRequest2 as parseTenantRequest, oidcCallbackSchema, logoutBackChannel2 as logoutBackChannel, logout, jwtAssertionClaimsSchema, initiateLogin2 as initiateLogin, idTokenClaimsSchema, iam, groupResourceSchema, getWorkloadToken, getWorkload, getUser2 as getUser, getRequiredUser2 as getRequiredUser, getDefaultInstance, enterpriseStandard, ciam, callback, X509Certificate, WorkloadValidators, WorkloadTokenStore, WorkloadTokenResponse, WorkloadIdentity, WorkloadConfig, Workload, Vault, UsersInboundHandlerConfig, UserStore, User, TokenValidationResult, TokenResponse, TenantWebhookPayload, TenantValidators, TenantStore, TenantStatus, TenantRequestError, TenantConfig, Tenant, StoredUser, StoredTenant, StoredGroup, SessionStore, ServerOnlyWorkloadConfig, User2 as ScimUser, ScimResult, ScimListResponse, ScimError, SSOValidators, SSOHandlerConfig, SSOConfig, SSO, Role, PhoneNumber, OidcCallbackParams, Name, MagicLinkStore, MagicLink, JwtBearerWorkloadConfig, JWTAssertionClaims, InMemoryWorkloadTokenStore, InMemoryUserStore, InMemoryTenantStore, InMemorySessionStore, InMemoryMagicLinkStore, InMemoryGroupStore, IdTokenClaims, IAMValidators, IAMUsersInbound, IAMHandlerConfig, IAMGroupsOutbound, IAMGroupsInbound, IAMConfig, IAM, GroupsInboundHandlerConfig, GroupStore, GroupResource, GroupMember, Group, EnvironmentType, EnterpriseUser, EnterpriseStandard, EnterpriseExtension, Email, ESValidators, CreateUserOptions, CreateTenantResponse, CreateTenantRequest, CreateGroupOptions, ClientCredentialsWorkloadConfig, CachedWorkloadToken, CIAMConfig, CIAM, BaseUser, Address };