@enterprisestandard/core 0.0.6

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.
@@ -0,0 +1,2431 @@
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
+ /**
8
+ * SCIM Name sub-attribute
9
+ */
10
+ interface Name {
11
+ /**
12
+ * The full name, including all middle names, titles, and suffixes as appropriate
13
+ */
14
+ formatted?: string;
15
+ /**
16
+ * The family name of the User, or last name
17
+ */
18
+ familyName?: string;
19
+ /**
20
+ * The given name of the User, or first name
21
+ */
22
+ givenName?: string;
23
+ /**
24
+ * The middle name(s) of the User
25
+ */
26
+ middleName?: string;
27
+ /**
28
+ * The honorific prefix(es) of the User, or title
29
+ */
30
+ honorificPrefix?: string;
31
+ /**
32
+ * The honorific suffix(es) of the User, or suffix
33
+ */
34
+ honorificSuffix?: string;
35
+ }
36
+ /**
37
+ * SCIM Email sub-attribute
38
+ */
39
+ interface Email {
40
+ /**
41
+ * The email address value
42
+ */
43
+ value: string;
44
+ /**
45
+ * A human-readable name, primarily used for display purposes
46
+ */
47
+ display?: string;
48
+ /**
49
+ * A label indicating the attribute's function (e.g., "work" or "home")
50
+ */
51
+ type?: string;
52
+ /**
53
+ * A Boolean value indicating the 'primary' or preferred attribute value
54
+ */
55
+ primary?: boolean;
56
+ }
57
+ /**
58
+ * SCIM Phone Number sub-attribute
59
+ */
60
+ interface PhoneNumber {
61
+ /**
62
+ * The phone number value
63
+ */
64
+ value: string;
65
+ /**
66
+ * A human-readable name, primarily used for display purposes
67
+ */
68
+ display?: string;
69
+ /**
70
+ * A label indicating the attribute's function (e.g., "work", "home", "mobile")
71
+ */
72
+ type?: string;
73
+ /**
74
+ * A Boolean value indicating the 'primary' or preferred attribute value
75
+ */
76
+ primary?: boolean;
77
+ }
78
+ /**
79
+ * SCIM Address sub-attribute
80
+ */
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;
94
+ /**
95
+ * The state or region component
96
+ */
97
+ region?: string;
98
+ /**
99
+ * The zip code or postal code component
100
+ */
101
+ postalCode?: string;
102
+ /**
103
+ * The country name component
104
+ */
105
+ country?: string;
106
+ /**
107
+ * A label indicating the attribute's function (e.g., "work" or "home")
108
+ */
109
+ type?: string;
110
+ /**
111
+ * A Boolean value indicating the 'primary' or preferred attribute value
112
+ */
113
+ primary?: boolean;
114
+ }
115
+ /**
116
+ * SCIM Group reference (used within User resources)
117
+ */
118
+ interface Group {
119
+ /**
120
+ * The identifier of the User's group
121
+ */
122
+ value: string;
123
+ /**
124
+ * The URI of the corresponding 'Group' resource
125
+ */
126
+ $ref?: string;
127
+ /**
128
+ * A human-readable name, primarily used for display purposes
129
+ */
130
+ display?: string;
131
+ /**
132
+ * A label indicating the attribute's function (e.g., "direct" or "indirect")
133
+ */
134
+ type?: string;
135
+ }
136
+ /**
137
+ * SCIM Group Member reference
138
+ */
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";
156
+ }
157
+ /**
158
+ * SCIM 2.0 Group Resource
159
+ * @see https://datatracker.ietf.org/doc/html/rfc7643#section-4.2
160
+ */
161
+ interface GroupResource {
162
+ /**
163
+ * REQUIRED. The schemas attribute
164
+ */
165
+ schemas?: string[];
166
+ /**
167
+ * Unique identifier for the Group, assigned by the service provider
168
+ */
169
+ id?: string;
170
+ /**
171
+ * External identifier from the provisioning client
172
+ */
173
+ externalId?: string;
174
+ /**
175
+ * Resource metadata
176
+ */
177
+ meta?: {
178
+ resourceType?: string;
179
+ created?: string;
180
+ lastModified?: string;
181
+ location?: string;
182
+ version?: string;
183
+ };
184
+ /**
185
+ * REQUIRED. A human-readable name for the Group
186
+ */
187
+ displayName: string;
188
+ /**
189
+ * A list of members of the Group
190
+ */
191
+ members?: GroupMember[];
192
+ }
193
+ /**
194
+ * SCIM Role
195
+ */
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;
213
+ }
214
+ /**
215
+ * SCIM X509 Certificate
216
+ */
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;
234
+ }
235
+ /**
236
+ * SCIM Enterprise User Extension
237
+ * @see https://datatracker.ietf.org/doc/html/rfc7643#section-4.3
238
+ */
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;
260
+ /**
261
+ * The user's manager
262
+ */
263
+ manager?: {
264
+ /**
265
+ * The "id" of the SCIM resource representing the User's manager
266
+ */
267
+ value?: string;
268
+ /**
269
+ * The URI of the SCIM resource representing the User's manager
270
+ */
271
+ $ref?: string;
272
+ /**
273
+ * The displayName of the User's manager
274
+ */
275
+ displayName?: string;
276
+ };
277
+ }
278
+ /**
279
+ * SCIM User Resource
280
+ */
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;
290
+ /**
291
+ * Resource metadata
292
+ */
293
+ meta?: {
294
+ resourceType?: string;
295
+ created?: string;
296
+ lastModified?: string;
297
+ location?: string;
298
+ version?: string;
299
+ };
300
+ /**
301
+ * REQUIRED. Unique identifier for the User, typically used for login
302
+ */
303
+ userName: string;
304
+ /**
305
+ * The components of the user's name
306
+ */
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;
316
+ /**
317
+ * A fully qualified URL pointing to a page representing the User's online profile
318
+ */
319
+ profileUrl?: string;
320
+ /**
321
+ * The user's title, such as "Vice President"
322
+ */
323
+ title?: string;
324
+ /**
325
+ * Used to identify the relationship between the organization and the user
326
+ */
327
+ userType?: string;
328
+ /**
329
+ * Indicates the User's preferred written or spoken language
330
+ */
331
+ preferredLanguage?: string;
332
+ /**
333
+ * Used to indicate the User's default location for purposes of localizing items such as currency
334
+ */
335
+ locale?: string;
336
+ /**
337
+ * The User's time zone in the "Olson" time zone database format
338
+ */
339
+ timezone?: string;
340
+ /**
341
+ * A Boolean value indicating the User's administrative status
342
+ */
343
+ active?: boolean;
344
+ /**
345
+ * The User's cleartext password
346
+ */
347
+ password?: string;
348
+ /**
349
+ * Email addresses for the user
350
+ */
351
+ emails?: Email[];
352
+ /**
353
+ * Phone numbers for the User
354
+ */
355
+ phoneNumbers?: PhoneNumber[];
356
+ /**
357
+ * Instant messaging addresses for the User
358
+ */
359
+ ims?: Array<{
360
+ value: string;
361
+ display?: string;
362
+ type?: string;
363
+ primary?: boolean;
364
+ }>;
365
+ /**
366
+ * URLs of photos of the User
367
+ */
368
+ photos?: Array<{
369
+ value: string;
370
+ display?: string;
371
+ type?: string;
372
+ primary?: boolean;
373
+ }>;
374
+ /**
375
+ * Physical mailing addresses for this User
376
+ */
377
+ addresses?: Address[];
378
+ /**
379
+ * A list of groups to which the user belongs
380
+ */
381
+ groups?: Group[];
382
+ /**
383
+ * A list of entitlements for the User
384
+ */
385
+ entitlements?: Array<{
386
+ value: string;
387
+ display?: string;
388
+ type?: string;
389
+ primary?: boolean;
390
+ }>;
391
+ /**
392
+ * A list of roles for the User
393
+ */
394
+ roles?: Role[];
395
+ /**
396
+ * A list of certificates issued to the User
397
+ */
398
+ x509Certificates?: X509Certificate[];
399
+ /**
400
+ * Enterprise User Extension
401
+ */
402
+ "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"?: EnterpriseExtension;
403
+ /**
404
+ * REQUIRED. The schemas attribute is an array of Strings which allows introspection of the supported schema version
405
+ */
406
+ schemas?: string[];
407
+ }
408
+ /**
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.
416
+ * @param vendor - The name of the vendor creating this schema
417
+ * @returns A StandardSchemaV1 instance for SCIM Group resources
418
+ */
419
+ declare function groupResourceSchema(vendor: string): StandardSchemaV1<Record<string, unknown>, GroupResource>;
420
+ /**
421
+ * Stored group data with required id and tracking metadata.
422
+ *
423
+ * @template TExtended - Type-safe custom data that consumers can add to groups
424
+ */
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;
435
+ /**
436
+ * Optional external identifier from provisioning client.
437
+ */
438
+ externalId?: string;
439
+ /**
440
+ * List of members in the group.
441
+ */
442
+ members?: GroupMember[];
443
+ /**
444
+ * Timestamp when the group was first stored.
445
+ */
446
+ createdAt: Date;
447
+ /**
448
+ * Timestamp when the group was last updated.
449
+ */
450
+ updatedAt: Date;
451
+ } & TExtended;
452
+ /**
453
+ * Abstract interface for group storage backends.
454
+ *
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.)
459
+ *
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
468
+ */
469
+ get(id: string): Promise<StoredGroup<TExtended> | null>;
470
+ /**
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
475
+ */
476
+ getByExternalId(externalId: string): Promise<StoredGroup<TExtended> | null>;
477
+ /**
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
482
+ */
483
+ getByDisplayName(displayName: string): Promise<StoredGroup<TExtended> | null>;
484
+ /**
485
+ * List all groups in the store.
486
+ *
487
+ * @returns Array of all stored groups
488
+ */
489
+ list(): Promise<StoredGroup<TExtended>[]>;
490
+ /**
491
+ * Create or update a group in the store.
492
+ *
493
+ * If a group with the same `id` exists, it will be updated.
494
+ * Otherwise, a new group will be created.
495
+ *
496
+ * @param group - The group data to store
497
+ */
498
+ upsert(group: StoredGroup<TExtended>): Promise<void>;
499
+ /**
500
+ * Delete a group by its unique identifier.
501
+ *
502
+ * @param id - The group's unique identifier to delete
503
+ */
504
+ delete(id: string): Promise<void>;
505
+ /**
506
+ * Add a member to a group.
507
+ *
508
+ * @param groupId - The group's unique identifier
509
+ * @param member - The member to add
510
+ */
511
+ addMember(groupId: string, member: GroupMember): Promise<void>;
512
+ /**
513
+ * Remove a member from a group.
514
+ *
515
+ * @param groupId - The group's unique identifier
516
+ * @param memberId - The member's value/id to remove
517
+ */
518
+ removeMember(groupId: string, memberId: string): Promise<void>;
519
+ }
520
+ /**
521
+ * In-memory group store implementation using Maps.
522
+ *
523
+ * Suitable for:
524
+ * - Development and testing
525
+ * - Single-server deployments
526
+ * - Applications without high availability requirements
527
+ *
528
+ * NOT suitable for:
529
+ * - Multi-server deployments (groups not shared)
530
+ * - High availability scenarios (groups lost on restart)
531
+ * - Production applications with distributed architecture
532
+ *
533
+ * For production, implement GroupStore with Redis or a database.
534
+ *
535
+ * @template TExtended - Type-safe custom data that consumers can add to groups
536
+ */
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>;
552
+ }
553
+ /**
554
+ * Base user with simple, developer-friendly attributes.
555
+ * Extended by User (SSO) and EnterpriseUser (SCIM).
556
+ */
557
+ interface BaseUser {
558
+ /**
559
+ * Unique identifier for the user
560
+ */
561
+ id?: string;
562
+ /**
563
+ * REQUIRED. Unique identifier for login
564
+ */
565
+ userName: string;
566
+ /**
567
+ * REQUIRED. Simple display name
568
+ */
569
+ name: string;
570
+ /**
571
+ * REQUIRED. Primary email address
572
+ */
573
+ email: string;
574
+ /**
575
+ * URL to user's avatar/profile picture
576
+ */
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 {
585
+ /**
586
+ * REQUIRED. The authorization code returned from the authorization server.
587
+ */
588
+ code: string;
589
+ /**
590
+ * REQUIRED if the "state" parameter was present in the client authorization request.
591
+ * The exact value received from the client.
592
+ */
593
+ state?: string;
594
+ /**
595
+ * RECOMMENDED. The session state value. Clients should use this to verify the session state.
596
+ */
597
+ session_state?: string;
598
+ /**
599
+ * OAuth 2.0 error code if the authorization request failed.
600
+ */
601
+ error?: string;
602
+ /**
603
+ * Human-readable ASCII text providing additional information for the error.
604
+ */
605
+ error_description?: string;
606
+ /**
607
+ * A URI identifying a human-readable web page with information about the error.
608
+ */
609
+ error_uri?: string;
610
+ /**
611
+ * The "iss" (issuer) parameter identifies the principal that issued the response.
612
+ * This is typically used in the implicit flow.
613
+ */
614
+ iss?: string;
615
+ }
616
+ /**
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
620
+ */
621
+ declare function oidcCallbackSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, OidcCallbackParams>;
622
+ /**
623
+ * Token Response from IdP
624
+ */
625
+ interface TokenResponse {
626
+ access_token: string;
627
+ id_token: string;
628
+ refresh_token?: string;
629
+ token_type: string;
630
+ expires_in?: number;
631
+ scope?: string;
632
+ refresh_expires_in?: number;
633
+ session_state?: string;
634
+ expires?: string;
635
+ }
636
+ /**
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
640
+ */
641
+ declare function tokenResponseSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, TokenResponse>;
642
+ /**
643
+ * ID Token Claims
644
+ */
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
+ /**
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
662
+ */
663
+ declare function idTokenClaimsSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, IdTokenClaims>;
664
+ /**
665
+ * Primary user type for SSO/OIDC applications.
666
+ * Extends BaseUser with SSO-specific data.
667
+ */
668
+ interface User2 extends BaseUser {
669
+ /**
670
+ * SSO/OIDC authentication data
671
+ */
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
+ };
701
+ }
702
+ /**
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
711
+ */
712
+ type StoredUser<TExtended = {}> = User2 & {
713
+ /**
714
+ * Required unique identifier (the `sub` claim from the IdP).
715
+ * This is the primary key for user storage.
716
+ */
717
+ id: string;
718
+ /**
719
+ * Timestamp when the user was first stored.
720
+ */
721
+ createdAt: Date;
722
+ /**
723
+ * Timestamp when the user was last updated (e.g., on re-login).
724
+ */
725
+ updatedAt: Date;
726
+ } & TExtended;
727
+ /**
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
+ * ```
754
+ */
755
+ interface UserStore<TExtended = {}> {
756
+ /**
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
761
+ */
762
+ get(sub: string): Promise<StoredUser<TExtended> | null>;
763
+ /**
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
768
+ */
769
+ getByEmail(email: string): Promise<StoredUser<TExtended> | null>;
770
+ /**
771
+ * Retrieve a user by their username.
772
+ *
773
+ * @param userName - The user's username
774
+ * @returns The user if found, null otherwise
775
+ */
776
+ getByUserName(userName: string): Promise<StoredUser<TExtended> | null>;
777
+ /**
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
784
+ */
785
+ upsert(user: StoredUser<TExtended>): Promise<void>;
786
+ /**
787
+ * Delete a user by their subject identifier (sub).
788
+ *
789
+ * @param sub - The user's unique identifier to delete
790
+ */
791
+ delete(sub: string): Promise<void>;
792
+ }
793
+ /**
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
809
+ */
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>;
822
+ }
823
+ import { StandardSchemaV1 as StandardSchemaV14 } from "@standard-schema/spec";
824
+ import { StandardSchemaV1 as StandardSchemaV13 } from "@standard-schema/spec";
825
+ /**
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
829
+ */
830
+ interface JWTAssertionClaims {
831
+ /**
832
+ * REQUIRED. Issuer - the workload identity (e.g., SPIFFE ID) or authorization server
833
+ */
834
+ iss: string;
835
+ /**
836
+ * REQUIRED. Subject - the workload identity or service account
837
+ */
838
+ sub: string;
839
+ /**
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
842
+ */
843
+ aud?: string | string[];
844
+ /**
845
+ * REQUIRED. Expiration time (Unix timestamp)
846
+ */
847
+ exp: number;
848
+ /**
849
+ * REQUIRED. Issued at time (Unix timestamp)
850
+ */
851
+ iat: number;
852
+ /**
853
+ * OPTIONAL. JWT ID - unique identifier for this token
854
+ * Note: Required for JWT assertions, optional for access tokens
855
+ */
856
+ jti?: string;
857
+ /**
858
+ * OPTIONAL. Requested OAuth scopes (space-delimited)
859
+ */
860
+ scope?: string;
861
+ /**
862
+ * Allow additional claims for extensibility
863
+ */
864
+ [key: string]: unknown;
865
+ }
866
+ /**
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
875
+ */
876
+ interface WorkloadTokenResponse {
877
+ /**
878
+ * REQUIRED. The access token issued by the authorization server.
879
+ */
880
+ access_token: string;
881
+ /**
882
+ * REQUIRED. The type of the token (typically "Bearer").
883
+ */
884
+ token_type: string;
885
+ /**
886
+ * RECOMMENDED. The lifetime in seconds of the access token.
887
+ */
888
+ expires_in?: number;
889
+ /**
890
+ * OPTIONAL. The scope of the access token.
891
+ */
892
+ scope?: string;
893
+ /**
894
+ * OPTIONAL. The refresh token (rarely used for workload identities).
895
+ */
896
+ refresh_token?: string;
897
+ /**
898
+ * OPTIONAL. The expiration time as an ISO 8601 string.
899
+ */
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 {
912
+ /**
913
+ * Whether the token is valid
914
+ */
915
+ valid: boolean;
916
+ /**
917
+ * The decoded and validated claims (if valid)
918
+ */
919
+ claims?: JWTAssertionClaims;
920
+ /**
921
+ * Error message (if invalid)
922
+ */
923
+ error?: string;
924
+ /**
925
+ * Token expiration time (if valid)
926
+ */
927
+ expiresAt?: Date;
928
+ }
929
+ /**
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
977
+ */
978
+ type CachedWorkloadToken<TExtended = object> = {
979
+ /**
980
+ * Workload identifier (typically SPIFFE ID).
981
+ * Used as the primary key for token lookup.
982
+ */
983
+ workload_id: string;
984
+ /**
985
+ * OAuth2 access token (Bearer token)
986
+ */
987
+ access_token: string;
988
+ /**
989
+ * Token type (always "Bearer" for OAuth2)
990
+ */
991
+ token_type: string;
992
+ /**
993
+ * OAuth2 scopes granted for this token
994
+ */
995
+ scope?: string;
996
+ /**
997
+ * Timestamp when the token expires.
998
+ * Used for automatic cleanup and refresh logic.
999
+ */
1000
+ expires_at: Date;
1001
+ /**
1002
+ * Timestamp when the token was created/cached.
1003
+ */
1004
+ created_at: Date;
1005
+ /**
1006
+ * Optional refresh token (rarely used for workload identities)
1007
+ */
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> {
1055
+ /**
1056
+ * Store or update a workload token in the cache.
1057
+ *
1058
+ * @param token - The token data to cache
1059
+ */
1060
+ set(token: CachedWorkloadToken<TExtended>): Promise<void>;
1061
+ /**
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
1066
+ */
1067
+ get(workload_id: string): Promise<CachedWorkloadToken<TExtended> | null>;
1068
+ /**
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
1074
+ */
1075
+ delete(workload_id: string): Promise<void>;
1076
+ /**
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
1081
+ */
1082
+ isValid(workload_id: string): Promise<boolean>;
1083
+ /**
1084
+ * Remove all expired tokens from the cache.
1085
+ *
1086
+ * Should be called periodically to prevent memory leaks.
1087
+ */
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 = {
1119
+ /**
1120
+ * OAuth2 token endpoint URL
1121
+ * REQUIRED for client role (token acquisition)
1122
+ */
1123
+ tokenUrl?: string;
1124
+ /**
1125
+ * JWKS endpoint URL for public key retrieval
1126
+ * REQUIRED for server role (token validation)
1127
+ */
1128
+ jwksUri?: string;
1129
+ /**
1130
+ * Expected token issuer URL for validation
1131
+ * RECOMMENDED for server role (token validation)
1132
+ */
1133
+ issuer?: string;
1134
+ /**
1135
+ * Target audience for tokens
1136
+ */
1137
+ audience?: string;
1138
+ /**
1139
+ * Default OAuth2 scopes (space-delimited)
1140
+ */
1141
+ scope?: string;
1142
+ /**
1143
+ * JWT assertion/token lifetime in seconds
1144
+ * @default 300 (5 minutes)
1145
+ */
1146
+ tokenLifetime?: number;
1147
+ /**
1148
+ * Refresh threshold in seconds (refresh token this many seconds before expiry)
1149
+ * @default 60
1150
+ */
1151
+ refreshThreshold?: number;
1152
+ /**
1153
+ * Optional token store for caching access tokens
1154
+ */
1155
+ tokenStore?: WorkloadTokenStore;
1156
+ /**
1157
+ * Automatically refresh tokens before expiration
1158
+ * @default true
1159
+ */
1160
+ autoRefresh?: boolean;
1161
+ /**
1162
+ * Optional RFC 7009 token revocation endpoint
1163
+ */
1164
+ revocationEndpoint?: string;
1165
+ /**
1166
+ * Optional handler defaults (merged with per-call overrides in `handler`)
1167
+ */
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
+ };
1177
+ /**
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.
1182
+ *
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
+ * ```
1194
+ */
1195
+ type JwtBearerWorkloadConfig = WorkloadConfigBase & {
1196
+ /**
1197
+ * Workload identifier (e.g., SPIFFE ID: spiffe://trust-domain/namespace/service)
1198
+ * REQUIRED for JWT Bearer Grant mode
1199
+ */
1200
+ workloadId: string;
1201
+ /**
1202
+ * PEM-encoded private key for signing JWT assertions
1203
+ * REQUIRED for client role in JWT Bearer Grant mode
1204
+ */
1205
+ privateKey: string;
1206
+ /**
1207
+ * Key ID (kid) to include in JWT header for key rotation support
1208
+ */
1209
+ keyId?: string;
1210
+ /**
1211
+ * JWT signing algorithm
1212
+ * @default 'RS256'
1213
+ */
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 & {
1235
+ /**
1236
+ * OAuth2 client identifier registered with the authorization server
1237
+ * REQUIRED for Client Credentials mode
1238
+ */
1239
+ clientId: string;
1240
+ /**
1241
+ * OAuth2 client secret
1242
+ * REQUIRED for Client Credentials mode
1243
+ */
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
+ };
1264
+ /**
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:
1269
+ *
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
1273
+ *
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
1279
+ */
1280
+ type WorkloadIdentity = {
1281
+ /**
1282
+ * Workload identifier (for JWT Bearer Grant tokens)
1283
+ */
1284
+ workloadId?: string;
1285
+ /**
1286
+ * Client identifier (for OAuth2 Client Credentials tokens)
1287
+ */
1288
+ clientId?: string;
1289
+ /**
1290
+ * Granted scopes
1291
+ */
1292
+ scope?: string;
1293
+ /**
1294
+ * Full JWT claims from the token
1295
+ */
1296
+ claims: JWTAssertionClaims;
1297
+ };
1298
+ /**
1299
+ * Workload Identity Authentication Interface
1300
+ */
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;
1313
+ /**
1314
+ * SCIM Error response structure
1315
+ */
1316
+ interface ScimError {
1317
+ schemas: string[];
1318
+ status: string;
1319
+ scimType?: string;
1320
+ detail?: string;
1321
+ }
1322
+ /**
1323
+ * SCIM List Response for bulk operations
1324
+ */
1325
+ interface ScimListResponse<T> {
1326
+ schemas: string[];
1327
+ totalResults: number;
1328
+ startIndex?: number;
1329
+ itemsPerPage?: number;
1330
+ Resources: T[];
1331
+ }
1332
+ /**
1333
+ * Result of a SCIM operation
1334
+ */
1335
+ interface ScimResult<T> {
1336
+ success: boolean;
1337
+ data?: T;
1338
+ error?: ScimError;
1339
+ status: number;
1340
+ }
1341
+ /**
1342
+ * Handler configuration for IAM
1343
+ */
1344
+ interface IAMHandlerConfig {
1345
+ /**
1346
+ * Base path for the SCIM Users endpoints (e.g., '/api/iam/Users')
1347
+ */
1348
+ usersUrl?: string;
1349
+ /**
1350
+ * Base path for the SCIM Groups endpoints (e.g., '/api/iam/Groups')
1351
+ */
1352
+ groupsUrl?: string;
1353
+ }
1354
+ /**
1355
+ * IAM configuration
1356
+ *
1357
+ * - If `url` is provided, groups_outbound is enabled (app calls external IAM)
1358
+ * - If `groupStore` is provided, groups_inbound is enabled (external IAM calls app)
1359
+ * - If `userStore` is provided, users_inbound is enabled (external IAM calls app)
1360
+ */
1361
+ type IAMConfig = {
1362
+ /**
1363
+ * Base URL of the external SCIM endpoint (e.g., https://sailpoint.example.com/scim/v2)
1364
+ * If provided, enables outbound SCIM operations (app -> external IAM)
1365
+ */
1366
+ url?: string;
1367
+ /**
1368
+ * Store for inbound user provisioning from external IAM providers.
1369
+ * When configured, the app can receive user CRUD operations via SCIM.
1370
+ */
1371
+ userStore?: UserStore;
1372
+ /**
1373
+ * Store for inbound group provisioning from external IAM providers.
1374
+ * When configured, enables groups_inbound (external IAM -> app).
1375
+ */
1376
+ groupStore?: GroupStore;
1377
+ /**
1378
+ * Optional handler defaults. These are merged with per-call overrides in
1379
+ * `iam.handler`, with per-call values taking precedence.
1380
+ */
1381
+ usersUrl?: string;
1382
+ groupsUrl?: string;
1383
+ };
1384
+ type IAMValidators = {
1385
+ user: StandardSchemaV15<unknown, User>;
1386
+ group: StandardSchemaV15<unknown, GroupResource>;
1387
+ };
1388
+ /**
1389
+ * Options for creating a user
1390
+ */
1391
+ interface CreateUserOptions {
1392
+ /**
1393
+ * External identifier for the user
1394
+ */
1395
+ externalId?: string;
1396
+ }
1397
+ /**
1398
+ * Options for creating a group
1399
+ */
1400
+ interface CreateGroupOptions {
1401
+ /**
1402
+ * External identifier for the group
1403
+ */
1404
+ externalId?: string;
1405
+ /**
1406
+ * Initial members to add to the group
1407
+ */
1408
+ members?: GroupMember[];
1409
+ }
1410
+ /**
1411
+ * Handler configuration for groups_inbound
1412
+ */
1413
+ interface GroupsInboundHandlerConfig {
1414
+ /**
1415
+ * Base path for the SCIM Groups endpoints (e.g., '/api/iam/Groups')
1416
+ */
1417
+ basePath?: string;
1418
+ }
1419
+ /**
1420
+ * Handler configuration for users_inbound
1421
+ */
1422
+ interface UsersInboundHandlerConfig {
1423
+ /**
1424
+ * Base path for the SCIM Users endpoints (e.g., '/api/iam/Users')
1425
+ */
1426
+ basePath?: string;
1427
+ }
1428
+ /**
1429
+ * Groups Outbound extension - for creating groups in external IAM providers.
1430
+ * Enabled when `url` is configured in IAMConfig.
1431
+ */
1432
+ type IAMGroupsOutbound = {
1433
+ /**
1434
+ * Create a new group in the external IAM provider
1435
+ * @param displayName - The display name for the group
1436
+ * @param options - Optional configuration for the group creation
1437
+ * @returns The created group resource from the provider
1438
+ */
1439
+ createGroup: (displayName: string, options?: CreateGroupOptions) => Promise<ScimResult<GroupResource>>;
1440
+ };
1441
+ /**
1442
+ * Groups Inbound extension - for receiving group provisioning from external IAM providers.
1443
+ * Enabled when `groupStore` is configured in IAMConfig.
1444
+ */
1445
+ type IAMGroupsInbound = {
1446
+ /**
1447
+ * Handle inbound SCIM requests for group management.
1448
+ * Routes: GET/POST /Groups, GET/PUT/PATCH/DELETE /Groups/:id
1449
+ */
1450
+ handler: (request: Request, config?: GroupsInboundHandlerConfig) => Promise<Response>;
1451
+ };
1452
+ /**
1453
+ * Users Inbound extension - for receiving user provisioning from external IAM providers.
1454
+ * Enabled when `userStore` is configured in IAMConfig.
1455
+ */
1456
+ type IAMUsersInbound = {
1457
+ /**
1458
+ * Handle inbound SCIM requests for user management.
1459
+ * Routes: GET/POST /Users, GET/PUT/PATCH/DELETE /Users/:id
1460
+ */
1461
+ handler: (request: Request, config?: UsersInboundHandlerConfig) => Promise<Response>;
1462
+ };
1463
+ /**
1464
+ * Core IAM service interface.
1465
+ *
1466
+ * - Core functions are user-related (outbound to external IAM)
1467
+ * - `groups_outbound` is available when `url` is configured
1468
+ * - `groups_inbound` is available when `groupStore` is configured
1469
+ * - `users_inbound` is available when `userStore` is configured
1470
+ */
1471
+ type IAM = IAMConfig & {
1472
+ /**
1473
+ * Create a new user/account in the external IAM provider
1474
+ * Only available when `url` is configured.
1475
+ */
1476
+ createUser?: (user: User, options?: CreateUserOptions) => Promise<ScimResult<User>>;
1477
+ /**
1478
+ * Get the configured external SCIM base URL
1479
+ */
1480
+ getBaseUrl: () => string | undefined;
1481
+ /**
1482
+ * Groups Outbound extension - create groups in external IAM provider.
1483
+ * Available when `url` is configured in IAMConfig.
1484
+ */
1485
+ groups_outbound?: IAMGroupsOutbound;
1486
+ /**
1487
+ * Groups Inbound extension - receive group provisioning from external IAM.
1488
+ * Available when `groupStore` is configured in IAMConfig.
1489
+ */
1490
+ groups_inbound?: IAMGroupsInbound;
1491
+ /**
1492
+ * Users Inbound extension - receive user provisioning from external IAM.
1493
+ * Available when `userStore` is configured in IAMConfig.
1494
+ */
1495
+ users_inbound?: IAMUsersInbound;
1496
+ /**
1497
+ * Framework-agnostic request handler for IAM endpoints.
1498
+ * Routes to users_inbound or groups_inbound handlers based on the request path.
1499
+ */
1500
+ handler: (request: Request, config?: IAMHandlerConfig) => Promise<Response>;
1501
+ };
1502
+ /**
1503
+ * Creates an IAM service instance.
1504
+ *
1505
+ * - If `url` is configured, enables outbound SCIM operations to external IAM
1506
+ * - If `groupStore` is configured, enables inbound SCIM operations from external IAM
1507
+ *
1508
+ * @param config - IAM configuration
1509
+ * @param workload - Workload instance for authentication
1510
+ * @returns IAM service instance
1511
+ */
1512
+ declare function iam(validators: IAMValidators, workload?: Workload, fromVault?: Partial<IAMConfig>, fromCode?: Partial<IAMConfig>): IAM | undefined;
1513
+ 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
+ type SSOConfig<
1693
+ TSessionData = {},
1694
+ TUserData = {}
1695
+ > = {
1696
+ authority?: string;
1697
+ tokenUrl?: string;
1698
+ authorizationUrl?: string;
1699
+ clientId?: string;
1700
+ clientSecret?: string;
1701
+ redirectUri?: string;
1702
+ responseType?: "code";
1703
+ scope?: string;
1704
+ silentRedirectUri?: string;
1705
+ jwksUri?: string;
1706
+ cookiesPrefix?: string;
1707
+ cookiesPath?: string;
1708
+ cookiesSecure?: boolean;
1709
+ cookiesSameSite?: "Strict" | "Lax";
1710
+ endSessionEndpoint?: string;
1711
+ revocationEndpoint?: string;
1712
+ sessionStore?: SessionStore<TSessionData>;
1713
+ /**
1714
+ * Optional user store for persisting user profiles from SSO authentication.
1715
+ * When configured, users are automatically stored/updated on each login.
1716
+ */
1717
+ userStore?: UserStore<TUserData>;
1718
+ /**
1719
+ * Enable Just-In-Time (JIT) user provisioning.
1720
+ * When enabled, new users are automatically created in the userStore on their first login.
1721
+ * When disabled (default), only existing users in the userStore are updated on login.
1722
+ * Requires userStore to be configured.
1723
+ * @default false
1724
+ */
1725
+ enableJitUserProvisioning?: boolean;
1726
+ validators?: SSOValidators;
1727
+ } & SSOHandlerConfig;
1728
+ type LoginConfig = {
1729
+ landingUrl: string;
1730
+ errorUrl?: string;
1731
+ };
1732
+ type SSOHandlerConfig = {
1733
+ loginUrl?: string;
1734
+ userUrl?: string;
1735
+ errorUrl?: string;
1736
+ landingUrl?: string;
1737
+ tokenUrl?: string;
1738
+ refreshUrl?: string;
1739
+ jwksUrl?: string;
1740
+ logoutUrl?: string;
1741
+ logoutBackChannelUrl?: string;
1742
+ };
1743
+ type SSOValidators = {
1744
+ callbackParams: StandardSchemaV16<unknown, OidcCallbackParams>;
1745
+ idTokenClaims: StandardSchemaV16<unknown, IdTokenClaims>;
1746
+ tokenResponse: StandardSchemaV16<unknown, TokenResponse>;
1747
+ };
1748
+ type SSO<
1749
+ TSessionData = {},
1750
+ TUserData = {}
1751
+ > = {
1752
+ config: SSOConfig<TSessionData, TUserData>;
1753
+ getUser: (request: Request) => Promise<User2 | undefined>;
1754
+ getRequiredUser: (request: Request) => Promise<User2>;
1755
+ getJwt: (request: Request) => Promise<string | undefined>;
1756
+ initiateLogin: (config: LoginConfig, requestUrl?: string) => Promise<Response>;
1757
+ logout: (request: Request, config?: LoginConfig) => Promise<Response>;
1758
+ callbackHandler: (request: Request) => Promise<Response>;
1759
+ handler: (request: Request) => Promise<Response>;
1760
+ };
1761
+ declare function sso<
1762
+ TSessionData = {},
1763
+ TUserData = {}
1764
+ >(validators: SSOValidators, fromVault?: Partial<SSOConfig<TSessionData, TUserData>>, fromCode?: Partial<SSOConfig<TSessionData, TUserData>>): SSO<TSessionData, TUserData> | undefined;
1765
+ import { StandardSchemaV1 as StandardSchemaV17 } from "@standard-schema/spec";
1766
+ /**
1767
+ * Environment type for tenant creation
1768
+ */
1769
+ type EnvironmentType = "POC" | "DEV" | "QA" | "PROD";
1770
+ /**
1771
+ * Status of tenant creation process
1772
+ */
1773
+ type TenantStatus = "pending" | "processing" | "completed" | "failed";
1774
+ /**
1775
+ * Request payload from ESVS for creating a tenant
1776
+ */
1777
+ interface CreateTenantRequest {
1778
+ /**
1779
+ * Required app identifier to use when initializing EnterpriseStandard for this tenant.
1780
+ * This is the primary identifier for tenant management. A company can have multiple
1781
+ * applications (e.g., one instance on the east coast, one on the west coast).
1782
+ */
1783
+ appId: string;
1784
+ /**
1785
+ * Company ID (used for reporting purposes only, not for tenant identification)
1786
+ */
1787
+ companyId: string;
1788
+ /**
1789
+ * Company Name
1790
+ */
1791
+ companyName: string;
1792
+ /**
1793
+ * Environment Type (POC, DEV, QA, PROD)
1794
+ */
1795
+ environmentType: EnvironmentType;
1796
+ /**
1797
+ * Email (The email or distribution list used to communicate to the team)
1798
+ */
1799
+ email: string;
1800
+ /**
1801
+ * Webhook URL where the application can send updates around the creation of the tenant
1802
+ */
1803
+ webhookUrl: string;
1804
+ /**
1805
+ * URL that the tenant will be available at.
1806
+ * Can be a full URL (e.g., "https://app.example.com/tenants/tenant-123")
1807
+ * or a relative path (e.g., "/tenants/tenant-123").
1808
+ * If relative, it will be normalized using the base URL from the request.
1809
+ */
1810
+ tenantUrl: string;
1811
+ }
1812
+ /**
1813
+ * Response payload for tenant creation
1814
+ */
1815
+ interface CreateTenantResponse {
1816
+ /**
1817
+ * URL that the tenant will be available at
1818
+ */
1819
+ tenantUrl: string;
1820
+ /**
1821
+ * Current status of tenant creation
1822
+ */
1823
+ status: TenantStatus;
1824
+ }
1825
+ /**
1826
+ * Payload sent to webhook URL for status updates
1827
+ */
1828
+ interface TenantWebhookPayload {
1829
+ /**
1830
+ * Company ID
1831
+ */
1832
+ companyId: string;
1833
+ /**
1834
+ * Current status of tenant creation
1835
+ */
1836
+ status: TenantStatus;
1837
+ /**
1838
+ * URL that the tenant will be available at (provided once creation completes)
1839
+ */
1840
+ tenantUrl?: string;
1841
+ /**
1842
+ * Error message (only present if status is "failed")
1843
+ */
1844
+ error?: string;
1845
+ }
1846
+ /**
1847
+ * Validators for tenant management
1848
+ */
1849
+ type TenantValidators = {
1850
+ createTenantRequest: StandardSchemaV17<unknown, CreateTenantRequest>;
1851
+ };
1852
+ /**
1853
+ * Configuration for tenant management
1854
+ */
1855
+ type TenantConfig = {};
1856
+ /**
1857
+ * Tenant service interface
1858
+ */
1859
+ type Tenant = {
1860
+ config: TenantConfig;
1861
+ /**
1862
+ * Parse and validate a tenant creation request from an HTTP request.
1863
+ * Returns a validation result object with either `issues` (if validation fails) or `value` (if validation succeeds).
1864
+ *
1865
+ * @param request - The HTTP request containing the tenant creation data
1866
+ * @returns Validation result with either `issues` array or `value` containing the validated request
1867
+ *
1868
+ * @example
1869
+ * ```typescript
1870
+ * app.post('/api/tenant', async (c) => {
1871
+ * const result = await tenantService.parseTenantRequest(c.req.raw);
1872
+ * if (result.issues) {
1873
+ * return validationFailureResponse(result.issues, 'Tenant request validation failed');
1874
+ * }
1875
+ * // Use result.value as CreateTenantRequest
1876
+ * });
1877
+ * ```
1878
+ */
1879
+ parseTenantRequest: (request: Request) => Promise<StandardSchemaV17.Result<CreateTenantRequest>>;
1880
+ /**
1881
+ * Send a webhook update to ESVS with tenant creation status.
1882
+ *
1883
+ * @param webhookUrl - The webhook URL provided in the tenant creation request
1884
+ * @param payload - The webhook payload with status and tenant information
1885
+ */
1886
+ sendTenantWebhook: (webhookUrl: string, payload: TenantWebhookPayload) => Promise<void>;
1887
+ };
1888
+ /**
1889
+ * Creates a tenant service instance.
1890
+ *
1891
+ * @param validators - Validators for tenant request validation
1892
+ * @param fromVault - Configuration from vault (optional)
1893
+ * @param fromCode - Configuration from code (optional)
1894
+ * @returns Tenant service instance or undefined if no config provided
1895
+ */
1896
+ declare function tenant(validators: TenantValidators, fromVault?: Partial<TenantConfig>, fromCode?: Partial<TenantConfig>): Tenant | undefined;
1897
+ /**
1898
+ * Stored tenant data with required appId and tracking metadata.
1899
+ *
1900
+ * @template TExtended - Type-safe custom data that consumers can add to tenants
1901
+ */
1902
+ type StoredTenant<TExtended = {}> = {
1903
+ /**
1904
+ * Required app identifier used to initialize EnterpriseStandard for this tenant.
1905
+ * This is the primary key for tenant storage. A company can have multiple
1906
+ * applications (e.g., one instance on the east coast, one on the west coast).
1907
+ */
1908
+ appId: string;
1909
+ /**
1910
+ * Company ID (used for reporting purposes only, not for tenant identification)
1911
+ */
1912
+ companyId: string;
1913
+ /**
1914
+ * Company Name
1915
+ */
1916
+ companyName: string;
1917
+ /**
1918
+ * Environment Type (POC, DEV, QA, PROD)
1919
+ */
1920
+ environmentType: EnvironmentType;
1921
+ /**
1922
+ * Email (The email or distribution list used to communicate to the team)
1923
+ */
1924
+ email: string;
1925
+ /**
1926
+ * Webhook URL where the application can send updates around the creation of the tenant
1927
+ */
1928
+ webhookUrl: string;
1929
+ /**
1930
+ * URL that the tenant will be available at
1931
+ */
1932
+ tenantUrl?: string;
1933
+ /**
1934
+ * Current status of tenant creation
1935
+ */
1936
+ status: TenantStatus;
1937
+ /**
1938
+ * Error message (only present if status is "failed")
1939
+ */
1940
+ error?: string;
1941
+ /**
1942
+ * Timestamp when the tenant was first stored.
1943
+ */
1944
+ createdAt: Date;
1945
+ /**
1946
+ * Timestamp when the tenant was last updated.
1947
+ */
1948
+ updatedAt: Date;
1949
+ /**
1950
+ * Serialized Enterprise Standard configuration.
1951
+ * This is a JSON-serializable version of the ESConfig with non-serializable items excluded.
1952
+ */
1953
+ config?: any;
1954
+ } & TExtended;
1955
+ /**
1956
+ * Abstract interface for tenant storage backends.
1957
+ *
1958
+ * Consumers can implement this interface to use different storage backends:
1959
+ * - In-memory (for development/testing)
1960
+ * - Redis (for production with fast lookups)
1961
+ * - Database (PostgreSQL, MySQL, etc.)
1962
+ *
1963
+ * @template TExtended - Type-safe custom data that consumers can add to tenants
1964
+ */
1965
+ interface TenantStore<TExtended = {}> {
1966
+ /**
1967
+ * Retrieve a tenant by its app identifier.
1968
+ *
1969
+ * @param appId - The tenant's app identifier (primary key)
1970
+ * @returns The tenant if found, null otherwise
1971
+ */
1972
+ get(appId: string): Promise<StoredTenant<TExtended> | null>;
1973
+ /**
1974
+ * Retrieve all tenants for a company ID.
1975
+ * Since a company can have multiple applications, this returns an array.
1976
+ *
1977
+ * @param companyId - The company ID (used for reporting, not primary identification)
1978
+ * @returns Array of tenants for the company, empty array if none found
1979
+ */
1980
+ getByCompanyId(companyId: string): Promise<StoredTenant<TExtended>[]>;
1981
+ /**
1982
+ * List all tenants in the store.
1983
+ *
1984
+ * @returns Array of all stored tenants
1985
+ */
1986
+ list(): Promise<StoredTenant<TExtended>[]>;
1987
+ /**
1988
+ * Create or update a tenant in the store.
1989
+ *
1990
+ * If a tenant with the same `appId` exists, it will be updated.
1991
+ * Otherwise, a new tenant will be created.
1992
+ *
1993
+ * @param tenant - The tenant data to store
1994
+ * @returns The stored tenant
1995
+ */
1996
+ upsert(tenant: StoredTenant<TExtended>): Promise<StoredTenant<TExtended>>;
1997
+ /**
1998
+ * Delete a tenant by its app identifier.
1999
+ *
2000
+ * @param appId - The tenant's app identifier to delete
2001
+ */
2002
+ delete(appId: string): Promise<void>;
2003
+ }
2004
+ /**
2005
+ * In-memory tenant store implementation using Maps.
2006
+ *
2007
+ * Suitable for:
2008
+ * - Development and testing
2009
+ * - Single-server deployments
2010
+ * - Applications without high availability requirements
2011
+ *
2012
+ * NOT suitable for:
2013
+ * - Multi-server deployments (tenants not shared)
2014
+ * - High availability scenarios (tenants lost on restart)
2015
+ * - Production applications with distributed architecture
2016
+ *
2017
+ * For production, implement TenantStore with Redis or a database.
2018
+ *
2019
+ * @template TExtended - Type-safe custom data that consumers can add to tenants
2020
+ */
2021
+ declare class InMemoryTenantStore<TExtended = {}> implements TenantStore<TExtended> {
2022
+ /** Primary storage: appId -> tenant */
2023
+ private tenants;
2024
+ /** Secondary index: companyId -> Set of appIds (since one company can have multiple apps) */
2025
+ private companyIdIndex;
2026
+ get(appId: string): Promise<StoredTenant<TExtended> | null>;
2027
+ getByCompanyId(companyId: string): Promise<StoredTenant<TExtended>[]>;
2028
+ list(): Promise<StoredTenant<TExtended>[]>;
2029
+ upsert(tenant: StoredTenant<TExtended>): Promise<StoredTenant<TExtended>>;
2030
+ delete(appId: string): Promise<void>;
2031
+ }
2032
+ type LfvConfig = {
2033
+ type: "lfv";
2034
+ webhookUrl: string;
2035
+ };
2036
+ type VaultItem<T> = {
2037
+ data: T;
2038
+ metadata: MetaData;
2039
+ };
2040
+ type MetaData = {
2041
+ path: string;
2042
+ version: number;
2043
+ created: Date;
2044
+ };
2045
+ type VaultType = "lfv" | "azure" | "aws" | "gcp" | "openbao" | "dev";
2046
+ type Vault = {
2047
+ type: VaultType;
2048
+ getFullSecret: <T>(path: string) => Promise<VaultItem<T>>;
2049
+ getSecret: <T>(path: string) => Promise<T>;
2050
+ };
2051
+ type AzureConfig = {
2052
+ type: "azure";
2053
+ };
2054
+ type AwsConfig = {
2055
+ type: "aws";
2056
+ };
2057
+ type GcpConfig = {
2058
+ type: "gcp";
2059
+ };
2060
+ type OpenBaoConfig = {
2061
+ type: "openbao";
2062
+ url?: string;
2063
+ token?: string;
2064
+ };
2065
+ type DevConfig = {
2066
+ type: "dev";
2067
+ };
2068
+ type VaultConfig = LfvConfig | AzureConfig | AwsConfig | GcpConfig | OpenBaoConfig | DevConfig;
2069
+ declare function vault(config?: VaultConfig): Vault;
2070
+ /**
2071
+ * Helper gets the user from the Request using the supplied EnterpriseStandard or the default instance
2072
+ */
2073
+ declare function getUser2(request: Request, es?: EnterpriseStandard): Promise<User2 | undefined>;
2074
+ declare function getRequiredUser2(request: Request, es?: EnterpriseStandard): Promise<User2>;
2075
+ declare function initiateLogin2(config: LoginConfig, es?: EnterpriseStandard): Promise<Response>;
2076
+ declare function callback(request: Request, es?: EnterpriseStandard): Promise<Response>;
2077
+ /**
2078
+ * Enterprise user with SCIM attributes.
2079
+ * Extends BaseUser (simple fields) with optional complex SCIM fields.
2080
+ * For IAM/provisioning and enterprise directory integration.
2081
+ */
2082
+ interface EnterpriseUser extends BaseUser {
2083
+ /**
2084
+ * External identifier from the provisioning system
2085
+ */
2086
+ externalId?: string;
2087
+ /**
2088
+ * Resource metadata
2089
+ */
2090
+ meta?: {
2091
+ resourceType?: string;
2092
+ created?: string;
2093
+ lastModified?: string;
2094
+ version?: string;
2095
+ location?: string;
2096
+ };
2097
+ /**
2098
+ * SCIM schemas supported by this user
2099
+ */
2100
+ schemas?: string[];
2101
+ /**
2102
+ * Structured name with family/given names, prefixes, suffixes.
2103
+ * Use alongside the simple `name` string from BaseUser.
2104
+ */
2105
+ fullName?: Name;
2106
+ /**
2107
+ * Multiple email addresses with types (work, home, etc.).
2108
+ * Use alongside the simple `email` string from BaseUser.
2109
+ */
2110
+ emails?: Email[];
2111
+ /**
2112
+ * Name for display purposes
2113
+ */
2114
+ displayName?: string;
2115
+ /**
2116
+ * Casual name to address the user
2117
+ */
2118
+ nickName?: string;
2119
+ /**
2120
+ * URL to user's online profile
2121
+ */
2122
+ profileUrl?: string;
2123
+ /**
2124
+ * Job title
2125
+ */
2126
+ title?: string;
2127
+ /**
2128
+ * User type (e.g., "Employee", "Contractor")
2129
+ */
2130
+ userType?: string;
2131
+ /**
2132
+ * Preferred language (e.g., "en-US")
2133
+ */
2134
+ preferredLanguage?: string;
2135
+ /**
2136
+ * Locale for localization (e.g., "en-US")
2137
+ */
2138
+ locale?: string;
2139
+ /**
2140
+ * Timezone (e.g., "America/New_York")
2141
+ */
2142
+ timezone?: string;
2143
+ /**
2144
+ * Whether the user account is active
2145
+ */
2146
+ active?: boolean;
2147
+ /**
2148
+ * Password (for provisioning only, should not be returned)
2149
+ */
2150
+ password?: string;
2151
+ /**
2152
+ * Phone numbers
2153
+ */
2154
+ phoneNumbers?: PhoneNumber[];
2155
+ /**
2156
+ * Instant messaging addresses
2157
+ */
2158
+ ims?: Array<{
2159
+ value: string;
2160
+ display?: string;
2161
+ type?: string;
2162
+ primary?: boolean;
2163
+ }>;
2164
+ /**
2165
+ * Photo URLs
2166
+ */
2167
+ photos?: Array<{
2168
+ value: string;
2169
+ display?: string;
2170
+ type?: string;
2171
+ primary?: boolean;
2172
+ }>;
2173
+ /**
2174
+ * Physical mailing addresses
2175
+ */
2176
+ addresses?: Address[];
2177
+ /**
2178
+ * Groups the user belongs to
2179
+ */
2180
+ groups?: Group[];
2181
+ /**
2182
+ * Entitlements
2183
+ */
2184
+ entitlements?: Array<{
2185
+ value: string;
2186
+ display?: string;
2187
+ type?: string;
2188
+ primary?: boolean;
2189
+ }>;
2190
+ /**
2191
+ * Roles assigned to the user
2192
+ */
2193
+ roles?: Role[];
2194
+ /**
2195
+ * X.509 certificates
2196
+ */
2197
+ x509Certificates?: Array<{
2198
+ value: string;
2199
+ display?: string;
2200
+ type?: string;
2201
+ primary?: boolean;
2202
+ }>;
2203
+ /**
2204
+ * Employee number
2205
+ */
2206
+ employeeNumber?: string;
2207
+ /**
2208
+ * Cost center
2209
+ */
2210
+ costCenter?: string;
2211
+ /**
2212
+ * Organization name
2213
+ */
2214
+ organization?: string;
2215
+ /**
2216
+ * Division name
2217
+ */
2218
+ division?: string;
2219
+ /**
2220
+ * Department name
2221
+ */
2222
+ department?: string;
2223
+ /**
2224
+ * User's manager
2225
+ */
2226
+ manager?: {
2227
+ value: string;
2228
+ $ref?: string;
2229
+ displayName?: string;
2230
+ };
2231
+ }
2232
+ /**
2233
+ * Get the default EnterpriseStandard instance
2234
+ */
2235
+ declare function getDefaultInstance(): EnterpriseStandard | undefined;
2236
+ /**
2237
+ * Returns a 400 Response with the issues if there are any.
2238
+ * @param issues - Any validation issues.
2239
+ * @param message - The message to include in the response.
2240
+ * @returns A 400 Response with the issues if it does, otherwise null.
2241
+ */
2242
+ declare function validationFailureResponse(issues: any, message: string): Response;
2243
+ /**
2244
+ * Serializes an ESConfig or EnterpriseStandard instance to a JSON-serializable format
2245
+ * by removing non-serializable properties like stores, validators, and functions.
2246
+ *
2247
+ * Since EnterpriseStandard now extends ESConfig, the config (including handler URLs)
2248
+ * is accessible directly from the instance.
2249
+ *
2250
+ * @param configOrES - The ESConfig object or EnterpriseStandard instance to serialize
2251
+ * @returns A JSON-serializable version of the config
2252
+ */
2253
+ declare function serializeESConfig(configOrES: any): any;
2254
+ /**
2255
+ * Get the workload identity from an incoming request.
2256
+ * Returns undefined if no valid workload token is present.
2257
+ *
2258
+ * @param request - Request with Authorization header
2259
+ * @param config - Optional EnterpriseStandard configuration
2260
+ * @returns WorkloadIdentity or undefined
2261
+ *
2262
+ * @example
2263
+ * ```typescript
2264
+ * import { getWorkload } from '@enterprisestandard/react';
2265
+ *
2266
+ * async function handler(request: Request) {
2267
+ * const workload = await getWorkload(request);
2268
+ *
2269
+ * if (!workload) {
2270
+ * return new Response('Unauthorized', { status: 401 });
2271
+ * }
2272
+ *
2273
+ * console.log('Request from workload:', workload.workloadId);
2274
+ * // ... process authenticated request
2275
+ * }
2276
+ * ```
2277
+ */
2278
+ declare function getWorkload(request: Request, es?: EnterpriseStandard): Promise<WorkloadIdentity | undefined>;
2279
+ /**
2280
+ * Get an access token for the configured workload identity.
2281
+ *
2282
+ * @param scope - Optional OAuth2 scopes (space-delimited)
2283
+ * @param config - Optional EnterpriseStandard configuration
2284
+ * @returns Access token string
2285
+ *
2286
+ * @example
2287
+ * ```typescript
2288
+ * import { getWorkloadToken } from '@enterprisestandard/react/server';
2289
+ *
2290
+ * // Get token for API calls
2291
+ * const token = await getWorkloadToken('api:read api:write');
2292
+ *
2293
+ * // Use in outbound requests
2294
+ * const response = await fetch('https://api.example.com/data', {
2295
+ * headers: { 'Authorization': `Bearer ${token}` },
2296
+ * });
2297
+ * ```
2298
+ */
2299
+ declare function getWorkloadToken(scope?: string, es?: EnterpriseStandard): Promise<string>;
2300
+ /**
2301
+ * Validate a workload token from an incoming request.
2302
+ *
2303
+ * @param request - Request with Authorization header
2304
+ * @param config - Optional EnterpriseStandard configuration
2305
+ * @returns Token validation result
2306
+ *
2307
+ * @example
2308
+ * ```typescript
2309
+ * import { validateWorkloadToken } from '@enterprisestandard/react/server';
2310
+ *
2311
+ * async function handler(request: Request) {
2312
+ * const result = await validateWorkloadToken(request);
2313
+ *
2314
+ * if (!result.valid) {
2315
+ * return new Response(
2316
+ * JSON.stringify({ error: result.error }),
2317
+ * { status: 401 }
2318
+ * );
2319
+ * }
2320
+ *
2321
+ * const workloadId = result.claims?.iss;
2322
+ * // ... process authenticated request
2323
+ * }
2324
+ * ```
2325
+ */
2326
+ declare function validateWorkloadToken(request: Request, es?: EnterpriseStandard): Promise<TokenValidationResult>;
2327
+ /**
2328
+ * Revoke a workload access token.
2329
+ *
2330
+ * @param token - The access token to revoke
2331
+ * @param config - Optional EnterpriseStandard configuration
2332
+ *
2333
+ * @example
2334
+ * ```typescript
2335
+ * import { revokeWorkloadToken } from '@enterprisestandard/react/server';
2336
+ *
2337
+ * // Revoke token when workload is decommissioned
2338
+ * await revokeWorkloadToken(accessToken);
2339
+ * ```
2340
+ */
2341
+ declare function revokeWorkloadToken(token: string, es?: EnterpriseStandard): Promise<void>;
2342
+ /**
2343
+ * Framework-agnostic handler for workload authentication routes.
2344
+ *
2345
+ * The handler reads configuration (handler URLs, validation) directly from the
2346
+ * EnterpriseStandard instance, so no config parameter is needed.
2347
+ *
2348
+ * @param request - Incoming request
2349
+ * @param config - Optional ESConfig to specify which EnterpriseStandard instance to use
2350
+ * @returns Response
2351
+ *
2352
+ * @example
2353
+ * ```typescript
2354
+ * import { workloadHandler } from '@enterprisestandard/react/server';
2355
+ *
2356
+ * // TanStack Start example
2357
+ * const Route = createFileRoute('/api/workload/$')({
2358
+ * server: {
2359
+ * handlers: ({ createHandlers }) =>
2360
+ * createHandlers({
2361
+ * GET: {
2362
+ * handler: async ({ request }) => {
2363
+ * return workloadHandler(request);
2364
+ * },
2365
+ * },
2366
+ * POST: {
2367
+ * handler: async ({ request }) => {
2368
+ * return workloadHandler(request);
2369
+ * },
2370
+ * },
2371
+ * }),
2372
+ * },
2373
+ * });
2374
+ * ```
2375
+ */
2376
+ declare function workloadHandler(request: Request, es?: EnterpriseStandard): Promise<Response>;
2377
+ import { StandardSchemaV1 as StandardSchemaV18 } from "@standard-schema/spec";
2378
+ /**
2379
+ * Parse and validate a tenant creation request.
2380
+ * Uses the default EnterpriseStandard instance's tenant service.
2381
+ *
2382
+ * @param request - The HTTP request containing the tenant creation data
2383
+ * @returns Validation result with either the parsed request or validation issues
2384
+ * @throws Error if no default EnterpriseStandard instance is available
2385
+ */
2386
+ declare function parseTenantRequest2(request: Request): Promise<StandardSchemaV18.Result<CreateTenantRequest>>;
2387
+ /**
2388
+ * Send a webhook update to ESVS with tenant creation status.
2389
+ * Uses the default EnterpriseStandard instance's tenant service.
2390
+ *
2391
+ * @param webhookUrl - The webhook URL provided in the tenant creation request
2392
+ * @param payload - The webhook payload with status and tenant information
2393
+ * @throws Error if no default EnterpriseStandard instance is available
2394
+ */
2395
+ declare function sendTenantWebhook2(webhookUrl: string, payload: TenantWebhookPayload): Promise<void>;
2396
+ /**
2397
+ * Error thrown when tenant request validation fails
2398
+ */
2399
+ declare class TenantRequestError extends Error {
2400
+ constructor(message: string);
2401
+ }
2402
+ type EnterpriseStandard = {
2403
+ defaultInstance?: boolean;
2404
+ vault?: Vault;
2405
+ sso?: SSO;
2406
+ iam?: IAM;
2407
+ workload?: Workload;
2408
+ tenants?: Tenant;
2409
+ /**
2410
+ * Framework-agnostic request handler that routes requests to the appropriate
2411
+ * standard handler (SSO, IAM, or Workload) based on the configured URLs.
2412
+ */
2413
+ handler: (request: Request) => Promise<Response>;
2414
+ };
2415
+ type ESConfig = {
2416
+ defaultInstance?: boolean;
2417
+ vaultPath?: string;
2418
+ sso?: SSOConfig;
2419
+ iam?: IAMConfig;
2420
+ workload?: WorkloadConfig;
2421
+ tenant?: TenantConfig;
2422
+ validators?: ESValidators;
2423
+ };
2424
+ type ESValidators = {
2425
+ sso: SSOValidators;
2426
+ iam: IAMValidators;
2427
+ workload: WorkloadValidators;
2428
+ tenant: TenantValidators;
2429
+ };
2430
+ 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 };