@enterprisestandard/core 0.0.15-beta.20260420.1 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1321 -1349
- package/dist/index.js +1 -1
- package/dist/server.d.ts +1127 -1241
- package/dist/server.js +1 -1
- package/dist/shared/core-wpy88bze.js +12 -0
- package/package.json +3 -3
- package/dist/shared/core-assxr5dn.js +0 -12
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { version } from "../package.json";
|
|
2
2
|
import { StandardSchemaV1 as StandardSchemaV18 } from "@standard-schema/spec";
|
|
3
|
-
import { StandardSchemaV1 as
|
|
3
|
+
import { StandardSchemaV1 as StandardSchemaV17 } from "@standard-schema/spec";
|
|
4
4
|
/**
|
|
5
5
|
* Minimal logger interface compatible with common patterns (console, pino, winston, etc.)
|
|
6
6
|
*/
|
|
@@ -641,37 +641,8 @@ interface GroupStore<TExtended = Record<string, never>> {
|
|
|
641
641
|
*/
|
|
642
642
|
removeMember(groupId: string, memberId: string): Promise<void>;
|
|
643
643
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
* Extended by User (SSO) and EnterpriseUser (SCIM).
|
|
647
|
-
*/
|
|
648
|
-
interface BaseUser {
|
|
649
|
-
/**
|
|
650
|
-
* Unique identifier for the user
|
|
651
|
-
*/
|
|
652
|
-
id?: string;
|
|
653
|
-
/**
|
|
654
|
-
* REQUIRED. Unique identifier for login
|
|
655
|
-
*/
|
|
656
|
-
userName: string;
|
|
657
|
-
/**
|
|
658
|
-
* REQUIRED. Simple display name
|
|
659
|
-
*/
|
|
660
|
-
name: string;
|
|
661
|
-
/**
|
|
662
|
-
* REQUIRED. Primary email address
|
|
663
|
-
*/
|
|
664
|
-
email: string;
|
|
665
|
-
/**
|
|
666
|
-
* URL to user's avatar/profile picture
|
|
667
|
-
*/
|
|
668
|
-
avatar?: string;
|
|
669
|
-
/**
|
|
670
|
-
* User type (e.g., "employee", "contractor", "customer").
|
|
671
|
-
* Shared by SSO/CIAM and enterprise/SCIM user representations.
|
|
672
|
-
*/
|
|
673
|
-
userType?: string;
|
|
674
|
-
}
|
|
644
|
+
import { StandardSchemaV1 as StandardSchemaV16 } from "@standard-schema/spec";
|
|
645
|
+
import { StandardSchemaV1 as StandardSchemaV13 } from "@standard-schema/spec";
|
|
675
646
|
import { StandardSchemaV1 as StandardSchemaV12 } from "@standard-schema/spec";
|
|
676
647
|
/**
|
|
677
648
|
* OIDC Code Flow Callback URL Parameters
|
|
@@ -758,6 +729,191 @@ interface IdTokenClaims {
|
|
|
758
729
|
*/
|
|
759
730
|
declare function idTokenClaimsSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, IdTokenClaims>;
|
|
760
731
|
/**
|
|
732
|
+
* Session management for tracking user sessions and enabling backchannel logout.
|
|
733
|
+
*
|
|
734
|
+
* Session stores are optional - the package works with JWT cookies alone.
|
|
735
|
+
* Sessions are only required for backchannel logout functionality.
|
|
736
|
+
*
|
|
737
|
+
* ## Session Validation Strategies
|
|
738
|
+
*
|
|
739
|
+
* When using a session store, you can configure when sessions are validated:
|
|
740
|
+
*
|
|
741
|
+
* ### 'always' (default)
|
|
742
|
+
* Validates session on every authenticated request.
|
|
743
|
+
* - **Security**: Maximum - immediate session revocation
|
|
744
|
+
* - **Performance**: InMemory ~0.00005ms, Redis ~1-2ms per request
|
|
745
|
+
* - **Backchannel Logout**: Takes effect immediately
|
|
746
|
+
* - **Use when**: Security is paramount, using InMemory or Redis backend
|
|
747
|
+
*
|
|
748
|
+
* ### 'refresh-only'
|
|
749
|
+
* Validates session only during token refresh (typically every 5-15 minutes).
|
|
750
|
+
* - **Security**: Good - 5-15 minute revocation window
|
|
751
|
+
* - **Performance**: 99% reduction in session lookups
|
|
752
|
+
* - **Backchannel Logout**: Takes effect within token TTL (5-15 min)
|
|
753
|
+
* - **Use when**: Performance is critical AND delayed revocation is acceptable
|
|
754
|
+
* - **WARNING**: Compromised sessions remain valid until next refresh
|
|
755
|
+
*
|
|
756
|
+
* ### 'disabled'
|
|
757
|
+
* Never validates sessions against the store.
|
|
758
|
+
* - **Security**: None - backchannel logout doesn't work
|
|
759
|
+
* - **Performance**: No overhead
|
|
760
|
+
* - **Use when**: Cookie-only mode without session store
|
|
761
|
+
* - **WARNING**: Do not use with sessionStore configured
|
|
762
|
+
*
|
|
763
|
+
* ## Performance Characteristics
|
|
764
|
+
*
|
|
765
|
+
* | Backend | Lookup Time | Impact on Request | Recommendation |
|
|
766
|
+
* |--------------|-------------|-------------------|------------------------|
|
|
767
|
+
* | InMemory | <0.00005ms | Negligible | Use 'always' |
|
|
768
|
+
* | Redis | 1-2ms | 2-4% increase | Use 'always' or test |
|
|
769
|
+
* | Database | 5-20ms | 10-40% increase | Use Redis cache layer |
|
|
770
|
+
*
|
|
771
|
+
* ## Example Usage
|
|
772
|
+
*
|
|
773
|
+
* ```typescript
|
|
774
|
+
* import { sso, InMemorySessionStore } from '@enterprisestandard/server';
|
|
775
|
+
*
|
|
776
|
+
* // Maximum security (default)
|
|
777
|
+
* const secure = sso({
|
|
778
|
+
* // ... other config
|
|
779
|
+
* sessionStore: new InMemorySessionStore(),
|
|
780
|
+
* session_validation: 'always', // Immediate revocation
|
|
781
|
+
* });
|
|
782
|
+
*
|
|
783
|
+
* // High performance
|
|
784
|
+
* const fast = sso({
|
|
785
|
+
* // ... other config
|
|
786
|
+
* sessionStore: new InMemorySessionStore(),
|
|
787
|
+
* session_validation: {
|
|
788
|
+
* strategy: 'refresh-only' // 5-15 min revocation delay
|
|
789
|
+
* }
|
|
790
|
+
* });
|
|
791
|
+
* ```
|
|
792
|
+
*/
|
|
793
|
+
/**
|
|
794
|
+
* Core session data tracked for each authenticated user session.
|
|
795
|
+
*
|
|
796
|
+
* @template TExtended - Type-safe custom data that consumers can add to sessions
|
|
797
|
+
*/
|
|
798
|
+
type Session<TExtended = object> = {
|
|
799
|
+
/**
|
|
800
|
+
* Session ID from the Identity Provider (from `sid` claim in ID token).
|
|
801
|
+
* This is the unique identifier for the session.
|
|
802
|
+
*/
|
|
803
|
+
sid: string;
|
|
804
|
+
/**
|
|
805
|
+
* Subject identifier (user ID) from the Identity Provider.
|
|
806
|
+
* From the `sub` claim in the ID token.
|
|
807
|
+
*/
|
|
808
|
+
sub: string;
|
|
809
|
+
/**
|
|
810
|
+
* Timestamp when the session was created.
|
|
811
|
+
*/
|
|
812
|
+
createdAt: Date;
|
|
813
|
+
/**
|
|
814
|
+
* Timestamp of the last activity in this session.
|
|
815
|
+
* Can be updated to track session activity.
|
|
816
|
+
*/
|
|
817
|
+
lastActivityAt: Date;
|
|
818
|
+
/**
|
|
819
|
+
* Allow consumers to add runtime data to sessions.
|
|
820
|
+
*/
|
|
821
|
+
[key: string]: unknown;
|
|
822
|
+
} & TExtended;
|
|
823
|
+
/**
|
|
824
|
+
* Abstract interface for session storage backends.
|
|
825
|
+
*
|
|
826
|
+
* Consumers can implement this interface to use different storage backends:
|
|
827
|
+
* - Redis
|
|
828
|
+
* - Database (PostgreSQL, MySQL, etc.)
|
|
829
|
+
* - Distributed cache
|
|
830
|
+
* - Custom solutions
|
|
831
|
+
*
|
|
832
|
+
* @template TExtended - Type-safe custom data that consumers can add to sessions
|
|
833
|
+
*
|
|
834
|
+
* @example
|
|
835
|
+
* ```typescript
|
|
836
|
+
* // Custom session data
|
|
837
|
+
* type MySessionData = {
|
|
838
|
+
* ipAddress: string;
|
|
839
|
+
* userAgent: string;
|
|
840
|
+
* };
|
|
841
|
+
*
|
|
842
|
+
* // Implement custom store
|
|
843
|
+
* class RedisSessionStore implements SessionStore<MySessionData> {
|
|
844
|
+
* async create(session: Session<MySessionData>): Promise<void> {
|
|
845
|
+
* await redis.set(`session:${session.sid}`, JSON.stringify(session));
|
|
846
|
+
* }
|
|
847
|
+
* // ... other methods
|
|
848
|
+
* }
|
|
849
|
+
* ```
|
|
850
|
+
*/
|
|
851
|
+
interface SessionStore<TExtended = object> {
|
|
852
|
+
/**
|
|
853
|
+
* Create a new session in the store.
|
|
854
|
+
*
|
|
855
|
+
* @param session - The session data to store
|
|
856
|
+
* @throws Error if session with same sid already exists
|
|
857
|
+
*/
|
|
858
|
+
create(session: Session<TExtended>): Promise<void>;
|
|
859
|
+
/**
|
|
860
|
+
* Retrieve a session by its IdP session ID (sid).
|
|
861
|
+
*
|
|
862
|
+
* @param sid - The session.sid from the Identity Provider
|
|
863
|
+
* @returns The session if found, null otherwise
|
|
864
|
+
*/
|
|
865
|
+
get(sid: string): Promise<Session<TExtended> | null>;
|
|
866
|
+
/**
|
|
867
|
+
* Update an existing session with partial data.
|
|
868
|
+
*
|
|
869
|
+
* Commonly used to update lastActivityAt or add custom fields.
|
|
870
|
+
*
|
|
871
|
+
* @param sid - The session.sid to update
|
|
872
|
+
* @param data - Partial session data to merge
|
|
873
|
+
* @throws Error if session not found
|
|
874
|
+
*/
|
|
875
|
+
update(sid: string, data: Partial<Session<TExtended>>): Promise<void>;
|
|
876
|
+
/**
|
|
877
|
+
* Delete a session by its IdP session ID (sid).
|
|
878
|
+
*
|
|
879
|
+
* Used for both normal logout and backchannel logout flows.
|
|
880
|
+
*
|
|
881
|
+
* @param sid - The session.sid to delete
|
|
882
|
+
*/
|
|
883
|
+
delete(sid: string): Promise<void>;
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Base user with simple, developer-friendly attributes.
|
|
887
|
+
* Extended by User (SSO) and EnterpriseUser (SCIM).
|
|
888
|
+
*/
|
|
889
|
+
interface BaseUser {
|
|
890
|
+
/**
|
|
891
|
+
* Unique identifier for the user
|
|
892
|
+
*/
|
|
893
|
+
id?: string;
|
|
894
|
+
/**
|
|
895
|
+
* REQUIRED. Unique identifier for login
|
|
896
|
+
*/
|
|
897
|
+
userName: string;
|
|
898
|
+
/**
|
|
899
|
+
* REQUIRED. Simple display name
|
|
900
|
+
*/
|
|
901
|
+
name: string;
|
|
902
|
+
/**
|
|
903
|
+
* REQUIRED. Primary email address
|
|
904
|
+
*/
|
|
905
|
+
email: string;
|
|
906
|
+
/**
|
|
907
|
+
* URL to user's avatar/profile picture
|
|
908
|
+
*/
|
|
909
|
+
avatar?: string;
|
|
910
|
+
/**
|
|
911
|
+
* User type (e.g., "employee", "contractor", "customer").
|
|
912
|
+
* Shared by SSO/CIAM and enterprise/SCIM user representations.
|
|
913
|
+
*/
|
|
914
|
+
userType?: string;
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
761
917
|
* Primary user type for SSO/OIDC applications.
|
|
762
918
|
* Extends BaseUser with SSO-specific data.
|
|
763
919
|
*/
|
|
@@ -795,229 +951,111 @@ interface User2 extends BaseUser {
|
|
|
795
951
|
expires: Date;
|
|
796
952
|
};
|
|
797
953
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
954
|
+
type SSOConfig<
|
|
955
|
+
TSessionData = Record<string, never>,
|
|
956
|
+
TUserData = Record<string, never>
|
|
957
|
+
> = {
|
|
958
|
+
authority?: string;
|
|
959
|
+
tokenUrl?: string;
|
|
960
|
+
authorizationUrl?: string;
|
|
961
|
+
clientId?: string;
|
|
962
|
+
clientSecret?: string;
|
|
963
|
+
redirectUri?: string;
|
|
964
|
+
responseType?: "code";
|
|
965
|
+
scope?: string;
|
|
966
|
+
silentRedirectUri?: string;
|
|
967
|
+
jwksUri?: string;
|
|
968
|
+
cookiesPrefix?: string;
|
|
969
|
+
cookiesPath?: string;
|
|
970
|
+
cookiesSecure?: boolean;
|
|
971
|
+
cookiesSameSite?: "Strict" | "Lax";
|
|
809
972
|
/**
|
|
810
|
-
*
|
|
811
|
-
*
|
|
973
|
+
* Optional cookie name for tracking the active session across tenants.
|
|
974
|
+
* Defaults to 'es.active_session' when using the helper utilities.
|
|
812
975
|
*/
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
*/
|
|
818
|
-
tenantId?: string;
|
|
819
|
-
/**
|
|
820
|
-
* Optional external identifier from the provisioning client.
|
|
821
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
822
|
-
*/
|
|
823
|
-
externalId?: string;
|
|
824
|
-
/**
|
|
825
|
-
* Optional SCIM display name distinct from the simple `name` field.
|
|
826
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
827
|
-
*/
|
|
828
|
-
displayName?: string;
|
|
829
|
-
/**
|
|
830
|
-
* Optional structured SCIM name.
|
|
831
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
832
|
-
*/
|
|
833
|
-
scimName?: Name;
|
|
834
|
-
/**
|
|
835
|
-
* Optional SCIM email collection.
|
|
836
|
-
* The simple `email` field still stores the primary email for quick lookup.
|
|
837
|
-
*/
|
|
838
|
-
emails?: Email[];
|
|
839
|
-
/**
|
|
840
|
-
* Optional SCIM nickname.
|
|
841
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
842
|
-
*/
|
|
843
|
-
nickName?: string;
|
|
844
|
-
/**
|
|
845
|
-
* Optional SCIM account state.
|
|
846
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
847
|
-
*/
|
|
848
|
-
active?: boolean;
|
|
849
|
-
/**
|
|
850
|
-
* Optional SCIM job title.
|
|
851
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
852
|
-
*/
|
|
853
|
-
title?: string;
|
|
854
|
-
/**
|
|
855
|
-
* Optional SCIM preferred language.
|
|
856
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
857
|
-
*/
|
|
858
|
-
preferredLanguage?: string;
|
|
859
|
-
/**
|
|
860
|
-
* Optional SCIM locale.
|
|
861
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
862
|
-
*/
|
|
863
|
-
locale?: string;
|
|
864
|
-
/**
|
|
865
|
-
* Optional SCIM timezone.
|
|
866
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
867
|
-
*/
|
|
868
|
-
timezone?: string;
|
|
869
|
-
/**
|
|
870
|
-
* Optional SCIM phone numbers.
|
|
871
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
872
|
-
*/
|
|
873
|
-
phoneNumbers?: PhoneNumber[];
|
|
874
|
-
/**
|
|
875
|
-
* Optional SCIM instant messaging addresses.
|
|
876
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
877
|
-
*/
|
|
878
|
-
ims?: Array<{
|
|
879
|
-
value: string;
|
|
880
|
-
display?: string;
|
|
881
|
-
type?: string;
|
|
882
|
-
primary?: boolean;
|
|
883
|
-
}>;
|
|
884
|
-
/**
|
|
885
|
-
* Optional SCIM photos.
|
|
886
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
887
|
-
*/
|
|
888
|
-
photos?: Photo[];
|
|
889
|
-
/**
|
|
890
|
-
* Optional SCIM addresses.
|
|
891
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
892
|
-
*/
|
|
893
|
-
addresses?: Address[];
|
|
894
|
-
/**
|
|
895
|
-
* Optional SCIM roles.
|
|
896
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
897
|
-
*/
|
|
898
|
-
roles?: Role[];
|
|
899
|
-
/**
|
|
900
|
-
* Optional SCIM groups.
|
|
901
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
902
|
-
*/
|
|
903
|
-
groups?: Group[];
|
|
904
|
-
/**
|
|
905
|
-
* Optional SCIM entitlements.
|
|
906
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
907
|
-
*/
|
|
908
|
-
entitlements?: Array<{
|
|
909
|
-
value: string;
|
|
910
|
-
display?: string;
|
|
911
|
-
type?: string;
|
|
912
|
-
primary?: boolean;
|
|
913
|
-
}>;
|
|
914
|
-
/**
|
|
915
|
-
* Optional SCIM X.509 certificates.
|
|
916
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
917
|
-
*/
|
|
918
|
-
x509Certificates?: X509Certificate[];
|
|
919
|
-
/**
|
|
920
|
-
* Optional SCIM enterprise extension.
|
|
921
|
-
* Mirrors `urn:ietf:params:scim:schemas:extension:enterprise:2.0:User`.
|
|
922
|
-
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
923
|
-
*/
|
|
924
|
-
scimEnterprise?: EnterpriseExtension;
|
|
925
|
-
/**
|
|
926
|
-
* Optional pass-through SCIM schema extensions keyed by full URN.
|
|
927
|
-
* Use this when your validator accepts custom top-level SCIM extensions and
|
|
928
|
-
* you want them to round-trip through a `UserStore` implementation.
|
|
929
|
-
*/
|
|
930
|
-
scimSchemaExtensions?: Record<string, unknown>;
|
|
931
|
-
/**
|
|
932
|
-
* Timestamp when the user was first stored.
|
|
933
|
-
*/
|
|
934
|
-
createdAt: Date;
|
|
935
|
-
/**
|
|
936
|
-
* Timestamp when the user was last updated (e.g., on re-login).
|
|
937
|
-
*/
|
|
938
|
-
updatedAt: Date;
|
|
939
|
-
} & TExtended;
|
|
940
|
-
/**
|
|
941
|
-
* Abstract interface for user storage backends.
|
|
942
|
-
*
|
|
943
|
-
* Consumers can implement this interface to use different storage backends:
|
|
944
|
-
* - In-memory (for development/testing)
|
|
945
|
-
* - Redis (for production with fast lookups)
|
|
946
|
-
* - Database (PostgreSQL, MySQL, etc.)
|
|
947
|
-
*
|
|
948
|
-
* @template TExtended - Type-safe custom data that consumers can add to users
|
|
949
|
-
*
|
|
950
|
-
* @example
|
|
951
|
-
* ```typescript
|
|
952
|
-
* // Custom user data
|
|
953
|
-
* type MyUserData = {
|
|
954
|
-
* department: string;
|
|
955
|
-
* roles: string[];
|
|
956
|
-
* };
|
|
957
|
-
*
|
|
958
|
-
* // Implement custom store
|
|
959
|
-
* class RedisUserStore implements UserStore<MyUserData> {
|
|
960
|
-
* async get(sub: string): Promise<StoredUser<MyUserData> | null> {
|
|
961
|
-
* const data = await redis.get(`user:${sub}`);
|
|
962
|
-
* return data ? JSON.parse(data) : null;
|
|
963
|
-
* }
|
|
964
|
-
* // ... other methods
|
|
965
|
-
* }
|
|
966
|
-
* ```
|
|
967
|
-
*/
|
|
968
|
-
interface UserStore<TExtended = object> {
|
|
969
|
-
/**
|
|
970
|
-
* Retrieve a user by their subject identifier (sub).
|
|
971
|
-
*
|
|
972
|
-
* @param sub - The user's unique identifier from the IdP
|
|
973
|
-
* @returns The user if found, null otherwise
|
|
974
|
-
*/
|
|
975
|
-
get(sub: string): Promise<StoredUser<TExtended> | null>;
|
|
976
|
-
/**
|
|
977
|
-
* Retrieve a user by their email address.
|
|
978
|
-
*
|
|
979
|
-
* @param email - The user's email address
|
|
980
|
-
* @returns The user if found, null otherwise
|
|
981
|
-
*/
|
|
982
|
-
getByEmail(email: string): Promise<StoredUser<TExtended> | null>;
|
|
983
|
-
/**
|
|
984
|
-
* Retrieve a user by their username.
|
|
985
|
-
*
|
|
986
|
-
* @param userName - The user's username
|
|
987
|
-
* @returns The user if found, null otherwise
|
|
988
|
-
*/
|
|
989
|
-
getByUserName(userName: string): Promise<StoredUser<TExtended> | null>;
|
|
990
|
-
/**
|
|
991
|
-
* Create or update a user in the store.
|
|
992
|
-
*
|
|
993
|
-
* If a user with the same `id` (sub) exists, it will be updated.
|
|
994
|
-
* Otherwise, a new user will be created.
|
|
995
|
-
*
|
|
996
|
-
* @param user - The user data to store
|
|
997
|
-
*/
|
|
998
|
-
upsert(user: StoredUser<TExtended>): Promise<void>;
|
|
976
|
+
activeSessionCookieName?: string;
|
|
977
|
+
endSessionEndpoint?: string;
|
|
978
|
+
revocationEndpoint?: string;
|
|
979
|
+
sessionStore?: SessionStore<TSessionData>;
|
|
999
980
|
/**
|
|
1000
|
-
*
|
|
1001
|
-
*
|
|
1002
|
-
* @param sub - The user's unique identifier to delete
|
|
981
|
+
* Optional user store for persisting user profiles from SSO authentication.
|
|
982
|
+
* When configured, users are automatically stored/updated on each login.
|
|
1003
983
|
*/
|
|
1004
|
-
|
|
984
|
+
userStore?: UserStore<TUserData>;
|
|
1005
985
|
/**
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
986
|
+
* Enable Just-In-Time (JIT) user provisioning.
|
|
987
|
+
* When enabled, new users are automatically created in the userStore on their first login.
|
|
988
|
+
* When disabled (default), only existing users in the userStore are updated on login.
|
|
989
|
+
* Requires userStore to be configured.
|
|
990
|
+
* @default false
|
|
1010
991
|
*/
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
992
|
+
enableJitUserProvisioning?: boolean;
|
|
993
|
+
validators?: SSOValidators;
|
|
994
|
+
} & SSOHandlerConfig;
|
|
995
|
+
type StateCookie = {
|
|
996
|
+
state: string;
|
|
997
|
+
codeVerifier: string;
|
|
998
|
+
landingUrl: string;
|
|
999
|
+
errorUrl?: string;
|
|
1000
|
+
};
|
|
1001
|
+
type CookieOptions = {
|
|
1002
|
+
cookieName?: string;
|
|
1003
|
+
path?: string;
|
|
1004
|
+
maxAge?: number;
|
|
1005
|
+
secure?: boolean;
|
|
1006
|
+
sameSite?: "Strict" | "Lax";
|
|
1007
|
+
};
|
|
1008
|
+
declare function getActiveSession(request: Request, options?: CookieOptions): string | undefined;
|
|
1009
|
+
declare function setActiveSession(clientId: string, options?: CookieOptions): string;
|
|
1010
|
+
declare function clearActiveSession(options?: CookieOptions): string;
|
|
1011
|
+
declare function listSsoClientIdsFromCookies(requestOrCookieHeader: Request | string | null | undefined, options?: {
|
|
1012
|
+
cookiePrefix?: string;
|
|
1013
|
+
}): string[];
|
|
1014
|
+
declare function findTenantFromStateParam(cookieHeader: string | null | undefined, stateParam: string): {
|
|
1015
|
+
clientId: string;
|
|
1016
|
+
stateCookie: StateCookie;
|
|
1017
|
+
} | undefined;
|
|
1018
|
+
type LoginConfig = {
|
|
1019
|
+
landingUrl: string;
|
|
1020
|
+
errorUrl?: string;
|
|
1021
|
+
};
|
|
1022
|
+
type SSOHandlerConfig = {
|
|
1023
|
+
loginUrl?: string;
|
|
1024
|
+
userUrl?: string;
|
|
1025
|
+
errorUrl?: string;
|
|
1026
|
+
landingUrl?: string;
|
|
1027
|
+
tokenUrl?: string;
|
|
1028
|
+
refreshUrl?: string;
|
|
1029
|
+
jwksUrl?: string;
|
|
1030
|
+
logoutUrl?: string;
|
|
1031
|
+
logoutBackChannelUrl?: string;
|
|
1032
|
+
};
|
|
1033
|
+
type SSOValidators = {
|
|
1034
|
+
callbackParams: StandardSchemaV13<unknown, OidcCallbackParams>;
|
|
1035
|
+
idTokenClaims: StandardSchemaV13<unknown, IdTokenClaims>;
|
|
1036
|
+
tokenResponse: StandardSchemaV13<unknown, TokenResponse>;
|
|
1037
|
+
};
|
|
1038
|
+
type SSO<
|
|
1039
|
+
TSessionData = Record<string, never>,
|
|
1040
|
+
TUserData = Record<string, never>
|
|
1041
|
+
> = SSOConfig<TSessionData, TUserData> & {
|
|
1042
|
+
getUser: (request: Request) => Promise<User2 | undefined>;
|
|
1043
|
+
getRequiredUser: (request: Request) => Promise<User2>;
|
|
1044
|
+
getJwt: (request: Request) => Promise<string | undefined>;
|
|
1045
|
+
initiateLogin: (config: LoginConfig, requestUrl?: string) => Promise<Response>;
|
|
1046
|
+
logout: (request: Request, config?: LoginConfig) => Promise<Response>;
|
|
1047
|
+
logoutBackChannel2: (request: Request) => Promise<Response>;
|
|
1048
|
+
callbackHandler: (request: Request) => Promise<Response>;
|
|
1049
|
+
handler: (request: Request) => Promise<Response>;
|
|
1050
|
+
};
|
|
1051
|
+
import { StandardSchemaV1 as StandardSchemaV15 } from "@standard-schema/spec";
|
|
1014
1052
|
type ChangeListener = () => void;
|
|
1015
1053
|
type ReactiveHandle = {
|
|
1016
1054
|
beforeChange?(listener: ChangeListener): () => void;
|
|
1017
1055
|
afterChange?(listener: ChangeListener): () => void;
|
|
1018
1056
|
isAvailable?(): boolean;
|
|
1019
1057
|
};
|
|
1020
|
-
import { StandardSchemaV1 as
|
|
1058
|
+
import { StandardSchemaV1 as StandardSchemaV14 } from "@standard-schema/spec";
|
|
1021
1059
|
/**
|
|
1022
1060
|
* JWT Assertion Claims for OAuth2 JWT Bearer Grant (RFC 7523) and OAuth2 Access Tokens
|
|
1023
1061
|
* @see https://datatracker.ietf.org/doc/html/rfc7523
|
|
@@ -1064,7 +1102,7 @@ interface JWTAssertionClaims {
|
|
|
1064
1102
|
* @param vendor - The name of the vendor creating this schema
|
|
1065
1103
|
* @returns A StandardSchemaV1 instance for JWT Assertion Claims validation
|
|
1066
1104
|
*/
|
|
1067
|
-
declare function jwtAssertionClaimsSchema(vendor: string):
|
|
1105
|
+
declare function jwtAssertionClaimsSchema(vendor: string): StandardSchemaV14<Record<string, unknown>, JWTAssertionClaims>;
|
|
1068
1106
|
/**
|
|
1069
1107
|
* Workload Token Response from OAuth2 token endpoint
|
|
1070
1108
|
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
|
|
@@ -1100,7 +1138,7 @@ interface WorkloadTokenResponse {
|
|
|
1100
1138
|
* @param vendor - The name of the vendor creating this schema
|
|
1101
1139
|
* @returns A StandardSchemaV1 instance for Workload Token Response validation
|
|
1102
1140
|
*/
|
|
1103
|
-
declare function workloadTokenResponseSchema(vendor: string):
|
|
1141
|
+
declare function workloadTokenResponseSchema(vendor: string): StandardSchemaV14<Record<string, unknown>, WorkloadTokenResponse>;
|
|
1104
1142
|
/**
|
|
1105
1143
|
* Token Validation Result
|
|
1106
1144
|
*/
|
|
@@ -1347,8 +1385,8 @@ type WorkloadConfigBase = {
|
|
|
1347
1385
|
validators?: WorkloadValidators;
|
|
1348
1386
|
};
|
|
1349
1387
|
type WorkloadValidators = {
|
|
1350
|
-
jwtAssertionClaims:
|
|
1351
|
-
tokenResponse:
|
|
1388
|
+
jwtAssertionClaims: StandardSchemaV15<unknown, JWTAssertionClaims>;
|
|
1389
|
+
tokenResponse: StandardSchemaV15<unknown, WorkloadTokenResponse>;
|
|
1352
1390
|
};
|
|
1353
1391
|
/**
|
|
1354
1392
|
* JWT Bearer Grant (RFC 7523) Configuration
|
|
@@ -1528,951 +1566,545 @@ type Workload = WorkloadConfig & ReactiveHandle & {
|
|
|
1528
1566
|
};
|
|
1529
1567
|
type WorkloadClient = Pick<Workload, "getToken" | "refreshToken" | "generateJWTAssertion" | "revokeToken" | "beforeChange" | "afterChange" | "isAvailable">;
|
|
1530
1568
|
/**
|
|
1531
|
-
*
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
schemas: string[];
|
|
1535
|
-
status: string;
|
|
1536
|
-
scimType?: string;
|
|
1537
|
-
detail?: string;
|
|
1538
|
-
}
|
|
1539
|
-
/**
|
|
1540
|
-
* SCIM List Response for bulk operations
|
|
1541
|
-
*/
|
|
1542
|
-
interface ScimListResponse<T> {
|
|
1543
|
-
schemas: string[];
|
|
1544
|
-
totalResults: number;
|
|
1545
|
-
startIndex?: number;
|
|
1546
|
-
itemsPerPage?: number;
|
|
1547
|
-
Resources: T[];
|
|
1548
|
-
}
|
|
1549
|
-
/**
|
|
1550
|
-
* Result of a SCIM operation
|
|
1551
|
-
*/
|
|
1552
|
-
interface ScimResult<T> {
|
|
1553
|
-
success: boolean;
|
|
1554
|
-
data?: T;
|
|
1555
|
-
error?: ScimError;
|
|
1556
|
-
status: number;
|
|
1557
|
-
}
|
|
1558
|
-
/**
|
|
1559
|
-
* Handler configuration for IAM
|
|
1569
|
+
* Magic link data stored in the store.
|
|
1570
|
+
*
|
|
1571
|
+
* @template TExtended - Type-safe custom data that consumers can add to magic links
|
|
1560
1572
|
*/
|
|
1561
|
-
|
|
1573
|
+
type MagicLink<TExtended = object> = {
|
|
1562
1574
|
/**
|
|
1563
|
-
*
|
|
1575
|
+
* The magic link token (unique identifier)
|
|
1564
1576
|
*/
|
|
1565
|
-
|
|
1577
|
+
token: string;
|
|
1566
1578
|
/**
|
|
1567
|
-
*
|
|
1579
|
+
* User information associated with this magic link
|
|
1568
1580
|
*/
|
|
1569
|
-
|
|
1581
|
+
user: BaseUser;
|
|
1570
1582
|
/**
|
|
1571
|
-
*
|
|
1583
|
+
* Timestamp when the magic link was created
|
|
1572
1584
|
*/
|
|
1573
|
-
|
|
1574
|
-
|
|
1585
|
+
createdAt: Date;
|
|
1586
|
+
/**
|
|
1587
|
+
* Timestamp when the magic link expires
|
|
1588
|
+
*/
|
|
1589
|
+
expiresAt: Date;
|
|
1590
|
+
/**
|
|
1591
|
+
* Allow consumers to add runtime data to magic links
|
|
1592
|
+
*/
|
|
1593
|
+
[key: string]: unknown;
|
|
1594
|
+
} & TExtended;
|
|
1575
1595
|
/**
|
|
1576
|
-
*
|
|
1596
|
+
* Abstract interface for magic link storage backends.
|
|
1577
1597
|
*
|
|
1578
|
-
*
|
|
1579
|
-
* -
|
|
1580
|
-
* -
|
|
1598
|
+
* Consumers can implement this interface to use different storage backends:
|
|
1599
|
+
* - In-memory (for development/testing)
|
|
1600
|
+
* - Redis (for production with fast lookups and automatic expiration)
|
|
1601
|
+
* - Database (PostgreSQL, MySQL, etc.)
|
|
1602
|
+
*
|
|
1603
|
+
* @template TExtended - Type-safe custom data that consumers can add to magic links
|
|
1604
|
+
*
|
|
1605
|
+
* @example
|
|
1606
|
+
* ```typescript
|
|
1607
|
+
* // Custom magic link data
|
|
1608
|
+
* type MyMagicLinkData = {
|
|
1609
|
+
* source: string;
|
|
1610
|
+
* metadata: Record<string, unknown>;
|
|
1611
|
+
* };
|
|
1612
|
+
*
|
|
1613
|
+
* // Implement custom store
|
|
1614
|
+
* class RedisMagicLinkStore implements MagicLinkStore<MyMagicLinkData> {
|
|
1615
|
+
* async create(token: string, user: BaseUser, expiresAt: Date): Promise<void> {
|
|
1616
|
+
* const magicLink: MagicLink<MyMagicLinkData> = {
|
|
1617
|
+
* token,
|
|
1618
|
+
* user,
|
|
1619
|
+
* createdAt: new Date(),
|
|
1620
|
+
* expiresAt,
|
|
1621
|
+
* source: 'api',
|
|
1622
|
+
* metadata: {},
|
|
1623
|
+
* };
|
|
1624
|
+
* const ttl = Math.floor((expiresAt.getTime() - Date.now()) / 1000);
|
|
1625
|
+
* await redis.setex(`magic-link:${token}`, ttl, JSON.stringify(magicLink));
|
|
1626
|
+
* }
|
|
1627
|
+
* // ... other methods
|
|
1628
|
+
* }
|
|
1629
|
+
* ```
|
|
1581
1630
|
*/
|
|
1582
|
-
|
|
1631
|
+
interface MagicLinkStore<TExtended = object> {
|
|
1583
1632
|
/**
|
|
1584
|
-
*
|
|
1585
|
-
*
|
|
1633
|
+
* Create a new magic link in the store.
|
|
1634
|
+
*
|
|
1635
|
+
* @param token - The magic link token (unique identifier)
|
|
1636
|
+
* @param user - The user information to associate with this magic link
|
|
1637
|
+
* @param expiresAt - When the magic link expires
|
|
1638
|
+
* @throws Error if magic link with same token already exists
|
|
1586
1639
|
*/
|
|
1587
|
-
|
|
1640
|
+
create(token: string, user: BaseUser, expiresAt: Date): Promise<void>;
|
|
1588
1641
|
/**
|
|
1589
|
-
*
|
|
1590
|
-
*
|
|
1642
|
+
* Retrieve a magic link by its token.
|
|
1643
|
+
*
|
|
1644
|
+
* @param token - The magic link token
|
|
1645
|
+
* @returns The magic link if found and not expired, null otherwise
|
|
1591
1646
|
*/
|
|
1592
|
-
|
|
1647
|
+
get(token: string): Promise<MagicLink<TExtended> | null>;
|
|
1593
1648
|
/**
|
|
1594
|
-
*
|
|
1595
|
-
*
|
|
1596
|
-
*
|
|
1649
|
+
* Delete a magic link by its token.
|
|
1650
|
+
*
|
|
1651
|
+
* Used after a magic link has been consumed (one-time use).
|
|
1652
|
+
*
|
|
1653
|
+
* @param token - The magic link token to delete
|
|
1597
1654
|
*/
|
|
1598
|
-
|
|
1655
|
+
delete(token: string): Promise<void>;
|
|
1656
|
+
}
|
|
1657
|
+
type Secret<T> = {
|
|
1658
|
+
data: T;
|
|
1659
|
+
metadata: MetaData;
|
|
1660
|
+
};
|
|
1661
|
+
type MetaData = {
|
|
1662
|
+
path: string;
|
|
1663
|
+
version: number;
|
|
1664
|
+
created: Date;
|
|
1665
|
+
};
|
|
1666
|
+
type SecretsSourceType = "vault" | "azure" | "aws" | "gcp" | "dev";
|
|
1667
|
+
type SecretRequestSeverity = "low" | "medium" | "high" | "critical";
|
|
1668
|
+
type SecretLifecycleRequest = {
|
|
1669
|
+
reason?: string;
|
|
1670
|
+
severity?: SecretRequestSeverity;
|
|
1671
|
+
incidentRef?: string;
|
|
1672
|
+
};
|
|
1673
|
+
type SecretsOperationOptions = {
|
|
1674
|
+
/** Optional timeout in milliseconds for this secrets operation. */
|
|
1675
|
+
timeout?: number;
|
|
1676
|
+
};
|
|
1677
|
+
type SecretsSource = ReactiveHandle & {
|
|
1678
|
+
type: SecretsSourceType;
|
|
1679
|
+
getFullSecret: <T>(path: string, options?: SecretsOperationOptions) => Promise<Secret<T>>;
|
|
1680
|
+
getSecret: <T>(path: string, options?: SecretsOperationOptions) => Promise<T>;
|
|
1599
1681
|
/**
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
1682
|
+
* Write a secret at the given path.
|
|
1683
|
+
* Implementations may throw for read-only or unsupported secret sources.
|
|
1602
1684
|
*/
|
|
1603
|
-
|
|
1685
|
+
putSecret: (path: string, value: Record<string, unknown>, options?: SecretsOperationOptions) => Promise<void>;
|
|
1604
1686
|
/**
|
|
1605
|
-
*
|
|
1606
|
-
*
|
|
1687
|
+
* When set, the secret source supports change detection and will call onChange when the secret changes.
|
|
1688
|
+
* Implementations must only invoke onChange when the secret version has changed from the last time
|
|
1689
|
+
* that subscription was notified (same version must not trigger a second call).
|
|
1690
|
+
* @returns a unsubscribe/cleanup function.
|
|
1607
1691
|
*/
|
|
1608
|
-
|
|
1609
|
-
groupsUrl?: string;
|
|
1610
|
-
discovery?: IAMDiscoveryConfig;
|
|
1611
|
-
};
|
|
1612
|
-
type IAMValidators = {
|
|
1613
|
-
user: StandardSchemaV15<unknown, User>;
|
|
1614
|
-
group: StandardSchemaV15<unknown, GroupResource>;
|
|
1615
|
-
};
|
|
1616
|
-
interface IAMInboundUserContext {
|
|
1692
|
+
subscribe<T>(path: string, onChange: (fullSecret: Secret<T>) => void): () => void;
|
|
1617
1693
|
/**
|
|
1618
|
-
*
|
|
1694
|
+
* Deletes a secret at the given path.
|
|
1619
1695
|
*/
|
|
1620
|
-
|
|
1696
|
+
deleteSecret(path: string, options?: SecretsOperationOptions): Promise<void>;
|
|
1621
1697
|
/**
|
|
1622
|
-
*
|
|
1698
|
+
* Lists child paths under the given base path.
|
|
1623
1699
|
*/
|
|
1624
|
-
|
|
1625
|
-
}
|
|
1626
|
-
interface IAMInboundUsersConfig {
|
|
1700
|
+
listPaths(path: string, options?: SecretsOperationOptions): Promise<string[]>;
|
|
1627
1701
|
/**
|
|
1628
|
-
*
|
|
1702
|
+
* Returns true when a secret path exists.
|
|
1629
1703
|
*/
|
|
1630
|
-
|
|
1704
|
+
exists(path: string, options?: SecretsOperationOptions): Promise<boolean>;
|
|
1631
1705
|
/**
|
|
1632
|
-
*
|
|
1706
|
+
* Requests secret rotation for the given path.
|
|
1633
1707
|
*/
|
|
1634
|
-
|
|
1635
|
-
}
|
|
1636
|
-
interface ScimAuthenticationScheme {
|
|
1637
|
-
type: string;
|
|
1638
|
-
name: string;
|
|
1639
|
-
description?: string;
|
|
1640
|
-
specUri?: string;
|
|
1641
|
-
documentationUri?: string;
|
|
1642
|
-
primary?: boolean;
|
|
1643
|
-
}
|
|
1644
|
-
interface ScimServiceProviderConfig {
|
|
1645
|
-
schemas: string[];
|
|
1646
|
-
documentationUri?: string;
|
|
1647
|
-
patch: {
|
|
1648
|
-
supported: boolean;
|
|
1649
|
-
};
|
|
1650
|
-
bulk: {
|
|
1651
|
-
supported: boolean;
|
|
1652
|
-
maxOperations?: number;
|
|
1653
|
-
maxPayloadSize?: number;
|
|
1654
|
-
};
|
|
1655
|
-
filter: {
|
|
1656
|
-
supported: boolean;
|
|
1657
|
-
maxResults?: number;
|
|
1658
|
-
};
|
|
1659
|
-
changePassword: {
|
|
1660
|
-
supported: boolean;
|
|
1661
|
-
};
|
|
1662
|
-
sort: {
|
|
1663
|
-
supported: boolean;
|
|
1664
|
-
};
|
|
1665
|
-
etag: {
|
|
1666
|
-
supported: boolean;
|
|
1667
|
-
};
|
|
1668
|
-
authenticationSchemes?: ScimAuthenticationScheme[];
|
|
1669
|
-
meta?: {
|
|
1670
|
-
resourceType?: string;
|
|
1671
|
-
location?: string;
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
|
-
interface ScimResourceTypeSchemaExtension {
|
|
1675
|
-
schema: string;
|
|
1676
|
-
required: boolean;
|
|
1677
|
-
}
|
|
1678
|
-
interface ScimResourceType {
|
|
1679
|
-
schemas: string[];
|
|
1680
|
-
id: string;
|
|
1681
|
-
name: string;
|
|
1682
|
-
description?: string;
|
|
1683
|
-
endpoint: string;
|
|
1684
|
-
schema: string;
|
|
1685
|
-
schemaExtensions?: ScimResourceTypeSchemaExtension[];
|
|
1686
|
-
meta?: {
|
|
1687
|
-
resourceType?: string;
|
|
1688
|
-
location?: string;
|
|
1689
|
-
};
|
|
1690
|
-
}
|
|
1691
|
-
interface ScimSchemaAttributeDefinition {
|
|
1692
|
-
name: string;
|
|
1693
|
-
type: "string" | "boolean" | "complex" | "reference" | "dateTime";
|
|
1694
|
-
multiValued: boolean;
|
|
1695
|
-
description?: string;
|
|
1696
|
-
required?: boolean;
|
|
1697
|
-
caseExact?: boolean;
|
|
1698
|
-
mutability?: "readOnly" | "readWrite" | "immutable" | "writeOnly";
|
|
1699
|
-
returned?: "always" | "never" | "default" | "request";
|
|
1700
|
-
uniqueness?: "none" | "server" | "global";
|
|
1701
|
-
referenceTypes?: string[];
|
|
1702
|
-
subAttributes?: ScimSchemaAttributeDefinition[];
|
|
1703
|
-
}
|
|
1704
|
-
interface ScimSchemaDefinition {
|
|
1705
|
-
schemas: string[];
|
|
1706
|
-
id: string;
|
|
1707
|
-
name: string;
|
|
1708
|
-
description?: string;
|
|
1709
|
-
attributes: ScimSchemaAttributeDefinition[];
|
|
1710
|
-
meta?: {
|
|
1711
|
-
resourceType?: string;
|
|
1712
|
-
location?: string;
|
|
1713
|
-
};
|
|
1714
|
-
}
|
|
1715
|
-
interface IAMDiscoveryContext {
|
|
1716
|
-
request: Request;
|
|
1717
|
-
basePath: string;
|
|
1718
|
-
usersUrl: string;
|
|
1719
|
-
groupsUrl: string;
|
|
1720
|
-
supportsUsers: boolean;
|
|
1721
|
-
supportsGroups: boolean;
|
|
1722
|
-
}
|
|
1723
|
-
interface IAMDiscoveryConfig {
|
|
1724
|
-
/**
|
|
1725
|
-
* Public IAM base path used for SCIM discovery. When omitted, the SDK derives
|
|
1726
|
-
* it from `usersUrl` or `groupsUrl`.
|
|
1727
|
-
*/
|
|
1728
|
-
basePath?: string;
|
|
1729
|
-
/**
|
|
1730
|
-
* Optional documentation URI advertised in ServiceProviderConfig.
|
|
1731
|
-
*/
|
|
1732
|
-
documentationUri?: string;
|
|
1708
|
+
requestRotate(path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
1733
1709
|
/**
|
|
1734
|
-
*
|
|
1710
|
+
* Requests secret revocation for the given path.
|
|
1735
1711
|
*/
|
|
1736
|
-
|
|
1712
|
+
requestRevoke(path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
1737
1713
|
/**
|
|
1738
|
-
*
|
|
1714
|
+
* Reads metadata for the given path.
|
|
1739
1715
|
*/
|
|
1740
|
-
|
|
1716
|
+
getMetadata(path: string, options?: SecretsOperationOptions): Promise<Record<string, unknown>>;
|
|
1717
|
+
};
|
|
1718
|
+
type SecretsValidators = {
|
|
1741
1719
|
/**
|
|
1742
|
-
*
|
|
1720
|
+
* Optional hook to validate merged source configs before they are resolved.
|
|
1721
|
+
* Throw from this callback to reject invalid secrets source configuration.
|
|
1743
1722
|
*/
|
|
1744
|
-
|
|
1745
|
-
}
|
|
1723
|
+
validateSourceConfig?(sourceName: string, config: SecretsSourceConfig): void;
|
|
1724
|
+
};
|
|
1725
|
+
type Secrets = ReactiveHandle & {
|
|
1726
|
+
/** Named secrets sources client configurations from RemoteConfig. */
|
|
1727
|
+
config: SecretsSourceMap;
|
|
1728
|
+
/** Returns configured secrets source names/keys. */
|
|
1729
|
+
listSecretsSources(): string[];
|
|
1730
|
+
/** Gets a named secrets source client. Throws when missing. */
|
|
1731
|
+
getSecretsSource(sourceName: string): SecretsSource;
|
|
1732
|
+
/** Reads a secret from a named secrets source client. */
|
|
1733
|
+
getSecret<T>(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<T>;
|
|
1734
|
+
/** Reads full secret data + metadata from a named secrets source client. */
|
|
1735
|
+
getFullSecret<T>(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<Secret<T>>;
|
|
1736
|
+
/** Writes a secret to a named secrets source client. */
|
|
1737
|
+
putSecret(sourceName: string, path: string, value: Record<string, unknown>, options?: SecretsOperationOptions): Promise<void>;
|
|
1738
|
+
/** Deletes a secret from a named secrets source client. */
|
|
1739
|
+
deleteSecret(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<void>;
|
|
1740
|
+
/** Lists child paths under a base path for a named secrets source client. */
|
|
1741
|
+
listPaths(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<string[]>;
|
|
1742
|
+
/** Returns true when a path exists for a named secrets source client. */
|
|
1743
|
+
exists(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<boolean>;
|
|
1744
|
+
/** Requests rotation for a secret path in a named secrets source client. */
|
|
1745
|
+
requestRotate(sourceName: string, path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
1746
|
+
/** Requests revocation for a secret path in a named secrets source client. */
|
|
1747
|
+
requestRevoke(sourceName: string, path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
1748
|
+
/** Reads metadata for a secret path in a named secrets source client. */
|
|
1749
|
+
getMetadata(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<Record<string, unknown>>;
|
|
1750
|
+
/** Subscribes to secret changes on a named secrets source client. */
|
|
1751
|
+
subscribe<T>(sourceName: string, path: string, onChange: (fullSecret: Secret<T>) => void): () => void;
|
|
1752
|
+
/** Returns true when request matches any configured LFV delivery path. */
|
|
1753
|
+
isLfvDeliveryRequest?(request: Request): boolean;
|
|
1754
|
+
/** Returns true when request matches any configured LFV events path. */
|
|
1755
|
+
isLfvEventsRequest?(request: Request): boolean;
|
|
1756
|
+
/** Handles LFV delivery callbacks for configured LFV sources. */
|
|
1757
|
+
handleLfvDelivery?(request: Request): Promise<Response>;
|
|
1758
|
+
/** Handles LFV events callbacks for configured LFV sources. */
|
|
1759
|
+
handleLfvEvents?(request: Request): Promise<Response>;
|
|
1760
|
+
};
|
|
1746
1761
|
/**
|
|
1747
|
-
*
|
|
1762
|
+
* Partial secrets source config used in framework/app code to declare expected source names.
|
|
1763
|
+
* ConfigSource-backed values may still provide the actual source details at runtime.
|
|
1748
1764
|
*/
|
|
1749
|
-
|
|
1750
|
-
/**
|
|
1751
|
-
* External identifier for the user
|
|
1752
|
-
*/
|
|
1753
|
-
externalId?: string;
|
|
1754
|
-
}
|
|
1765
|
+
type FrameworkSecretsSourceConfig = Partial<SecretsSourceConfig>;
|
|
1755
1766
|
/**
|
|
1756
|
-
*
|
|
1767
|
+
* Framework-level named secrets source declarations keyed by source name.
|
|
1768
|
+
* Values may be partial or empty when the app only wants to declare expected names/types.
|
|
1757
1769
|
*/
|
|
1758
|
-
|
|
1759
|
-
/**
|
|
1760
|
-
* External identifier for the group
|
|
1761
|
-
*/
|
|
1762
|
-
externalId?: string;
|
|
1763
|
-
/**
|
|
1764
|
-
* Initial members to add to the group
|
|
1765
|
-
*/
|
|
1766
|
-
members?: GroupMember[];
|
|
1767
|
-
}
|
|
1770
|
+
type FrameworkSecretsModuleConfig = Record<string, FrameworkSecretsSourceConfig>;
|
|
1768
1771
|
/**
|
|
1769
|
-
*
|
|
1772
|
+
* TODO: Let's see if we can do some clean inference and remove this!!!
|
|
1770
1773
|
*/
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
* Base path for the SCIM Groups endpoints (e.g., '/api/iam/Groups')
|
|
1774
|
-
*/
|
|
1775
|
-
basePath?: string;
|
|
1776
|
-
}
|
|
1774
|
+
type SecretsSourceMap = Record<string, SecretsSource>;
|
|
1775
|
+
type SecretsSourceConfig = DevSecretsConfig | GcpSecretsConfig | VaultSecretsConfig | AwsSecretsConfig | AzureSecretsConfig;
|
|
1777
1776
|
/**
|
|
1778
|
-
*
|
|
1777
|
+
* Raw module config keyed by source name.
|
|
1778
|
+
* The secrets module resolves this into a runtime SecretsSourceMap.
|
|
1779
1779
|
*/
|
|
1780
|
-
|
|
1780
|
+
type SecretsModuleConfig = Record<string, SecretsSourceConfig>;
|
|
1781
|
+
type DevSecretsConfig = {
|
|
1782
|
+
type: "dev";
|
|
1783
|
+
ioniteUrl: string;
|
|
1784
|
+
};
|
|
1785
|
+
type GcpSecretsConfig = {
|
|
1786
|
+
type: "gcp";
|
|
1787
|
+
};
|
|
1788
|
+
type VaultLfvSecretsConfig = {
|
|
1789
|
+
/** LFV server base URL for OTP/action endpoints. */
|
|
1790
|
+
lfvServerUrl?: string;
|
|
1791
|
+
/** LFV client id used for OTP issuance. */
|
|
1792
|
+
clientId?: string;
|
|
1793
|
+
/** Signature value for X-LFV-Signature header. */
|
|
1794
|
+
signature?: string;
|
|
1795
|
+
deliveryEndpoint: string;
|
|
1781
1796
|
/**
|
|
1782
|
-
*
|
|
1797
|
+
* Optional LFV signature verification key.
|
|
1798
|
+
* When omitted, LFV callbacks are accepted without signature verification.
|
|
1783
1799
|
*/
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1800
|
+
verifyPublicKey?: string;
|
|
1801
|
+
eventsEndpoint: string;
|
|
1802
|
+
path?: string;
|
|
1787
1803
|
/**
|
|
1788
|
-
*
|
|
1804
|
+
* Timeout in milliseconds when waiting for a LFV delivery payload after a read request.
|
|
1805
|
+
* After this duration, an error is thrown.
|
|
1789
1806
|
*/
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
*/
|
|
1796
|
-
type IAMGroupsOutbound = {
|
|
1807
|
+
deliveryTimeout?: number;
|
|
1808
|
+
/** Retry interval in milliseconds between LFV transport retry attempts. */
|
|
1809
|
+
retryInterval?: number;
|
|
1810
|
+
/** Warning interval in milliseconds for LFV retry logs. Set to 0 to disable warnings. */
|
|
1811
|
+
warnInterval?: number;
|
|
1797
1812
|
/**
|
|
1798
|
-
*
|
|
1799
|
-
*
|
|
1800
|
-
* @param options - Optional configuration for the group creation
|
|
1801
|
-
* @returns The created group resource from the provider
|
|
1813
|
+
* Optional logger for request/response tracing. Use `debugLogger` from `@enterprisestandard/core`
|
|
1814
|
+
* to get debug output with request_id for LFV operations.
|
|
1802
1815
|
*/
|
|
1803
|
-
|
|
1816
|
+
log?: Logger;
|
|
1804
1817
|
};
|
|
1805
1818
|
/**
|
|
1806
|
-
*
|
|
1807
|
-
*
|
|
1819
|
+
* Runtime-ready LFV source config.
|
|
1820
|
+
* Input config can be partially declared/merged, but LFV operations require these fields.
|
|
1808
1821
|
*/
|
|
1809
|
-
type
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
*/
|
|
1814
|
-
handler: (request: Request, config?: GroupsInboundHandlerConfig) => Promise<Response>;
|
|
1822
|
+
type ResolvedVaultLfvSecretsConfig = Omit<VaultLfvSecretsConfig, "lfvServerUrl" | "clientId" | "path"> & {
|
|
1823
|
+
lfvServerUrl: string;
|
|
1824
|
+
clientId: string;
|
|
1825
|
+
path: string;
|
|
1815
1826
|
};
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1827
|
+
type VaultWebSocketAuthHeader = "X-Vault-Token" | "Authorization";
|
|
1828
|
+
type VaultWebSocketSecretsConfig = {
|
|
1829
|
+
/** Websocket URL for vault command execution and live secret subscriptions. */
|
|
1830
|
+
url?: string;
|
|
1831
|
+
/** Token used during websocket connect/auth. */
|
|
1832
|
+
token?: string;
|
|
1833
|
+
/** Header name used to send the websocket token. Defaults to X-Vault-Token. */
|
|
1834
|
+
header?: VaultWebSocketAuthHeader;
|
|
1835
|
+
};
|
|
1836
|
+
type VaultSecretsConfig = {
|
|
1837
|
+
type: "vault";
|
|
1838
|
+
url?: string;
|
|
1839
|
+
token?: string;
|
|
1840
|
+
/** Optional LFV transport capability for reads/lifecycle operations. */
|
|
1841
|
+
lfv?: VaultLfvSecretsConfig;
|
|
1842
|
+
/** Optional websocket capability for vault commands and live subscriptions. */
|
|
1843
|
+
websocket?: VaultWebSocketSecretsConfig;
|
|
1821
1844
|
/**
|
|
1822
|
-
*
|
|
1823
|
-
* Routes: GET/POST /Users, GET/PUT/PATCH/DELETE /Users/:id
|
|
1845
|
+
* MINIMUM: 600_000 milliseconds (10 minutes). Polls the path every ttl milliseconds and calls onConfig when config changes.
|
|
1824
1846
|
*/
|
|
1825
|
-
|
|
1847
|
+
ttl?: number;
|
|
1826
1848
|
};
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
*/
|
|
1835
|
-
type IAM = IAMConfig & {
|
|
1849
|
+
type AwsSecretsConfig = {
|
|
1850
|
+
type: "aws";
|
|
1851
|
+
webhookUrl: string;
|
|
1852
|
+
};
|
|
1853
|
+
type AwsAuthMethod = "client_secret" | "workload_identity" | "managed_identity";
|
|
1854
|
+
type AzureSecretsConfig = {
|
|
1855
|
+
type: "azure";
|
|
1836
1856
|
/**
|
|
1837
|
-
*
|
|
1838
|
-
*
|
|
1857
|
+
* How to authenticate to Azure to fetch Key Vault access tokens.
|
|
1858
|
+
*
|
|
1859
|
+
* - client_secret: Entra app client secret (client credentials)
|
|
1860
|
+
* - workload_identity: AKS Workload Identity (federated token file)
|
|
1861
|
+
* - managed_identity: Azure Managed Identity via IMDS
|
|
1862
|
+
*
|
|
1863
|
+
* If omitted, we auto-detect:
|
|
1864
|
+
* - workload_identity if a federated token file is present
|
|
1865
|
+
* - else client_secret if a clientSecret is present
|
|
1866
|
+
* - else managed_identity
|
|
1867
|
+
*
|
|
1868
|
+
* Env: ES_AZURE_AUTH_METHOD
|
|
1839
1869
|
*/
|
|
1840
|
-
|
|
1870
|
+
authMethod?: AwsAuthMethod;
|
|
1841
1871
|
/**
|
|
1842
|
-
*
|
|
1872
|
+
* Azure Entra tenant id (GUID), used for OAuth2 token exchange.
|
|
1873
|
+
* Env: ES_AZURE_TENANT_ID
|
|
1843
1874
|
*/
|
|
1844
|
-
|
|
1875
|
+
tenantId?: string;
|
|
1845
1876
|
/**
|
|
1846
|
-
*
|
|
1847
|
-
*
|
|
1877
|
+
* Azure app registration client id (GUID).
|
|
1878
|
+
* Env: ES_AZURE_CLIENT_ID
|
|
1848
1879
|
*/
|
|
1849
|
-
|
|
1880
|
+
clientId?: string;
|
|
1850
1881
|
/**
|
|
1851
|
-
*
|
|
1852
|
-
*
|
|
1882
|
+
* Azure app registration client secret.
|
|
1883
|
+
* Env: ES_AZURE_CLIENT_SECRET
|
|
1853
1884
|
*/
|
|
1854
|
-
|
|
1885
|
+
clientSecret?: string;
|
|
1855
1886
|
/**
|
|
1856
|
-
*
|
|
1857
|
-
*
|
|
1858
|
-
*/
|
|
1859
|
-
users_inbound?: IAMUsersInbound;
|
|
1860
|
-
/**
|
|
1861
|
-
* Framework-agnostic request handler for the IAM module.
|
|
1862
|
-
* Routes to discovery, users_inbound, or groups_inbound based on the request path.
|
|
1863
|
-
*/
|
|
1864
|
-
handler: (request: Request, config?: IAMHandlerConfig) => Promise<Response>;
|
|
1865
|
-
};
|
|
1866
|
-
import { StandardSchemaV1 as StandardSchemaV16 } from "@standard-schema/spec";
|
|
1867
|
-
/**
|
|
1868
|
-
* Session management for tracking user sessions and enabling backchannel logout.
|
|
1869
|
-
*
|
|
1870
|
-
* Session stores are optional - the package works with JWT cookies alone.
|
|
1871
|
-
* Sessions are only required for backchannel logout functionality.
|
|
1872
|
-
*
|
|
1873
|
-
* ## Session Validation Strategies
|
|
1874
|
-
*
|
|
1875
|
-
* When using a session store, you can configure when sessions are validated:
|
|
1876
|
-
*
|
|
1877
|
-
* ### 'always' (default)
|
|
1878
|
-
* Validates session on every authenticated request.
|
|
1879
|
-
* - **Security**: Maximum - immediate session revocation
|
|
1880
|
-
* - **Performance**: InMemory ~0.00005ms, Redis ~1-2ms per request
|
|
1881
|
-
* - **Backchannel Logout**: Takes effect immediately
|
|
1882
|
-
* - **Use when**: Security is paramount, using InMemory or Redis backend
|
|
1883
|
-
*
|
|
1884
|
-
* ### 'refresh-only'
|
|
1885
|
-
* Validates session only during token refresh (typically every 5-15 minutes).
|
|
1886
|
-
* - **Security**: Good - 5-15 minute revocation window
|
|
1887
|
-
* - **Performance**: 99% reduction in session lookups
|
|
1888
|
-
* - **Backchannel Logout**: Takes effect within token TTL (5-15 min)
|
|
1889
|
-
* - **Use when**: Performance is critical AND delayed revocation is acceptable
|
|
1890
|
-
* - **WARNING**: Compromised sessions remain valid until next refresh
|
|
1891
|
-
*
|
|
1892
|
-
* ### 'disabled'
|
|
1893
|
-
* Never validates sessions against the store.
|
|
1894
|
-
* - **Security**: None - backchannel logout doesn't work
|
|
1895
|
-
* - **Performance**: No overhead
|
|
1896
|
-
* - **Use when**: Cookie-only mode without session store
|
|
1897
|
-
* - **WARNING**: Do not use with sessionStore configured
|
|
1898
|
-
*
|
|
1899
|
-
* ## Performance Characteristics
|
|
1900
|
-
*
|
|
1901
|
-
* | Backend | Lookup Time | Impact on Request | Recommendation |
|
|
1902
|
-
* |--------------|-------------|-------------------|------------------------|
|
|
1903
|
-
* | InMemory | <0.00005ms | Negligible | Use 'always' |
|
|
1904
|
-
* | Redis | 1-2ms | 2-4% increase | Use 'always' or test |
|
|
1905
|
-
* | Database | 5-20ms | 10-40% increase | Use Redis cache layer |
|
|
1906
|
-
*
|
|
1907
|
-
* ## Example Usage
|
|
1908
|
-
*
|
|
1909
|
-
* ```typescript
|
|
1910
|
-
* import { sso, InMemorySessionStore } from '@enterprisestandard/server';
|
|
1911
|
-
*
|
|
1912
|
-
* // Maximum security (default)
|
|
1913
|
-
* const secure = sso({
|
|
1914
|
-
* // ... other config
|
|
1915
|
-
* sessionStore: new InMemorySessionStore(),
|
|
1916
|
-
* session_validation: 'always', // Immediate revocation
|
|
1917
|
-
* });
|
|
1918
|
-
*
|
|
1919
|
-
* // High performance
|
|
1920
|
-
* const fast = sso({
|
|
1921
|
-
* // ... other config
|
|
1922
|
-
* sessionStore: new InMemorySessionStore(),
|
|
1923
|
-
* session_validation: {
|
|
1924
|
-
* strategy: 'refresh-only' // 5-15 min revocation delay
|
|
1925
|
-
* }
|
|
1926
|
-
* });
|
|
1927
|
-
* ```
|
|
1928
|
-
*/
|
|
1929
|
-
/**
|
|
1930
|
-
* Core session data tracked for each authenticated user session.
|
|
1931
|
-
*
|
|
1932
|
-
* @template TExtended - Type-safe custom data that consumers can add to sessions
|
|
1933
|
-
*/
|
|
1934
|
-
type Session<TExtended = object> = {
|
|
1935
|
-
/**
|
|
1936
|
-
* Session ID from the Identity Provider (from `sid` claim in ID token).
|
|
1937
|
-
* This is the unique identifier for the session.
|
|
1887
|
+
* Path to the projected federated token file (AKS Workload Identity).
|
|
1888
|
+
* Env: ES_AZURE_FEDERATED_TOKEN_FILE or AZURE_FEDERATED_TOKEN_FILE
|
|
1938
1889
|
*/
|
|
1939
|
-
|
|
1890
|
+
federatedTokenFile?: string;
|
|
1940
1891
|
/**
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1892
|
+
* Managed Identity client id (user-assigned), optional.
|
|
1893
|
+
* Env: ES_AZURE_MANAGED_IDENTITY_CLIENT_ID
|
|
1943
1894
|
*/
|
|
1944
|
-
|
|
1895
|
+
managedIdentityClientId?: string;
|
|
1945
1896
|
/**
|
|
1946
|
-
*
|
|
1897
|
+
* IMDS API version (managed identity).
|
|
1898
|
+
* Env: ES_AZURE_IMDS_API_VERSION
|
|
1899
|
+
* Default: 2018-02-01
|
|
1947
1900
|
*/
|
|
1948
|
-
|
|
1901
|
+
imdsApiVersion?: string;
|
|
1949
1902
|
/**
|
|
1950
|
-
*
|
|
1951
|
-
*
|
|
1903
|
+
* Key Vault URL, e.g. https://myvault.vault.azure.net
|
|
1904
|
+
* Env: ES_AZURE_KEY_VAULT_URL
|
|
1952
1905
|
*/
|
|
1953
|
-
|
|
1906
|
+
vaultUrl?: string;
|
|
1954
1907
|
/**
|
|
1955
|
-
*
|
|
1908
|
+
* Alternative to vaultUrl. If provided, vaultUrl is derived as:
|
|
1909
|
+
* https://${vaultName}.vault.azure.net
|
|
1910
|
+
* Env: ES_AZURE_KEY_VAULT_NAME
|
|
1956
1911
|
*/
|
|
1957
|
-
|
|
1958
|
-
} & TExtended;
|
|
1959
|
-
/**
|
|
1960
|
-
* Abstract interface for session storage backends.
|
|
1961
|
-
*
|
|
1962
|
-
* Consumers can implement this interface to use different storage backends:
|
|
1963
|
-
* - Redis
|
|
1964
|
-
* - Database (PostgreSQL, MySQL, etc.)
|
|
1965
|
-
* - Distributed cache
|
|
1966
|
-
* - Custom solutions
|
|
1967
|
-
*
|
|
1968
|
-
* @template TExtended - Type-safe custom data that consumers can add to sessions
|
|
1969
|
-
*
|
|
1970
|
-
* @example
|
|
1971
|
-
* ```typescript
|
|
1972
|
-
* // Custom session data
|
|
1973
|
-
* type MySessionData = {
|
|
1974
|
-
* ipAddress: string;
|
|
1975
|
-
* userAgent: string;
|
|
1976
|
-
* };
|
|
1977
|
-
*
|
|
1978
|
-
* // Implement custom store
|
|
1979
|
-
* class RedisSessionStore implements SessionStore<MySessionData> {
|
|
1980
|
-
* async create(session: Session<MySessionData>): Promise<void> {
|
|
1981
|
-
* await redis.set(`session:${session.sid}`, JSON.stringify(session));
|
|
1982
|
-
* }
|
|
1983
|
-
* // ... other methods
|
|
1984
|
-
* }
|
|
1985
|
-
* ```
|
|
1986
|
-
*/
|
|
1987
|
-
interface SessionStore<TExtended = object> {
|
|
1912
|
+
vaultName?: string;
|
|
1988
1913
|
/**
|
|
1989
|
-
*
|
|
1990
|
-
*
|
|
1991
|
-
*
|
|
1992
|
-
* @throws Error if session with same sid already exists
|
|
1914
|
+
* Key Vault API version.
|
|
1915
|
+
* Env: ES_AZURE_KEY_VAULT_API_VERSION
|
|
1916
|
+
* Default: 7.4
|
|
1993
1917
|
*/
|
|
1994
|
-
|
|
1918
|
+
apiVersion?: string;
|
|
1995
1919
|
/**
|
|
1996
|
-
*
|
|
1997
|
-
*
|
|
1998
|
-
* @param sid - The session.sid from the Identity Provider
|
|
1999
|
-
* @returns The session if found, null otherwise
|
|
1920
|
+
* Maps vault "path" to a Key Vault secret name.
|
|
1921
|
+
* Default: replaces `/` with `--` and trims leading/trailing `/`.
|
|
2000
1922
|
*/
|
|
2001
|
-
|
|
1923
|
+
secretNameTransform?: (path: string) => string;
|
|
2002
1924
|
/**
|
|
2003
|
-
*
|
|
2004
|
-
*
|
|
2005
|
-
* Commonly used to update lastActivityAt or add custom fields.
|
|
2006
|
-
*
|
|
2007
|
-
* @param sid - The session.sid to update
|
|
2008
|
-
* @param data - Partial session data to merge
|
|
2009
|
-
* @throws Error if session not found
|
|
1925
|
+
* Optional prefix added to all computed secret names.
|
|
1926
|
+
* Useful to namespace secrets by app/environment.
|
|
2010
1927
|
*/
|
|
2011
|
-
|
|
1928
|
+
secretNamePrefix?: string;
|
|
2012
1929
|
/**
|
|
2013
|
-
*
|
|
2014
|
-
*
|
|
2015
|
-
* Used for both normal logout and backchannel logout flows.
|
|
2016
|
-
*
|
|
2017
|
-
* @param sid - The session.sid to delete
|
|
1930
|
+
* OAuth2 scope; default is Key Vault resource scope.
|
|
1931
|
+
* Default: https://vault.azure.net/.default
|
|
2018
1932
|
*/
|
|
2019
|
-
delete(sid: string): Promise<void>;
|
|
2020
|
-
}
|
|
2021
|
-
type SSOConfig<
|
|
2022
|
-
TSessionData = Record<string, never>,
|
|
2023
|
-
TUserData = Record<string, never>
|
|
2024
|
-
> = {
|
|
2025
|
-
authority?: string;
|
|
2026
|
-
tokenUrl?: string;
|
|
2027
|
-
authorizationUrl?: string;
|
|
2028
|
-
clientId?: string;
|
|
2029
|
-
clientSecret?: string;
|
|
2030
|
-
redirectUri?: string;
|
|
2031
|
-
responseType?: "code";
|
|
2032
1933
|
scope?: string;
|
|
2033
|
-
silentRedirectUri?: string;
|
|
2034
|
-
jwksUri?: string;
|
|
2035
|
-
cookiesPrefix?: string;
|
|
2036
|
-
cookiesPath?: string;
|
|
2037
|
-
cookiesSecure?: boolean;
|
|
2038
|
-
cookiesSameSite?: "Strict" | "Lax";
|
|
2039
1934
|
/**
|
|
2040
|
-
*
|
|
2041
|
-
* Defaults to 'es.active_session' when using the helper utilities.
|
|
2042
|
-
*/
|
|
2043
|
-
activeSessionCookieName?: string;
|
|
2044
|
-
endSessionEndpoint?: string;
|
|
2045
|
-
revocationEndpoint?: string;
|
|
2046
|
-
sessionStore?: SessionStore<TSessionData>;
|
|
2047
|
-
/**
|
|
2048
|
-
* Optional user store for persisting user profiles from SSO authentication.
|
|
2049
|
-
* When configured, users are automatically stored/updated on each login.
|
|
2050
|
-
*/
|
|
2051
|
-
userStore?: UserStore<TUserData>;
|
|
2052
|
-
/**
|
|
2053
|
-
* Enable Just-In-Time (JIT) user provisioning.
|
|
2054
|
-
* When enabled, new users are automatically created in the userStore on their first login.
|
|
2055
|
-
* When disabled (default), only existing users in the userStore are updated on login.
|
|
2056
|
-
* Requires userStore to be configured.
|
|
2057
|
-
* @default false
|
|
1935
|
+
* When set, the vault implements subscribe: poll this path every ttl seconds and call onConfig when config changes.
|
|
2058
1936
|
*/
|
|
2059
|
-
|
|
2060
|
-
validators?: SSOValidators;
|
|
2061
|
-
} & SSOHandlerConfig;
|
|
2062
|
-
type StateCookie = {
|
|
2063
|
-
state: string;
|
|
2064
|
-
codeVerifier: string;
|
|
2065
|
-
landingUrl: string;
|
|
2066
|
-
errorUrl?: string;
|
|
1937
|
+
ttl?: number;
|
|
2067
1938
|
};
|
|
2068
|
-
type
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
1939
|
+
type ConfigSourceType = "vault" | "azure" | "aws" | "gcp";
|
|
1940
|
+
type ESValidators = {
|
|
1941
|
+
sso: SSOValidators;
|
|
1942
|
+
iam: IAMValidators;
|
|
1943
|
+
workload: WorkloadValidators;
|
|
1944
|
+
ciam: CIAMValidators;
|
|
1945
|
+
secrets?: SecretsValidators;
|
|
2074
1946
|
};
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
declare function clearActiveSession(options?: CookieOptions): string;
|
|
2078
|
-
declare function listSsoClientIdsFromCookies(requestOrCookieHeader: Request | string | null | undefined, options?: {
|
|
2079
|
-
cookiePrefix?: string;
|
|
2080
|
-
}): string[];
|
|
2081
|
-
declare function findTenantFromStateParam(cookieHeader: string | null | undefined, stateParam: string): {
|
|
2082
|
-
clientId: string;
|
|
2083
|
-
stateCookie: StateCookie;
|
|
2084
|
-
} | undefined;
|
|
2085
|
-
type LoginConfig = {
|
|
2086
|
-
landingUrl: string;
|
|
2087
|
-
errorUrl?: string;
|
|
1947
|
+
type ApplicationValidators<TTenantValidators extends TenantValidators = TenantValidators> = ESValidators & {
|
|
1948
|
+
tenant: TTenantValidators;
|
|
2088
1949
|
};
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
1950
|
+
/**
|
|
1951
|
+
* Configuration supplied by the framework/application when creating an Enterprise Standard instance.
|
|
1952
|
+
* Merged with RemoteConfig from the ConfigSource (framework config wins). Pass as the second
|
|
1953
|
+
* argument to enterpriseStandard(source, config).
|
|
1954
|
+
* Set a module to `null` to explicitly disable it; then the corresponding property on the
|
|
1955
|
+
* EnterpriseStandard instance is typed as `never`. Omit a module to allow it to be supplied
|
|
1956
|
+
* from ConfigSource / adaptive (typed as the module type, non-optional).
|
|
1957
|
+
*/
|
|
1958
|
+
type FrameworkConfig = {
|
|
1959
|
+
/** Optional `Logger` implementation (e.g. `consoleLogger`); exposed on the instance as `log`. */
|
|
1960
|
+
log?: Logger;
|
|
1961
|
+
sso?: SSOConfig | null;
|
|
1962
|
+
iam?: IAMConfig | null;
|
|
1963
|
+
workload?: FrameworkWorkloadConfig | null;
|
|
1964
|
+
secrets?: FrameworkSecretsModuleConfig | null;
|
|
1965
|
+
ciam?: CIAMConfig | null;
|
|
1966
|
+
validators: ESValidators;
|
|
2104
1967
|
};
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
1968
|
+
/**
|
|
1969
|
+
* Final configuration after merging ConfigSource (RemoteConfig) and FrameworkConfig.
|
|
1970
|
+
* Same shape as FrameworkConfig; each module (SSO, IAM, etc.) resolves its config from
|
|
1971
|
+
* both sources at runtime. Use this type when referring to the effective/merged config.
|
|
1972
|
+
*/
|
|
1973
|
+
type ESConfig = FrameworkConfig;
|
|
1974
|
+
/**
|
|
1975
|
+
* Remote config read from a config source (vault, lfv, etc.).
|
|
1976
|
+
*/
|
|
1977
|
+
type RemoteConfig = {
|
|
1978
|
+
/** Optional app/tenant identifier for this ESA (e.g. from vault path or config). */
|
|
1979
|
+
tenantId?: string;
|
|
1980
|
+
sso?: SSOConfig;
|
|
1981
|
+
iam?: IAMConfig;
|
|
2110
1982
|
/**
|
|
2111
|
-
*
|
|
2112
|
-
*
|
|
2113
|
-
*
|
|
2114
|
-
*
|
|
1983
|
+
* Workload: single config, or incoming/outgoing (server vs client roles), or flat map of named clients.
|
|
1984
|
+
* Preferred: { incoming: { jwksUri, issuer }, outgoing: { TNT_*: { clientId, ... } } }.
|
|
1985
|
+
* Legacy: { default: { jwksUri, issuer }, TNT_*: { ... } } (flat map).
|
|
1986
|
+
* TODO: Let's see if we can do some clean inference here!!!
|
|
2115
1987
|
*/
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
logout: (request: Request, config?: LoginConfig) => Promise<Response>;
|
|
2121
|
-
logoutBackChannel2: (request: Request) => Promise<Response>;
|
|
2122
|
-
callbackHandler: (request: Request) => Promise<Response>;
|
|
2123
|
-
handler: (request: Request) => Promise<Response>;
|
|
2124
|
-
};
|
|
2125
|
-
import { StandardSchemaV1 as StandardSchemaV17 } from "@standard-schema/spec";
|
|
2126
|
-
type Secret<T> = {
|
|
2127
|
-
data: T;
|
|
2128
|
-
metadata: MetaData;
|
|
1988
|
+
workload?: WorkloadConfig | WorkloadConfigMap | WorkloadIncomingOutgoing;
|
|
1989
|
+
/** Optional named secrets-source configs available to this ESA instance. */
|
|
1990
|
+
secrets?: SecretsModuleConfig;
|
|
1991
|
+
ciam?: CIAMConfig;
|
|
2129
1992
|
};
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
1993
|
+
/**
|
|
1994
|
+
* Stores supplied by the framework/application when creating an Enterprise Standard instance.
|
|
1995
|
+
*/
|
|
1996
|
+
type FrameworkStores = {
|
|
1997
|
+
sessionStore?: SessionStore<unknown>;
|
|
1998
|
+
userStore?: UserStore<unknown>;
|
|
1999
|
+
groupStore?: GroupStore<unknown>;
|
|
2000
|
+
magicLinkStore?: MagicLinkStore<unknown>;
|
|
2001
|
+
workloadTokenStore?: WorkloadTokenStore;
|
|
2134
2002
|
};
|
|
2135
|
-
type
|
|
2136
|
-
|
|
2137
|
-
type SecretLifecycleRequest = {
|
|
2138
|
-
reason?: string;
|
|
2139
|
-
severity?: SecretRequestSeverity;
|
|
2140
|
-
incidentRef?: string;
|
|
2003
|
+
type ModifiableFrameworkConfig = FrameworkConfig & {
|
|
2004
|
+
setStores(stores: FrameworkStores): void;
|
|
2141
2005
|
};
|
|
2142
|
-
type
|
|
2143
|
-
|
|
2144
|
-
|
|
2006
|
+
/** Return type from the beforeChange hook passed to enterpriseStandard(). */
|
|
2007
|
+
type ESConfigChangeResult = {
|
|
2008
|
+
config?: RemoteConfig;
|
|
2009
|
+
frameworkConfig?: FrameworkConfig;
|
|
2145
2010
|
};
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
/**
|
|
2151
|
-
* Write a secret at the given path.
|
|
2152
|
-
* Implementations may throw for read-only or unsupported secret sources.
|
|
2153
|
-
*/
|
|
2154
|
-
putSecret: (path: string, value: Record<string, unknown>, options?: SecretsOperationOptions) => Promise<void>;
|
|
2155
|
-
/**
|
|
2156
|
-
* When set, the secret source supports change detection and will call onChange when the secret changes.
|
|
2157
|
-
* Implementations must only invoke onChange when the secret version has changed from the last time
|
|
2158
|
-
* that subscription was notified (same version must not trigger a second call).
|
|
2159
|
-
* @returns a unsubscribe/cleanup function.
|
|
2160
|
-
*/
|
|
2161
|
-
subscribe<T>(path: string, onChange: (fullSecret: Secret<T>) => void): () => void;
|
|
2162
|
-
/**
|
|
2163
|
-
* Deletes a secret at the given path.
|
|
2164
|
-
*/
|
|
2165
|
-
deleteSecret(path: string, options?: SecretsOperationOptions): Promise<void>;
|
|
2166
|
-
/**
|
|
2167
|
-
* Lists child paths under the given base path.
|
|
2168
|
-
*/
|
|
2169
|
-
listPaths(path: string, options?: SecretsOperationOptions): Promise<string[]>;
|
|
2011
|
+
/** beforeChange callback invoked on every config application (initial load and updates). */
|
|
2012
|
+
type ESConfigChangeCallback = (config: RemoteConfig, frameworkConfig: ModifiableFrameworkConfig, oldConfig: RemoteConfig | undefined) => ESConfigChangeResult | void;
|
|
2013
|
+
type ConfigSource = {
|
|
2014
|
+
load(): Promise<RemoteConfig>;
|
|
2170
2015
|
/**
|
|
2171
|
-
*
|
|
2016
|
+
* Called when the config changes.
|
|
2017
|
+
* @param onConfig - The callback to call when the config changes.
|
|
2018
|
+
* @return an unsubscribe/cleanup function.
|
|
2172
2019
|
*/
|
|
2173
|
-
|
|
2020
|
+
subscribe(onConfig: (config: RemoteConfig) => void): undefined | (() => void);
|
|
2174
2021
|
/**
|
|
2175
|
-
*
|
|
2022
|
+
* Default secret client for the config source itself.
|
|
2023
|
+
* For vault-backed sources this is the vault used to read RemoteConfig.
|
|
2176
2024
|
*/
|
|
2177
|
-
|
|
2025
|
+
secret: SecretsSource;
|
|
2178
2026
|
/**
|
|
2179
|
-
*
|
|
2027
|
+
* Optional. If not set by the creator, the framework may set this before calling load/subscribe
|
|
2028
|
+
* so the source can use the same logger as the Enterprise Standard instance (`log`).
|
|
2180
2029
|
*/
|
|
2181
|
-
|
|
2030
|
+
log?: Logger;
|
|
2182
2031
|
/**
|
|
2183
|
-
*
|
|
2032
|
+
* Optional. If not set by the creator, the framework may set this before calling load/subscribe
|
|
2033
|
+
* so the source can use the same validators.
|
|
2184
2034
|
*/
|
|
2185
|
-
|
|
2035
|
+
validators?: ESValidators;
|
|
2186
2036
|
};
|
|
2187
|
-
type
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2037
|
+
type EnvironmentType = "POC" | "DEV" | "QA" | "PROD";
|
|
2038
|
+
type TenantStatus = "pending" | "processing" | "completed" | "failed" | "action_required";
|
|
2039
|
+
type UpsertTenantRequestBase = {
|
|
2040
|
+
tenantId: string;
|
|
2041
|
+
companyId: string;
|
|
2042
|
+
companyName: string;
|
|
2043
|
+
environmentType: EnvironmentType;
|
|
2044
|
+
email?: string;
|
|
2045
|
+
webhookUrl?: string;
|
|
2046
|
+
callbackUrl?: string;
|
|
2047
|
+
configSource: TenantSecretsConfig;
|
|
2193
2048
|
};
|
|
2194
|
-
type
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
putSecret(sourceName: string, path: string, value: Record<string, unknown>, options?: SecretsOperationOptions): Promise<void>;
|
|
2207
|
-
/** Deletes a secret from a named secrets source client. */
|
|
2208
|
-
deleteSecret(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<void>;
|
|
2209
|
-
/** Lists child paths under a base path for a named secrets source client. */
|
|
2210
|
-
listPaths(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<string[]>;
|
|
2211
|
-
/** Returns true when a path exists for a named secrets source client. */
|
|
2212
|
-
exists(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<boolean>;
|
|
2213
|
-
/** Requests rotation for a secret path in a named secrets source client. */
|
|
2214
|
-
requestRotate(sourceName: string, path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
2215
|
-
/** Requests revocation for a secret path in a named secrets source client. */
|
|
2216
|
-
requestRevoke(sourceName: string, path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
2217
|
-
/** Reads metadata for a secret path in a named secrets source client. */
|
|
2218
|
-
getMetadata(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<Record<string, unknown>>;
|
|
2219
|
-
/** Subscribes to secret changes on a named secrets source client. */
|
|
2220
|
-
subscribe<T>(sourceName: string, path: string, onChange: (fullSecret: Secret<T>) => void): () => void;
|
|
2221
|
-
/** Returns true when request matches any configured LFV delivery path. */
|
|
2222
|
-
isLfvDeliveryRequest?(request: Request): boolean;
|
|
2223
|
-
/** Returns true when request matches any configured LFV events path. */
|
|
2224
|
-
isLfvEventsRequest?(request: Request): boolean;
|
|
2225
|
-
/** Handles LFV delivery callbacks for configured LFV sources. */
|
|
2226
|
-
handleLfvDelivery?(request: Request): Promise<Response>;
|
|
2227
|
-
/** Handles LFV events callbacks for configured LFV sources. */
|
|
2228
|
-
handleLfvEvents?(request: Request): Promise<Response>;
|
|
2049
|
+
type UpsertTenantRequest<TExtended extends object = object> = UpsertTenantRequestBase & TExtended;
|
|
2050
|
+
type UpsertTenantResponse = {
|
|
2051
|
+
tenantUrl?: string;
|
|
2052
|
+
status: Exclude<TenantStatus, "action_required">;
|
|
2053
|
+
error?: string;
|
|
2054
|
+
refs?: RefUrls[];
|
|
2055
|
+
} | {
|
|
2056
|
+
status: "action_required";
|
|
2057
|
+
actionUrl: string;
|
|
2058
|
+
requestToken: string;
|
|
2059
|
+
expiresAt: string;
|
|
2060
|
+
refs?: RefUrls[];
|
|
2229
2061
|
};
|
|
2062
|
+
type CreateTenantRequest = UpsertTenantRequest;
|
|
2063
|
+
type CreateTenantResponse = UpsertTenantResponse;
|
|
2230
2064
|
/**
|
|
2231
|
-
*
|
|
2232
|
-
*
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
/**
|
|
2236
|
-
* Framework-level named secrets source declarations keyed by source name.
|
|
2237
|
-
* Values may be partial or empty when the app only wants to declare expected names/types.
|
|
2238
|
-
*/
|
|
2239
|
-
type FrameworkSecretsModuleConfig = Record<string, FrameworkSecretsSourceConfig>;
|
|
2240
|
-
/**
|
|
2241
|
-
* TODO: Let's see if we can do some clean inference and remove this!!!
|
|
2065
|
+
* The audience of the reference URL.
|
|
2066
|
+
* - 'human' for human-readable documentation such as user guides, documentation, etc.
|
|
2067
|
+
* - 'ai' for AI-generated reference such as llms.txt, AI-optimized markdown, etc.
|
|
2068
|
+
* - 'spec' for specifications and code-gen ready docs such as OpenAPI, GraphQL, etc.
|
|
2242
2069
|
*/
|
|
2243
|
-
type
|
|
2244
|
-
|
|
2070
|
+
type Audience = "human" | "ai" | "spec";
|
|
2071
|
+
interface RefUrls {
|
|
2072
|
+
audience: Audience;
|
|
2073
|
+
url: string;
|
|
2074
|
+
description: string;
|
|
2075
|
+
createdAt: Date;
|
|
2076
|
+
updatedAt: Date;
|
|
2077
|
+
}
|
|
2078
|
+
interface TenantWebhookPayload {
|
|
2079
|
+
tenantId: string;
|
|
2080
|
+
companyId: string;
|
|
2081
|
+
status: TenantStatus;
|
|
2082
|
+
tenantUrl?: string;
|
|
2083
|
+
actionUrl?: string;
|
|
2084
|
+
requestToken?: string;
|
|
2085
|
+
expiresAt?: string;
|
|
2086
|
+
error?: string;
|
|
2087
|
+
}
|
|
2088
|
+
declare class TenantRequestError extends Error {
|
|
2089
|
+
constructor(message: string, options?: ErrorOptions);
|
|
2090
|
+
}
|
|
2091
|
+
declare class MultipleTenantsForUserError extends Error {
|
|
2092
|
+
readonly userId: string;
|
|
2093
|
+
readonly tenantIds: string[];
|
|
2094
|
+
constructor(userId: string, tenantIds: string[], options?: ErrorOptions);
|
|
2095
|
+
}
|
|
2096
|
+
type TenantValidators<
|
|
2097
|
+
TUpsertTenantRequest extends UpsertTenantRequest = UpsertTenantRequest,
|
|
2098
|
+
TUpsertTenantResponse extends UpsertTenantResponse = UpsertTenantResponse
|
|
2099
|
+
> = {
|
|
2100
|
+
upsertTenantRequest: StandardSchemaV16<unknown, TUpsertTenantRequest>;
|
|
2101
|
+
upsertTenantResponse?: StandardSchemaV16<unknown, TUpsertTenantResponse>;
|
|
2102
|
+
createTenantRequest?: StandardSchemaV16<unknown, TUpsertTenantRequest>;
|
|
2103
|
+
createTenantResponse?: StandardSchemaV16<unknown, TUpsertTenantResponse>;
|
|
2104
|
+
};
|
|
2245
2105
|
/**
|
|
2246
|
-
*
|
|
2247
|
-
*
|
|
2248
|
-
*/
|
|
2249
|
-
type SecretsModuleConfig = Record<string, SecretsSourceConfig>;
|
|
2250
|
-
type DevSecretsConfig = {
|
|
2251
|
-
type: "dev";
|
|
2252
|
-
ioniteUrl: string;
|
|
2253
|
-
};
|
|
2254
|
-
type GcpSecretsConfig = {
|
|
2255
|
-
type: "gcp";
|
|
2256
|
-
};
|
|
2257
|
-
type VaultLfvSecretsConfig = {
|
|
2258
|
-
/** LFV server base URL for OTP/action endpoints. */
|
|
2259
|
-
lfvServerUrl?: string;
|
|
2260
|
-
/** LFV client id used for OTP issuance. */
|
|
2261
|
-
clientId?: string;
|
|
2262
|
-
/** Signature value for X-LFV-Signature header. */
|
|
2263
|
-
signature?: string;
|
|
2264
|
-
deliveryEndpoint: string;
|
|
2265
|
-
/**
|
|
2266
|
-
* Optional LFV signature verification key.
|
|
2267
|
-
* When omitted, LFV callbacks are accepted without signature verification.
|
|
2268
|
-
*/
|
|
2269
|
-
verifyPublicKey?: string;
|
|
2270
|
-
eventsEndpoint: string;
|
|
2271
|
-
path?: string;
|
|
2272
|
-
/**
|
|
2273
|
-
* Timeout in milliseconds when waiting for a LFV delivery payload after a read request.
|
|
2274
|
-
* After this duration, an error is thrown.
|
|
2275
|
-
*/
|
|
2276
|
-
deliveryTimeout?: number;
|
|
2277
|
-
/** Retry interval in milliseconds between LFV transport retry attempts. */
|
|
2278
|
-
retryInterval?: number;
|
|
2279
|
-
/** Warning interval in milliseconds for LFV retry logs. Set to 0 to disable warnings. */
|
|
2280
|
-
warnInterval?: number;
|
|
2281
|
-
/**
|
|
2282
|
-
* Optional logger for request/response tracing. Use `debugLogger` from `@enterprisestandard/core`
|
|
2283
|
-
* to get debug output with request_id for LFV operations.
|
|
2284
|
-
*/
|
|
2285
|
-
log?: Logger;
|
|
2286
|
-
};
|
|
2287
|
-
/**
|
|
2288
|
-
* Runtime-ready LFV source config.
|
|
2289
|
-
* Input config can be partially declared/merged, but LFV operations require these fields.
|
|
2290
|
-
*/
|
|
2291
|
-
type ResolvedVaultLfvSecretsConfig = Omit<VaultLfvSecretsConfig, "lfvServerUrl" | "clientId" | "path"> & {
|
|
2292
|
-
lfvServerUrl: string;
|
|
2293
|
-
clientId: string;
|
|
2294
|
-
path: string;
|
|
2295
|
-
};
|
|
2296
|
-
type VaultWebSocketAuthHeader = "X-Vault-Token" | "Authorization";
|
|
2297
|
-
type VaultWebSocketSecretsConfig = {
|
|
2298
|
-
/** Websocket URL for vault command execution and live secret subscriptions. */
|
|
2299
|
-
url?: string;
|
|
2300
|
-
/** Token used during websocket connect/auth. */
|
|
2301
|
-
token?: string;
|
|
2302
|
-
/** Header name used to send the websocket token. Defaults to X-Vault-Token. */
|
|
2303
|
-
header?: VaultWebSocketAuthHeader;
|
|
2304
|
-
};
|
|
2305
|
-
type VaultSecretsConfig = {
|
|
2306
|
-
type: "vault";
|
|
2307
|
-
url?: string;
|
|
2308
|
-
token?: string;
|
|
2309
|
-
/** Optional LFV transport capability for reads/lifecycle operations. */
|
|
2310
|
-
lfv?: VaultLfvSecretsConfig;
|
|
2311
|
-
/** Optional websocket capability for vault commands and live subscriptions. */
|
|
2312
|
-
websocket?: VaultWebSocketSecretsConfig;
|
|
2313
|
-
/**
|
|
2314
|
-
* MINIMUM: 600_000 milliseconds (10 minutes). Polls the path every ttl milliseconds and calls onConfig when config changes.
|
|
2315
|
-
*/
|
|
2316
|
-
ttl?: number;
|
|
2317
|
-
};
|
|
2318
|
-
type AwsSecretsConfig = {
|
|
2319
|
-
type: "aws";
|
|
2320
|
-
webhookUrl: string;
|
|
2321
|
-
};
|
|
2322
|
-
type AwsAuthMethod = "client_secret" | "workload_identity" | "managed_identity";
|
|
2323
|
-
type AzureSecretsConfig = {
|
|
2324
|
-
type: "azure";
|
|
2325
|
-
/**
|
|
2326
|
-
* How to authenticate to Azure to fetch Key Vault access tokens.
|
|
2327
|
-
*
|
|
2328
|
-
* - client_secret: Entra app client secret (client credentials)
|
|
2329
|
-
* - workload_identity: AKS Workload Identity (federated token file)
|
|
2330
|
-
* - managed_identity: Azure Managed Identity via IMDS
|
|
2331
|
-
*
|
|
2332
|
-
* If omitted, we auto-detect:
|
|
2333
|
-
* - workload_identity if a federated token file is present
|
|
2334
|
-
* - else client_secret if a clientSecret is present
|
|
2335
|
-
* - else managed_identity
|
|
2336
|
-
*
|
|
2337
|
-
* Env: ES_AZURE_AUTH_METHOD
|
|
2338
|
-
*/
|
|
2339
|
-
authMethod?: AwsAuthMethod;
|
|
2340
|
-
/**
|
|
2341
|
-
* Azure Entra tenant id (GUID), used for OAuth2 token exchange.
|
|
2342
|
-
* Env: ES_AZURE_TENANT_ID
|
|
2343
|
-
*/
|
|
2344
|
-
tenantId?: string;
|
|
2345
|
-
/**
|
|
2346
|
-
* Azure app registration client id (GUID).
|
|
2347
|
-
* Env: ES_AZURE_CLIENT_ID
|
|
2348
|
-
*/
|
|
2349
|
-
clientId?: string;
|
|
2350
|
-
/**
|
|
2351
|
-
* Azure app registration client secret.
|
|
2352
|
-
* Env: ES_AZURE_CLIENT_SECRET
|
|
2353
|
-
*/
|
|
2354
|
-
clientSecret?: string;
|
|
2355
|
-
/**
|
|
2356
|
-
* Path to the projected federated token file (AKS Workload Identity).
|
|
2357
|
-
* Env: ES_AZURE_FEDERATED_TOKEN_FILE or AZURE_FEDERATED_TOKEN_FILE
|
|
2358
|
-
*/
|
|
2359
|
-
federatedTokenFile?: string;
|
|
2360
|
-
/**
|
|
2361
|
-
* Managed Identity client id (user-assigned), optional.
|
|
2362
|
-
* Env: ES_AZURE_MANAGED_IDENTITY_CLIENT_ID
|
|
2363
|
-
*/
|
|
2364
|
-
managedIdentityClientId?: string;
|
|
2365
|
-
/**
|
|
2366
|
-
* IMDS API version (managed identity).
|
|
2367
|
-
* Env: ES_AZURE_IMDS_API_VERSION
|
|
2368
|
-
* Default: 2018-02-01
|
|
2369
|
-
*/
|
|
2370
|
-
imdsApiVersion?: string;
|
|
2371
|
-
/**
|
|
2372
|
-
* Key Vault URL, e.g. https://myvault.vault.azure.net
|
|
2373
|
-
* Env: ES_AZURE_KEY_VAULT_URL
|
|
2374
|
-
*/
|
|
2375
|
-
vaultUrl?: string;
|
|
2376
|
-
/**
|
|
2377
|
-
* Alternative to vaultUrl. If provided, vaultUrl is derived as:
|
|
2378
|
-
* https://${vaultName}.vault.azure.net
|
|
2379
|
-
* Env: ES_AZURE_KEY_VAULT_NAME
|
|
2380
|
-
*/
|
|
2381
|
-
vaultName?: string;
|
|
2382
|
-
/**
|
|
2383
|
-
* Key Vault API version.
|
|
2384
|
-
* Env: ES_AZURE_KEY_VAULT_API_VERSION
|
|
2385
|
-
* Default: 7.4
|
|
2386
|
-
*/
|
|
2387
|
-
apiVersion?: string;
|
|
2388
|
-
/**
|
|
2389
|
-
* Maps vault "path" to a Key Vault secret name.
|
|
2390
|
-
* Default: replaces `/` with `--` and trims leading/trailing `/`.
|
|
2391
|
-
*/
|
|
2392
|
-
secretNameTransform?: (path: string) => string;
|
|
2393
|
-
/**
|
|
2394
|
-
* Optional prefix added to all computed secret names.
|
|
2395
|
-
* Useful to namespace secrets by app/environment.
|
|
2396
|
-
*/
|
|
2397
|
-
secretNamePrefix?: string;
|
|
2398
|
-
/**
|
|
2399
|
-
* OAuth2 scope; default is Key Vault resource scope.
|
|
2400
|
-
* Default: https://vault.azure.net/.default
|
|
2401
|
-
*/
|
|
2402
|
-
scope?: string;
|
|
2403
|
-
/**
|
|
2404
|
-
* When set, the vault implements subscribe: poll this path every ttl seconds and call onConfig when config changes.
|
|
2405
|
-
*/
|
|
2406
|
-
ttl?: number;
|
|
2407
|
-
};
|
|
2408
|
-
type EnvironmentType = "POC" | "DEV" | "QA" | "PROD";
|
|
2409
|
-
type TenantStatus = "pending" | "processing" | "completed" | "failed" | "action_required";
|
|
2410
|
-
interface UpsertTenantRequest {
|
|
2411
|
-
tenantId: string;
|
|
2412
|
-
companyId: string;
|
|
2413
|
-
companyName: string;
|
|
2414
|
-
environmentType: EnvironmentType;
|
|
2415
|
-
email?: string;
|
|
2416
|
-
webhookUrl?: string;
|
|
2417
|
-
callbackUrl?: string;
|
|
2418
|
-
configSource: TenantSecretsConfig;
|
|
2419
|
-
}
|
|
2420
|
-
type UpsertTenantResponse = {
|
|
2421
|
-
tenantUrl?: string;
|
|
2422
|
-
status: Exclude<TenantStatus, "action_required">;
|
|
2423
|
-
error?: string;
|
|
2424
|
-
refs?: RefUrls[];
|
|
2425
|
-
} | {
|
|
2426
|
-
status: "action_required";
|
|
2427
|
-
actionUrl: string;
|
|
2428
|
-
requestToken: string;
|
|
2429
|
-
expiresAt: string;
|
|
2430
|
-
refs?: RefUrls[];
|
|
2431
|
-
};
|
|
2432
|
-
type CreateTenantRequest = UpsertTenantRequest;
|
|
2433
|
-
type CreateTenantResponse = UpsertTenantResponse;
|
|
2434
|
-
/**
|
|
2435
|
-
* The audience of the reference URL.
|
|
2436
|
-
* - 'human' for human-readable documentation such as user guides, documentation, etc.
|
|
2437
|
-
* - 'ai' for AI-generated reference such as llms.txt, AI-optimized markdown, etc.
|
|
2438
|
-
* - 'spec' for specifications and code-gen ready docs such as OpenAPI, GraphQL, etc.
|
|
2439
|
-
*/
|
|
2440
|
-
type Audience = "human" | "ai" | "spec";
|
|
2441
|
-
interface RefUrls {
|
|
2442
|
-
audience: Audience;
|
|
2443
|
-
url: string;
|
|
2444
|
-
description: string;
|
|
2445
|
-
createdAt: Date;
|
|
2446
|
-
updatedAt: Date;
|
|
2447
|
-
}
|
|
2448
|
-
interface TenantWebhookPayload {
|
|
2449
|
-
tenantId: string;
|
|
2450
|
-
companyId: string;
|
|
2451
|
-
status: TenantStatus;
|
|
2452
|
-
tenantUrl?: string;
|
|
2453
|
-
actionUrl?: string;
|
|
2454
|
-
requestToken?: string;
|
|
2455
|
-
expiresAt?: string;
|
|
2456
|
-
error?: string;
|
|
2457
|
-
}
|
|
2458
|
-
declare class TenantRequestError extends Error {
|
|
2459
|
-
constructor(message: string, options?: ErrorOptions);
|
|
2460
|
-
}
|
|
2461
|
-
declare class MultipleTenantsForUserError extends Error {
|
|
2462
|
-
readonly userId: string;
|
|
2463
|
-
readonly tenantIds: string[];
|
|
2464
|
-
constructor(userId: string, tenantIds: string[], options?: ErrorOptions);
|
|
2465
|
-
}
|
|
2466
|
-
type UserMode = "singleTenantOnly" | "multipleTenantsPerUser";
|
|
2467
|
-
type TenantValidators = {
|
|
2468
|
-
upsertTenantRequest: StandardSchemaV17<unknown, UpsertTenantRequest>;
|
|
2469
|
-
upsertTenantResponse?: StandardSchemaV17<unknown, UpsertTenantResponse>;
|
|
2470
|
-
createTenantRequest?: StandardSchemaV17<unknown, UpsertTenantRequest>;
|
|
2471
|
-
createTenantResponse?: StandardSchemaV17<unknown, UpsertTenantResponse>;
|
|
2472
|
-
};
|
|
2473
|
-
/**
|
|
2474
|
-
* Env-like tenant config variables used to build a ConfigSource at runtime.
|
|
2475
|
-
* These mirror the ES_* variables read by envConfig().
|
|
2106
|
+
* Env-like tenant config variables used to build a ConfigSource at runtime.
|
|
2107
|
+
* These mirror the ES_* variables read by envConfig().
|
|
2476
2108
|
*/
|
|
2477
2109
|
type TenantConfigEnv = {
|
|
2478
2110
|
ES_CONFIG_TYPE?: ConfigSourceType;
|
|
@@ -2554,317 +2186,657 @@ type TenantBaseRecord = {
|
|
|
2554
2186
|
/** Persisted tenant config metadata, or a runtime ConfigSource for internal-only tenants. */
|
|
2555
2187
|
configSource: TenantConfigSourceInput;
|
|
2556
2188
|
/** Runtime helper that returns a ConfigSource for this tenant. */
|
|
2557
|
-
config
|
|
2558
|
-
};
|
|
2559
|
-
type
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
type
|
|
2189
|
+
config?: (source?: SecretsSource) => ConfigSource;
|
|
2190
|
+
};
|
|
2191
|
+
type TenantBaseConstraint = Omit<TenantBaseRecord, "configSource"> & {
|
|
2192
|
+
configSource?: TenantConfigSourceInput;
|
|
2193
|
+
};
|
|
2194
|
+
type TenantRecordBase = TenantBaseConstraint;
|
|
2195
|
+
type StoredTenant<TTenant extends TenantRecordBase = TenantRecordBase> = TTenant;
|
|
2196
|
+
type StoredTenantRecord<TTenant extends TenantRecordBase = TenantRecordBase> = Omit<StoredTenant<TTenant>, "config">;
|
|
2197
|
+
type TenantEsFactory<TTenant extends TenantRecordBase = TenantRecordBase> = (tenant: StoredTenant<TTenant>) => EnterpriseStandard;
|
|
2198
|
+
type TenantConfigStoreRequest<
|
|
2199
|
+
TTenant extends TenantRecordBase = TenantRecordBase,
|
|
2200
|
+
TRequest extends UpsertTenantRequest = UpsertTenantRequest
|
|
2201
|
+
> = {
|
|
2563
2202
|
es: EnterpriseStandard;
|
|
2564
2203
|
tenantId: string;
|
|
2565
|
-
request:
|
|
2204
|
+
request: TRequest;
|
|
2566
2205
|
configData: TenantSecretsConfig;
|
|
2567
|
-
existingTenant: StoredTenant<
|
|
2206
|
+
existingTenant: StoredTenant<TTenant> | undefined;
|
|
2207
|
+
};
|
|
2208
|
+
type TenantStoreWithESOptions<TTenant extends TenantRecordBase = TenantRecordBase> = {
|
|
2209
|
+
/**
|
|
2210
|
+
* TTL for cached per-tenant EnterpriseStandard instances, in milliseconds.
|
|
2211
|
+
* Default is forever; set to 0 to recreate ES on every getEs() call.
|
|
2212
|
+
*/
|
|
2213
|
+
ttl?: number;
|
|
2214
|
+
/**
|
|
2215
|
+
* Optional factory used to create an ES instance for a tenant.
|
|
2216
|
+
* If omitted, getEs() throws.
|
|
2217
|
+
*/
|
|
2218
|
+
createEs?: TenantEsFactory<TTenant>;
|
|
2219
|
+
};
|
|
2220
|
+
type TenantUserRegistration = {
|
|
2221
|
+
registerUserTenantId(userId: string, tenantId: string | null | undefined): void | Promise<void>;
|
|
2222
|
+
registerUserToTenant?(userId: string, tenantId: string): void | Promise<void>;
|
|
2223
|
+
};
|
|
2224
|
+
declare abstract class TenantStore<TTenant extends TenantRecordBase = TenantRecordBase> implements TenantUserRegistration {
|
|
2225
|
+
storeConfig?(config: TenantConfigStoreRequest<TTenant>): Promise<TenantConfigSourceInput>;
|
|
2226
|
+
abstract get(tenantId: string): Promise<StoredTenant<TTenant> | undefined>;
|
|
2227
|
+
abstract list(options?: TenantListOptions): Promise<ListResult<StoredTenant<TTenant>>>;
|
|
2228
|
+
abstract upsert(tenant: StoredTenant<TTenant>): Promise<StoredTenant<TTenant>>;
|
|
2229
|
+
abstract delete(tenantId: string): Promise<number>;
|
|
2230
|
+
abstract registerUserTenantId(userId: string, tenantId: string | null | undefined): void | Promise<void>;
|
|
2231
|
+
registerUserToTenant?(userId: string, tenantId: string): void | Promise<void>;
|
|
2232
|
+
abstract findTenantsByUser(user: User2): Promise<StoredTenant<TTenant>[]>;
|
|
2233
|
+
findTenantByUser(user: User2): Promise<StoredTenant<TTenant> | undefined>;
|
|
2234
|
+
}
|
|
2235
|
+
type TenantManagerStore<TTenant extends TenantRecordBase = TenantRecordBase> = Pick<TenantStoreWithES<TTenant>, "get" | "list" | "upsert" | "delete" | "getEs" | "findTenantByUser" | "findTenantsByUser"> & {
|
|
2236
|
+
storeConfig?: TenantStoreWithES<TTenant>["storeConfig"];
|
|
2568
2237
|
};
|
|
2569
|
-
type
|
|
2238
|
+
type InMemoryTenantStoreOptions<TTenant extends TenantRecordBase = TenantRecordBase> = TenantStoreWithESOptions<TTenant>;
|
|
2239
|
+
type TenantStoreWithRequiredEsOptions<TTenant extends TenantRecordBase = TenantRecordBase> = Omit<TenantStoreWithESOptions<TTenant>, "createEs"> & {
|
|
2240
|
+
createEs: TenantEsFactory<TTenant>;
|
|
2241
|
+
};
|
|
2242
|
+
type SingleTenantStoreOptions<TTenant extends TenantRecordBase = TenantRecordBase> = TenantStoreWithRequiredEsOptions<TTenant>;
|
|
2243
|
+
type MultiTenantStoreOptions<TTenant extends TenantRecordBase = TenantRecordBase> = TenantStoreWithRequiredEsOptions<TTenant>;
|
|
2244
|
+
declare abstract class TenantStoreWithEsCache<TTenant extends TenantRecordBase = TenantRecordBase> extends TenantStore<TTenant> {
|
|
2245
|
+
readonly ttl: number;
|
|
2246
|
+
private readonly createEs?;
|
|
2247
|
+
private readonly tenantEsMap;
|
|
2248
|
+
constructor(options: TenantStoreWithESOptions<TTenant>);
|
|
2249
|
+
registerUserTenantId(userId: string, tenantId: string | null | undefined): Promise<void>;
|
|
2250
|
+
registerUserToTenant(_userId: string, _tenantId: string): Promise<void>;
|
|
2251
|
+
protected prepareTenantForCreateEs(tenant: StoredTenant<TTenant>): StoredTenant<TTenant>;
|
|
2252
|
+
protected invalidateTenantEsCache(tenantId: string): void;
|
|
2253
|
+
getEs(tenantId: string): Promise<EnterpriseStandard | undefined>;
|
|
2254
|
+
getCachedTenantIds(): string[];
|
|
2255
|
+
}
|
|
2256
|
+
declare abstract class SingleTenantStore<TTenant extends TenantRecordBase = TenantRecordBase> extends TenantStoreWithEsCache<TTenant> {
|
|
2257
|
+
abstract findTenantByUser(user: User2): Promise<StoredTenant<TTenant> | undefined>;
|
|
2258
|
+
findTenantsByUser(user: User2): Promise<StoredTenant<TTenant>[]>;
|
|
2259
|
+
}
|
|
2260
|
+
declare abstract class MultiTenantStore<TTenant extends TenantRecordBase = TenantRecordBase> extends TenantStoreWithEsCache<TTenant> {}
|
|
2261
|
+
type TenantStoreWithES<TTenant extends TenantRecordBase = TenantRecordBase> = TenantStoreWithEsCache<TTenant>;
|
|
2262
|
+
type InMemorySingleTenantStoreOptions<TTenant extends TenantRecordBase = TenantRecordBase> = InMemoryTenantStoreOptions<TTenant>;
|
|
2263
|
+
type InMemoryMultiTenantStoreOptions<TTenant extends TenantRecordBase = TenantRecordBase> = InMemoryTenantStoreOptions<TTenant>;
|
|
2264
|
+
declare class InMemorySingleTenantStore<TTenant extends TenantRecordBase = TenantRecordBase> extends SingleTenantStore<TTenant> {
|
|
2265
|
+
private readonly store;
|
|
2266
|
+
constructor(options?: InMemorySingleTenantStoreOptions<TTenant>);
|
|
2267
|
+
get(tenantId: string): Promise<StoredTenant<TTenant> | undefined>;
|
|
2268
|
+
list(options?: TenantListOptions): Promise<ListResult<StoredTenant<TTenant>>>;
|
|
2269
|
+
upsert(tenant: StoredTenant<TTenant>): Promise<StoredTenant<TTenant>>;
|
|
2270
|
+
delete(tenantId: string): Promise<number>;
|
|
2271
|
+
registerUserTenantId(userId: string, tenantId: string | null | undefined): Promise<void>;
|
|
2272
|
+
findTenantByUser(user: User2): Promise<StoredTenant<TTenant> | undefined>;
|
|
2273
|
+
}
|
|
2274
|
+
declare class InMemoryMultiTenantStore<TTenant extends TenantRecordBase = TenantRecordBase> extends MultiTenantStore<TTenant> {
|
|
2275
|
+
private readonly store;
|
|
2276
|
+
constructor(options?: InMemoryMultiTenantStoreOptions<TTenant>);
|
|
2277
|
+
get(tenantId: string): Promise<StoredTenant<TTenant> | undefined>;
|
|
2278
|
+
list(options?: TenantListOptions): Promise<ListResult<StoredTenant<TTenant>>>;
|
|
2279
|
+
upsert(tenant: StoredTenant<TTenant>): Promise<StoredTenant<TTenant>>;
|
|
2280
|
+
delete(tenantId: string): Promise<number>;
|
|
2281
|
+
registerUserTenantId(userId: string, tenantId: string | null | undefined): Promise<void>;
|
|
2282
|
+
findTenantsByUser(user: User2): Promise<StoredTenant<TTenant>[]>;
|
|
2283
|
+
}
|
|
2284
|
+
declare function sendTenantWebhook(webhookUrl: string, payload: TenantWebhookPayload, log: Logger): Promise<void>;
|
|
2285
|
+
/**
|
|
2286
|
+
* Stored user data with required id and tracking metadata.
|
|
2287
|
+
*
|
|
2288
|
+
* Extends the SSO User type with:
|
|
2289
|
+
* - Required `id` (the `sub` claim from the IdP)
|
|
2290
|
+
* - Timestamps for tracking when users were first seen and last updated
|
|
2291
|
+
* - Optional custom extended data
|
|
2292
|
+
*
|
|
2293
|
+
* @template TExtended - Type-safe custom data that consumers can add to users
|
|
2294
|
+
*/
|
|
2295
|
+
type StoredUser<TExtended = object> = Omit<User2, "sso"> & {
|
|
2296
|
+
/**
|
|
2297
|
+
* Required unique identifier (the `sub` claim from the IdP).
|
|
2298
|
+
* This is the primary key for user storage.
|
|
2299
|
+
*/
|
|
2300
|
+
id?: string;
|
|
2301
|
+
/**
|
|
2302
|
+
* Optional Enterprise Standard tenant identifier for tenant-aware apps.
|
|
2303
|
+
* Built-in user stores can use this when registering HRD mappings.
|
|
2304
|
+
*/
|
|
2305
|
+
tenantId?: string;
|
|
2306
|
+
/**
|
|
2307
|
+
* Optional external identifier from the provisioning client.
|
|
2308
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2309
|
+
*/
|
|
2310
|
+
externalId?: string;
|
|
2311
|
+
/**
|
|
2312
|
+
* Optional SCIM display name distinct from the simple `name` field.
|
|
2313
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2314
|
+
*/
|
|
2315
|
+
displayName?: string;
|
|
2316
|
+
/**
|
|
2317
|
+
* Optional structured SCIM name.
|
|
2318
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2319
|
+
*/
|
|
2320
|
+
scimName?: Name;
|
|
2321
|
+
/**
|
|
2322
|
+
* Optional SCIM email collection.
|
|
2323
|
+
* The simple `email` field still stores the primary email for convenience.
|
|
2324
|
+
*/
|
|
2325
|
+
emails?: Email[];
|
|
2326
|
+
/**
|
|
2327
|
+
* Optional SCIM nickname.
|
|
2328
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2329
|
+
*/
|
|
2330
|
+
nickName?: string;
|
|
2331
|
+
/**
|
|
2332
|
+
* Optional SCIM account state.
|
|
2333
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2334
|
+
*/
|
|
2335
|
+
active?: boolean;
|
|
2336
|
+
/**
|
|
2337
|
+
* Optional SCIM job title.
|
|
2338
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2339
|
+
*/
|
|
2340
|
+
title?: string;
|
|
2341
|
+
/**
|
|
2342
|
+
* Optional SCIM preferred language.
|
|
2343
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2344
|
+
*/
|
|
2345
|
+
preferredLanguage?: string;
|
|
2346
|
+
/**
|
|
2347
|
+
* Optional SCIM locale.
|
|
2348
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2349
|
+
*/
|
|
2350
|
+
locale?: string;
|
|
2351
|
+
/**
|
|
2352
|
+
* Optional SCIM timezone.
|
|
2353
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2354
|
+
*/
|
|
2355
|
+
timezone?: string;
|
|
2356
|
+
/**
|
|
2357
|
+
* Optional SCIM phone numbers.
|
|
2358
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2359
|
+
*/
|
|
2360
|
+
phoneNumbers?: PhoneNumber[];
|
|
2361
|
+
/**
|
|
2362
|
+
* Optional SCIM instant messaging addresses.
|
|
2363
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2364
|
+
*/
|
|
2365
|
+
ims?: Array<{
|
|
2366
|
+
value: string;
|
|
2367
|
+
display?: string;
|
|
2368
|
+
type?: string;
|
|
2369
|
+
primary?: boolean;
|
|
2370
|
+
}>;
|
|
2371
|
+
/**
|
|
2372
|
+
* Optional SCIM photos.
|
|
2373
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2374
|
+
*/
|
|
2375
|
+
photos?: Photo[];
|
|
2376
|
+
/**
|
|
2377
|
+
* Optional SCIM addresses.
|
|
2378
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2379
|
+
*/
|
|
2380
|
+
addresses?: Address[];
|
|
2381
|
+
/**
|
|
2382
|
+
* Optional SCIM roles.
|
|
2383
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2384
|
+
*/
|
|
2385
|
+
roles?: Role[];
|
|
2386
|
+
/**
|
|
2387
|
+
* Optional SCIM groups.
|
|
2388
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2389
|
+
*/
|
|
2390
|
+
groups?: Group[];
|
|
2391
|
+
/**
|
|
2392
|
+
* Optional SCIM entitlements.
|
|
2393
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2394
|
+
*/
|
|
2395
|
+
entitlements?: Array<{
|
|
2396
|
+
value: string;
|
|
2397
|
+
display?: string;
|
|
2398
|
+
type?: string;
|
|
2399
|
+
primary?: boolean;
|
|
2400
|
+
}>;
|
|
2401
|
+
/**
|
|
2402
|
+
* Optional SCIM X.509 certificates.
|
|
2403
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2404
|
+
*/
|
|
2405
|
+
x509Certificates?: X509Certificate[];
|
|
2406
|
+
/**
|
|
2407
|
+
* Optional SCIM enterprise extension.
|
|
2408
|
+
* Mirrors `urn:ietf:params:scim:schemas:extension:enterprise:2.0:User`.
|
|
2409
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2410
|
+
*/
|
|
2411
|
+
scimEnterprise?: EnterpriseExtension;
|
|
2412
|
+
/**
|
|
2413
|
+
* Optional pass-through SCIM schema extensions keyed by full URN.
|
|
2414
|
+
* Use this when your validator accepts custom top-level SCIM extensions and
|
|
2415
|
+
* you want them to round-trip through a `UserStore` implementation.
|
|
2416
|
+
*/
|
|
2417
|
+
scimSchemaExtensions?: Record<string, unknown>;
|
|
2418
|
+
/**
|
|
2419
|
+
* Timestamp when the user was first stored.
|
|
2420
|
+
*/
|
|
2421
|
+
createdAt?: Date;
|
|
2422
|
+
/**
|
|
2423
|
+
* Timestamp when the user was last updated (e.g., on re-login).
|
|
2424
|
+
*/
|
|
2425
|
+
updatedAt?: Date;
|
|
2426
|
+
/**
|
|
2427
|
+
* Optional SSO envelope for stores that persist full auth profile data.
|
|
2428
|
+
* Simple app stores MAY omit this field.
|
|
2429
|
+
*/
|
|
2430
|
+
sso?: User2["sso"];
|
|
2431
|
+
} & TExtended;
|
|
2432
|
+
type UserStoreOptions = {
|
|
2433
|
+
tenantId: string;
|
|
2434
|
+
tenants: TenantStore;
|
|
2435
|
+
};
|
|
2436
|
+
/**
|
|
2437
|
+
* Abstract interface for user storage backends.
|
|
2438
|
+
*
|
|
2439
|
+
* Consumers can implement this interface to use different storage backends:
|
|
2440
|
+
* - In-memory (for development/testing)
|
|
2441
|
+
* - Redis (for production with fast lookups)
|
|
2442
|
+
* - Database (PostgreSQL, MySQL, etc.)
|
|
2443
|
+
*
|
|
2444
|
+
* @template TExtended - Type-safe custom data that consumers can add to users
|
|
2445
|
+
*
|
|
2446
|
+
* @example
|
|
2447
|
+
* ```typescript
|
|
2448
|
+
* // Custom user data
|
|
2449
|
+
* type MyUserData = {
|
|
2450
|
+
* department: string;
|
|
2451
|
+
* roles: string[];
|
|
2452
|
+
* };
|
|
2453
|
+
*
|
|
2454
|
+
* // Implement custom store
|
|
2455
|
+
* class RedisUserStore implements UserStore<MyUserData> {
|
|
2456
|
+
* async get(sub: string): Promise<StoredUser<MyUserData> | null> {
|
|
2457
|
+
* const data = await redis.get(`user:${sub}`);
|
|
2458
|
+
* return data ? JSON.parse(data) : null;
|
|
2459
|
+
* }
|
|
2460
|
+
* // ... other methods
|
|
2461
|
+
* }
|
|
2462
|
+
* ```
|
|
2463
|
+
*/
|
|
2464
|
+
interface UserStore<TExtended = object> {
|
|
2465
|
+
/**
|
|
2466
|
+
* Retrieve a user by their subject identifier (sub).
|
|
2467
|
+
*
|
|
2468
|
+
* This is the canonical lookup used by SDK flows whenever possible.
|
|
2469
|
+
* Other lookup methods (userName) are secondary convenience indexes.
|
|
2470
|
+
*
|
|
2471
|
+
* @param sub - The user's unique identifier from the IdP
|
|
2472
|
+
* @returns The user if found, undefined otherwise
|
|
2473
|
+
*/
|
|
2474
|
+
get(sub: string): Promise<StoredUser<TExtended> | undefined>;
|
|
2475
|
+
/**
|
|
2476
|
+
* Retrieve a user by their username.
|
|
2477
|
+
*
|
|
2478
|
+
* @param userName - The user's username
|
|
2479
|
+
* @returns The user if found, undefined otherwise
|
|
2480
|
+
*/
|
|
2481
|
+
getByUserName(userName: string): Promise<StoredUser<TExtended> | undefined>;
|
|
2482
|
+
/**
|
|
2483
|
+
* Create or update a user in the store.
|
|
2484
|
+
*
|
|
2485
|
+
* If a user with the same `id` (sub) exists, it will be updated.
|
|
2486
|
+
* Otherwise, a new user will be created.
|
|
2487
|
+
*
|
|
2488
|
+
* @param user - The user data to store
|
|
2489
|
+
*/
|
|
2490
|
+
upsert(user: StoredUser<TExtended>): Promise<StoredUser<TExtended>>;
|
|
2491
|
+
/**
|
|
2492
|
+
* Delete a user by their subject identifier (sub).
|
|
2493
|
+
*
|
|
2494
|
+
* @param sub - The user's unique identifier to delete
|
|
2495
|
+
*/
|
|
2496
|
+
delete(sub: string): Promise<number>;
|
|
2497
|
+
/**
|
|
2498
|
+
* List users in the store with optional pagination and sort.
|
|
2499
|
+
*
|
|
2500
|
+
* @param options - Optional start (0-based), limit (page size), and sort
|
|
2501
|
+
* @returns ListResult with total, count, items, size, page, pages
|
|
2502
|
+
*/
|
|
2503
|
+
list(options?: UserListOptions): Promise<ListResult<StoredUser<TExtended>>>;
|
|
2504
|
+
}
|
|
2505
|
+
/**
|
|
2506
|
+
* SCIM Error response structure
|
|
2507
|
+
*/
|
|
2508
|
+
interface ScimError {
|
|
2509
|
+
schemas: string[];
|
|
2510
|
+
status: string;
|
|
2511
|
+
scimType?: string;
|
|
2512
|
+
detail?: string;
|
|
2513
|
+
}
|
|
2514
|
+
/**
|
|
2515
|
+
* SCIM List Response for bulk operations
|
|
2516
|
+
*/
|
|
2517
|
+
interface ScimListResponse<T> {
|
|
2518
|
+
schemas: string[];
|
|
2519
|
+
totalResults: number;
|
|
2520
|
+
startIndex?: number;
|
|
2521
|
+
itemsPerPage?: number;
|
|
2522
|
+
Resources: T[];
|
|
2523
|
+
}
|
|
2524
|
+
/**
|
|
2525
|
+
* Result of a SCIM operation
|
|
2526
|
+
*/
|
|
2527
|
+
interface ScimResult<T> {
|
|
2528
|
+
success: boolean;
|
|
2529
|
+
data?: T;
|
|
2530
|
+
error?: ScimError;
|
|
2531
|
+
status: number;
|
|
2532
|
+
}
|
|
2533
|
+
/**
|
|
2534
|
+
* Handler configuration for IAM
|
|
2535
|
+
*/
|
|
2536
|
+
interface IAMHandlerConfig {
|
|
2537
|
+
/**
|
|
2538
|
+
* Base path for the SCIM Users endpoints (e.g., '/api/iam/Users')
|
|
2539
|
+
*/
|
|
2540
|
+
usersUrl?: string;
|
|
2541
|
+
/**
|
|
2542
|
+
* Base path for the SCIM Groups endpoints (e.g., '/api/iam/Groups')
|
|
2543
|
+
*/
|
|
2544
|
+
groupsUrl?: string;
|
|
2545
|
+
/**
|
|
2546
|
+
* Handler overrides for SCIM discovery endpoints (e.g., '/api/iam/ServiceProviderConfig')
|
|
2547
|
+
*/
|
|
2548
|
+
discovery?: IAMDiscoveryHandlerConfig;
|
|
2549
|
+
}
|
|
2550
|
+
/**
|
|
2551
|
+
* IAM configuration
|
|
2552
|
+
*
|
|
2553
|
+
* - If `url` is provided, groups_outbound is enabled (app calls external IAM)
|
|
2554
|
+
* - If `groupStore` is provided, groups_inbound is enabled (external IAM calls app)
|
|
2555
|
+
* - If `userStore` is provided, users_inbound is enabled (external IAM calls app)
|
|
2556
|
+
*/
|
|
2557
|
+
type IAMConfig = {
|
|
2558
|
+
/**
|
|
2559
|
+
* Base URL of the external SCIM endpoint (e.g., https://sailpoint.example.com/scim/v2)
|
|
2560
|
+
* If provided, enables outbound SCIM operations (app -> external IAM)
|
|
2561
|
+
*/
|
|
2562
|
+
url?: string;
|
|
2563
|
+
/**
|
|
2564
|
+
* Store for inbound user provisioning from external IAM providers.
|
|
2565
|
+
* When configured, the app can receive user CRUD operations via SCIM.
|
|
2566
|
+
*/
|
|
2567
|
+
userStore?: UserStore;
|
|
2568
|
+
/**
|
|
2569
|
+
* Optional inbound user mapping hooks for SCIM provisioning.
|
|
2570
|
+
* Use these when the default StoredUser <-> SCIM mapping is not a direct fit
|
|
2571
|
+
* for your application's database model.
|
|
2572
|
+
*/
|
|
2573
|
+
inboundUsers?: IAMInboundUsersConfig;
|
|
2574
|
+
/**
|
|
2575
|
+
* Store for inbound group provisioning from external IAM providers.
|
|
2576
|
+
* When configured, enables groups_inbound (external IAM -> app).
|
|
2577
|
+
*/
|
|
2578
|
+
groupStore?: GroupStore;
|
|
2579
|
+
/**
|
|
2580
|
+
* Optional handler defaults. These are merged with per-call overrides in
|
|
2581
|
+
* `iam.handler`, with per-call values taking precedence.
|
|
2582
|
+
*/
|
|
2583
|
+
usersUrl?: string;
|
|
2584
|
+
groupsUrl?: string;
|
|
2585
|
+
discovery?: IAMDiscoveryConfig;
|
|
2586
|
+
};
|
|
2587
|
+
type IAMValidators = {
|
|
2588
|
+
user: StandardSchemaV17<unknown, User>;
|
|
2589
|
+
group: StandardSchemaV17<unknown, GroupResource>;
|
|
2590
|
+
};
|
|
2591
|
+
interface IAMInboundUserContext {
|
|
2592
|
+
/**
|
|
2593
|
+
* Current stored user when replacing or patching an existing resource.
|
|
2594
|
+
*/
|
|
2595
|
+
existing?: StoredUser;
|
|
2596
|
+
/**
|
|
2597
|
+
* Operation mode for the inbound mapper.
|
|
2598
|
+
*/
|
|
2599
|
+
mode: "create" | "replace" | "patch";
|
|
2600
|
+
}
|
|
2601
|
+
interface IAMInboundUsersConfig {
|
|
2602
|
+
/**
|
|
2603
|
+
* Replace the default validated SCIM -> StoredUser mapper.
|
|
2604
|
+
*/
|
|
2605
|
+
mapValidatedScimToStoredUser?: (validated: User, context: IAMInboundUserContext) => StoredUser | Promise<StoredUser>;
|
|
2606
|
+
/**
|
|
2607
|
+
* Replace the default StoredUser -> SCIM response mapper.
|
|
2608
|
+
*/
|
|
2609
|
+
mapStoredUserToScim?: (stored: StoredUser) => User | Promise<User>;
|
|
2610
|
+
}
|
|
2611
|
+
interface ScimAuthenticationScheme {
|
|
2612
|
+
type: string;
|
|
2613
|
+
name: string;
|
|
2614
|
+
description?: string;
|
|
2615
|
+
specUri?: string;
|
|
2616
|
+
documentationUri?: string;
|
|
2617
|
+
primary?: boolean;
|
|
2618
|
+
}
|
|
2619
|
+
interface ScimServiceProviderConfig {
|
|
2620
|
+
schemas: string[];
|
|
2621
|
+
documentationUri?: string;
|
|
2622
|
+
patch: {
|
|
2623
|
+
supported: boolean;
|
|
2624
|
+
};
|
|
2625
|
+
bulk: {
|
|
2626
|
+
supported: boolean;
|
|
2627
|
+
maxOperations?: number;
|
|
2628
|
+
maxPayloadSize?: number;
|
|
2629
|
+
};
|
|
2630
|
+
filter: {
|
|
2631
|
+
supported: boolean;
|
|
2632
|
+
maxResults?: number;
|
|
2633
|
+
};
|
|
2634
|
+
changePassword: {
|
|
2635
|
+
supported: boolean;
|
|
2636
|
+
};
|
|
2637
|
+
sort: {
|
|
2638
|
+
supported: boolean;
|
|
2639
|
+
};
|
|
2640
|
+
etag: {
|
|
2641
|
+
supported: boolean;
|
|
2642
|
+
};
|
|
2643
|
+
authenticationSchemes?: ScimAuthenticationScheme[];
|
|
2644
|
+
meta?: {
|
|
2645
|
+
resourceType?: string;
|
|
2646
|
+
location?: string;
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
interface ScimResourceTypeSchemaExtension {
|
|
2650
|
+
schema: string;
|
|
2651
|
+
required: boolean;
|
|
2652
|
+
}
|
|
2653
|
+
interface ScimResourceType {
|
|
2654
|
+
schemas: string[];
|
|
2655
|
+
id: string;
|
|
2656
|
+
name: string;
|
|
2657
|
+
description?: string;
|
|
2658
|
+
endpoint: string;
|
|
2659
|
+
schema: string;
|
|
2660
|
+
schemaExtensions?: ScimResourceTypeSchemaExtension[];
|
|
2661
|
+
meta?: {
|
|
2662
|
+
resourceType?: string;
|
|
2663
|
+
location?: string;
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
interface ScimSchemaAttributeDefinition {
|
|
2667
|
+
name: string;
|
|
2668
|
+
type: "string" | "boolean" | "complex" | "reference" | "dateTime";
|
|
2669
|
+
multiValued: boolean;
|
|
2670
|
+
description?: string;
|
|
2671
|
+
required?: boolean;
|
|
2672
|
+
caseExact?: boolean;
|
|
2673
|
+
mutability?: "readOnly" | "readWrite" | "immutable" | "writeOnly";
|
|
2674
|
+
returned?: "always" | "never" | "default" | "request";
|
|
2675
|
+
uniqueness?: "none" | "server" | "global";
|
|
2676
|
+
referenceTypes?: string[];
|
|
2677
|
+
subAttributes?: ScimSchemaAttributeDefinition[];
|
|
2678
|
+
}
|
|
2679
|
+
interface ScimSchemaDefinition {
|
|
2680
|
+
schemas: string[];
|
|
2681
|
+
id: string;
|
|
2682
|
+
name: string;
|
|
2683
|
+
description?: string;
|
|
2684
|
+
attributes: ScimSchemaAttributeDefinition[];
|
|
2685
|
+
meta?: {
|
|
2686
|
+
resourceType?: string;
|
|
2687
|
+
location?: string;
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
interface IAMDiscoveryContext {
|
|
2691
|
+
request: Request;
|
|
2692
|
+
basePath: string;
|
|
2693
|
+
usersUrl: string;
|
|
2694
|
+
groupsUrl: string;
|
|
2695
|
+
supportsUsers: boolean;
|
|
2696
|
+
supportsGroups: boolean;
|
|
2697
|
+
}
|
|
2698
|
+
interface IAMDiscoveryConfig {
|
|
2699
|
+
/**
|
|
2700
|
+
* Public IAM base path used for SCIM discovery. When omitted, the SDK derives
|
|
2701
|
+
* it from `usersUrl` or `groupsUrl`.
|
|
2702
|
+
*/
|
|
2703
|
+
basePath?: string;
|
|
2570
2704
|
/**
|
|
2571
|
-
*
|
|
2572
|
-
* Default is forever; set to 0 to recreate ES on every getEs() call.
|
|
2705
|
+
* Optional documentation URI advertised in ServiceProviderConfig.
|
|
2573
2706
|
*/
|
|
2574
|
-
|
|
2707
|
+
documentationUri?: string;
|
|
2575
2708
|
/**
|
|
2576
|
-
*
|
|
2577
|
-
* If omitted, getEs() throws.
|
|
2709
|
+
* Override the default ServiceProviderConfig response.
|
|
2578
2710
|
*/
|
|
2579
|
-
|
|
2580
|
-
};
|
|
2581
|
-
type TenantMetadataRecord = Omit<TenantBaseRecord, "tenantId" | "config" | "configSource" | "status" | "createdAt" | "updatedAt">;
|
|
2582
|
-
type TenantExtendedUpsertFields<TExtended extends object> = string extends keyof TExtended ? unknown : Partial<TExtended>;
|
|
2583
|
-
type TenantStoreUpsertRecord<TExtended extends object = Record<string, never>> = Pick<TenantBaseRecord, "tenantId" | "configSource"> & Partial<TenantMetadataRecord> & Partial<Pick<TenantBaseRecord, "status" | "createdAt" | "updatedAt">> & TenantExtendedUpsertFields<TExtended>;
|
|
2584
|
-
type TenantUserRegistration<TMode extends UserMode = UserMode> = {
|
|
2585
|
-
userMode: TMode;
|
|
2586
|
-
registerUserTenantId?(userId: string, tenantId: string | null | undefined): void | Promise<void>;
|
|
2587
|
-
};
|
|
2588
|
-
type TenantStoreBase<
|
|
2589
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2590
|
-
TExtended extends object = Record<string, never>
|
|
2591
|
-
> = TenantUserRegistration<TMode> & {
|
|
2592
|
-
storeConfig(config: TenantConfigStoreRequest<TExtended>): Promise<TenantConfigSourceInput>;
|
|
2593
|
-
get(tenantId: string): Promise<StoredTenant<TExtended> | null>;
|
|
2594
|
-
list(options?: TenantListOptions): Promise<ListResult<StoredTenant<TExtended>>>;
|
|
2595
|
-
upsert(tenant: TenantStoreUpsertRecord<TExtended>): Promise<StoredTenant<TExtended>>;
|
|
2596
|
-
delete(tenantId: string): Promise<void>;
|
|
2597
|
-
};
|
|
2598
|
-
type TenantLookupMethods<
|
|
2599
|
-
TMode extends UserMode,
|
|
2600
|
-
TExtended extends object
|
|
2601
|
-
> = TMode extends "singleTenantOnly" ? {
|
|
2602
|
-
findTenantByUserId(userId: string): Promise<StoredTenant<TExtended> | null>;
|
|
2603
|
-
findTenantsByUserIds?: never;
|
|
2604
|
-
} : {
|
|
2605
|
-
findTenantByUserId?: never;
|
|
2606
|
-
/**
|
|
2607
|
-
* Batched HRD lookup. Resolves the tenants associated with each user id and
|
|
2608
|
-
* returns a `Map` keyed by the original `userId`. Implementations MUST handle
|
|
2609
|
-
* up to {@link MAX_TENANTS_BATCH_USER_IDS} ids per call (matches
|
|
2610
|
-
* `MAX_SSO_DISCOVERY_CANDIDATES`); larger inputs are a programming error.
|
|
2611
|
-
*
|
|
2612
|
-
* **Per-engine guidance for store authors:**
|
|
2613
|
-
*
|
|
2614
|
-
* - Modern engines (PostgreSQL, MySQL/MariaDB, SQLite, MS SQL Server): a single
|
|
2615
|
-
* parameterized `WHERE user_id IN (?, ?, ...)` is fine at this batch size.
|
|
2616
|
-
* - PostgreSQL specifically: prefer `WHERE user_id = ANY($1::text[])` to keep
|
|
2617
|
-
* one prepared-statement plan in cache.
|
|
2618
|
-
* - Oracle (≤19c with the 1000-element `IN`-list cap): 32 is well within range;
|
|
2619
|
-
* if you ever raise this cap upstream, chunk by 1000.
|
|
2620
|
-
* - IBM DB2 / strict engines with low `IN`-list or 32 KB statement-text limits:
|
|
2621
|
-
* safe at 32, but if the cap is raised the implementation MUST chunk or fall
|
|
2622
|
-
* back to N+1 lookups internally — never throw and never return partial
|
|
2623
|
-
* results silently.
|
|
2624
|
-
* - Always parameterize the `IN` value type to match the `user_id` column
|
|
2625
|
-
* (text vs numeric) to avoid implicit-conversion full table scans.
|
|
2626
|
-
*/
|
|
2627
|
-
findTenantsByUserIds(userIds: string[]): Promise<Map<string, StoredTenant<TExtended>[]>>;
|
|
2628
|
-
};
|
|
2629
|
-
/**
|
|
2630
|
-
* Hard upper bound on the number of user ids accepted by
|
|
2631
|
-
* {@link TenantStore.findTenantsByUserIds} (and the public
|
|
2632
|
-
* `getTenantsForUserIds`/`getSSOUsers` helpers). Tied to the SSO discovery cap so
|
|
2633
|
-
* a single cookie-first browser request never exceeds this value.
|
|
2634
|
-
*
|
|
2635
|
-
* Treat this as an upper bound, not a target. See per-engine implementation
|
|
2636
|
-
* guidance on `findTenantsByUserIds`.
|
|
2637
|
-
*/
|
|
2638
|
-
declare const MAX_TENANTS_BATCH_USER_IDS = 32;
|
|
2639
|
-
type TenantStore<
|
|
2640
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2641
|
-
TExtended extends object = Record<string, never>
|
|
2642
|
-
> = TenantStoreBase<TMode, TExtended> & TenantLookupMethods<TMode, TExtended>;
|
|
2643
|
-
type TenantStoreWithES<
|
|
2644
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2645
|
-
TExtended extends object = Record<string, never>
|
|
2646
|
-
> = TenantStore<TMode, TExtended> & {
|
|
2647
|
-
ttl: number;
|
|
2648
|
-
getEs(tenantId: string): Promise<EnterpriseStandard | null>;
|
|
2649
|
-
getCachedTenantIds(): string[];
|
|
2650
|
-
};
|
|
2651
|
-
type InMemoryTenantStoreOptions<
|
|
2652
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2653
|
-
TExtended extends object = Record<string, never>
|
|
2654
|
-
> = TenantStoreWithESOptions<TExtended> & {
|
|
2655
|
-
userMode: TMode;
|
|
2656
|
-
};
|
|
2657
|
-
declare class InMemoryTenantStore<
|
|
2658
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2659
|
-
TExtended extends object = Record<string, never>
|
|
2660
|
-
> {
|
|
2661
|
-
private tenants;
|
|
2662
|
-
private tenantEsMap;
|
|
2663
|
-
private userTenantIds;
|
|
2664
|
-
readonly ttl: number;
|
|
2665
|
-
readonly userMode: TMode;
|
|
2666
|
-
private readonly createEs?;
|
|
2667
|
-
readonly findTenantByUserId: TMode extends "singleTenantOnly" ? (userId: string) => Promise<StoredTenant<TExtended> | null> : never;
|
|
2668
|
-
readonly findTenantsByUserIds: TMode extends "multipleTenantsPerUser" ? (userIds: string[]) => Promise<Map<string, StoredTenant<TExtended>[]>> : never;
|
|
2669
|
-
constructor(options: InMemoryTenantStoreOptions<TMode, TExtended>);
|
|
2670
|
-
get(tenantId: string): Promise<StoredTenant<TExtended> | null>;
|
|
2671
|
-
list(options?: TenantListOptions): Promise<ListResult<StoredTenant<TExtended>>>;
|
|
2672
|
-
upsert(tenant: TenantStoreUpsertRecord<TExtended>): Promise<StoredTenant<TExtended>>;
|
|
2673
|
-
delete(tenantId: string): Promise<void>;
|
|
2674
|
-
getEs(tenantId: string): Promise<EnterpriseStandard>;
|
|
2675
|
-
getCachedTenantIds(): string[];
|
|
2676
|
-
registerUserTenantId(userId: string, tenantId: string | null | undefined): Promise<void>;
|
|
2677
|
-
private findSingleTenantByUserId;
|
|
2678
|
-
private findMultipleTenantsByUserIds;
|
|
2679
|
-
private resolveTenantsByUserId;
|
|
2680
|
-
}
|
|
2681
|
-
declare function sendTenantWebhook(webhookUrl: string, payload: TenantWebhookPayload, log: Logger): Promise<void>;
|
|
2682
|
-
/**
|
|
2683
|
-
* Magic link data stored in the store.
|
|
2684
|
-
*
|
|
2685
|
-
* @template TExtended - Type-safe custom data that consumers can add to magic links
|
|
2686
|
-
*/
|
|
2687
|
-
type MagicLink<TExtended = object> = {
|
|
2711
|
+
buildServiceProviderConfig?: (context: IAMDiscoveryContext, defaults: ScimServiceProviderConfig) => ScimServiceProviderConfig | Promise<ScimServiceProviderConfig>;
|
|
2688
2712
|
/**
|
|
2689
|
-
*
|
|
2713
|
+
* Override the default ResourceTypes response.
|
|
2690
2714
|
*/
|
|
2691
|
-
|
|
2715
|
+
buildResourceTypes?: (context: IAMDiscoveryContext, defaults: ScimResourceType[]) => ScimResourceType[] | Promise<ScimResourceType[]>;
|
|
2692
2716
|
/**
|
|
2693
|
-
*
|
|
2717
|
+
* Override the default Schemas response.
|
|
2694
2718
|
*/
|
|
2695
|
-
|
|
2719
|
+
buildSchemas?: (context: IAMDiscoveryContext, defaults: ScimSchemaDefinition[]) => ScimSchemaDefinition[] | Promise<ScimSchemaDefinition[]>;
|
|
2720
|
+
}
|
|
2721
|
+
/**
|
|
2722
|
+
* Options for creating a user
|
|
2723
|
+
*/
|
|
2724
|
+
interface CreateUserOptions {
|
|
2696
2725
|
/**
|
|
2697
|
-
*
|
|
2726
|
+
* External identifier for the user
|
|
2698
2727
|
*/
|
|
2699
|
-
|
|
2728
|
+
externalId?: string;
|
|
2729
|
+
}
|
|
2730
|
+
/**
|
|
2731
|
+
* Options for creating a group
|
|
2732
|
+
*/
|
|
2733
|
+
interface CreateGroupOptions {
|
|
2700
2734
|
/**
|
|
2701
|
-
*
|
|
2735
|
+
* External identifier for the group
|
|
2702
2736
|
*/
|
|
2703
|
-
|
|
2737
|
+
externalId?: string;
|
|
2704
2738
|
/**
|
|
2705
|
-
*
|
|
2739
|
+
* Initial members to add to the group
|
|
2706
2740
|
*/
|
|
2707
|
-
[
|
|
2708
|
-
}
|
|
2741
|
+
members?: GroupMember[];
|
|
2742
|
+
}
|
|
2709
2743
|
/**
|
|
2710
|
-
*
|
|
2711
|
-
*
|
|
2712
|
-
* Consumers can implement this interface to use different storage backends:
|
|
2713
|
-
* - In-memory (for development/testing)
|
|
2714
|
-
* - Redis (for production with fast lookups and automatic expiration)
|
|
2715
|
-
* - Database (PostgreSQL, MySQL, etc.)
|
|
2716
|
-
*
|
|
2717
|
-
* @template TExtended - Type-safe custom data that consumers can add to magic links
|
|
2718
|
-
*
|
|
2719
|
-
* @example
|
|
2720
|
-
* ```typescript
|
|
2721
|
-
* // Custom magic link data
|
|
2722
|
-
* type MyMagicLinkData = {
|
|
2723
|
-
* source: string;
|
|
2724
|
-
* metadata: Record<string, unknown>;
|
|
2725
|
-
* };
|
|
2726
|
-
*
|
|
2727
|
-
* // Implement custom store
|
|
2728
|
-
* class RedisMagicLinkStore implements MagicLinkStore<MyMagicLinkData> {
|
|
2729
|
-
* async create(token: string, user: BaseUser, expiresAt: Date): Promise<void> {
|
|
2730
|
-
* const magicLink: MagicLink<MyMagicLinkData> = {
|
|
2731
|
-
* token,
|
|
2732
|
-
* user,
|
|
2733
|
-
* createdAt: new Date(),
|
|
2734
|
-
* expiresAt,
|
|
2735
|
-
* source: 'api',
|
|
2736
|
-
* metadata: {},
|
|
2737
|
-
* };
|
|
2738
|
-
* const ttl = Math.floor((expiresAt.getTime() - Date.now()) / 1000);
|
|
2739
|
-
* await redis.setex(`magic-link:${token}`, ttl, JSON.stringify(magicLink));
|
|
2740
|
-
* }
|
|
2741
|
-
* // ... other methods
|
|
2742
|
-
* }
|
|
2743
|
-
* ```
|
|
2744
|
+
* Handler configuration for groups_inbound
|
|
2744
2745
|
*/
|
|
2745
|
-
interface
|
|
2746
|
+
interface GroupsInboundHandlerConfig {
|
|
2746
2747
|
/**
|
|
2747
|
-
*
|
|
2748
|
-
*
|
|
2749
|
-
* @param token - The magic link token (unique identifier)
|
|
2750
|
-
* @param user - The user information to associate with this magic link
|
|
2751
|
-
* @param expiresAt - When the magic link expires
|
|
2752
|
-
* @throws Error if magic link with same token already exists
|
|
2748
|
+
* Base path for the SCIM Groups endpoints (e.g., '/api/iam/Groups')
|
|
2753
2749
|
*/
|
|
2754
|
-
|
|
2750
|
+
basePath?: string;
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Handler configuration for users_inbound
|
|
2754
|
+
*/
|
|
2755
|
+
interface UsersInboundHandlerConfig {
|
|
2755
2756
|
/**
|
|
2756
|
-
*
|
|
2757
|
-
*
|
|
2758
|
-
* @param token - The magic link token
|
|
2759
|
-
* @returns The magic link if found and not expired, null otherwise
|
|
2757
|
+
* Base path for the SCIM Users endpoints (e.g., '/api/iam/Users')
|
|
2760
2758
|
*/
|
|
2761
|
-
|
|
2759
|
+
basePath?: string;
|
|
2760
|
+
}
|
|
2761
|
+
interface IAMDiscoveryHandlerConfig {
|
|
2762
2762
|
/**
|
|
2763
|
-
*
|
|
2764
|
-
*
|
|
2765
|
-
* Used after a magic link has been consumed (one-time use).
|
|
2766
|
-
*
|
|
2767
|
-
* @param token - The magic link token to delete
|
|
2763
|
+
* Base path for the IAM discovery endpoints (e.g., '/api/iam')
|
|
2768
2764
|
*/
|
|
2769
|
-
|
|
2765
|
+
basePath?: string;
|
|
2770
2766
|
}
|
|
2771
|
-
type ConfigSourceType = "vault" | "azure" | "aws" | "gcp";
|
|
2772
|
-
type ESValidators = {
|
|
2773
|
-
sso: SSOValidators;
|
|
2774
|
-
iam: IAMValidators;
|
|
2775
|
-
workload: WorkloadValidators;
|
|
2776
|
-
ciam: CIAMValidators;
|
|
2777
|
-
secrets?: SecretsValidators;
|
|
2778
|
-
};
|
|
2779
|
-
type ApplicationValidators = ESValidators & {
|
|
2780
|
-
tenant: TenantValidators;
|
|
2781
|
-
};
|
|
2782
2767
|
/**
|
|
2783
|
-
*
|
|
2784
|
-
*
|
|
2785
|
-
* argument to enterpriseStandard(source, config).
|
|
2786
|
-
* Set a module to `null` to explicitly disable it; then the corresponding property on the
|
|
2787
|
-
* EnterpriseStandard instance is typed as `never`. Omit a module to allow it to be supplied
|
|
2788
|
-
* from ConfigSource / adaptive (typed as the module type, non-optional).
|
|
2768
|
+
* Groups Outbound extension - for creating groups in external IAM providers.
|
|
2769
|
+
* Enabled when `url` is configured in IAMConfig.
|
|
2789
2770
|
*/
|
|
2790
|
-
type
|
|
2791
|
-
/**
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
validators: ESValidators;
|
|
2771
|
+
type IAMGroupsOutbound = {
|
|
2772
|
+
/**
|
|
2773
|
+
* Create a new group in the external IAM provider
|
|
2774
|
+
* @param displayName - The display name for the group
|
|
2775
|
+
* @param options - Optional configuration for the group creation
|
|
2776
|
+
* @returns The created group resource from the provider
|
|
2777
|
+
*/
|
|
2778
|
+
createGroup: (displayName: string, options?: CreateGroupOptions) => Promise<ScimResult<GroupResource>>;
|
|
2799
2779
|
};
|
|
2800
2780
|
/**
|
|
2801
|
-
*
|
|
2802
|
-
*
|
|
2803
|
-
* both sources at runtime. Use this type when referring to the effective/merged config.
|
|
2781
|
+
* Groups Inbound extension - for receiving group provisioning from external IAM providers.
|
|
2782
|
+
* Enabled when `groupStore` is configured in IAMConfig.
|
|
2804
2783
|
*/
|
|
2805
|
-
type
|
|
2784
|
+
type IAMGroupsInbound = {
|
|
2785
|
+
/**
|
|
2786
|
+
* Handle inbound SCIM requests for group management.
|
|
2787
|
+
* Routes: GET/POST /Groups, GET/PUT/PATCH/DELETE /Groups/:id
|
|
2788
|
+
*/
|
|
2789
|
+
handler: (request: Request, config?: GroupsInboundHandlerConfig) => Promise<Response>;
|
|
2790
|
+
};
|
|
2806
2791
|
/**
|
|
2807
|
-
*
|
|
2792
|
+
* Users Inbound extension - for receiving user provisioning from external IAM providers.
|
|
2793
|
+
* Enabled when `userStore` is configured in IAMConfig.
|
|
2808
2794
|
*/
|
|
2809
|
-
type
|
|
2810
|
-
/** Optional app/tenant identifier for this ESA (e.g. from vault path or config). */
|
|
2811
|
-
tenantId?: string;
|
|
2812
|
-
sso?: SSOConfig;
|
|
2813
|
-
iam?: IAMConfig;
|
|
2795
|
+
type IAMUsersInbound = {
|
|
2814
2796
|
/**
|
|
2815
|
-
*
|
|
2816
|
-
*
|
|
2817
|
-
* Legacy: { default: { jwksUri, issuer }, TNT_*: { ... } } (flat map).
|
|
2818
|
-
* TODO: Let's see if we can do some clean inference here!!!
|
|
2797
|
+
* Handle inbound SCIM requests for user management.
|
|
2798
|
+
* Routes: GET/POST /Users, GET/PUT/PATCH/DELETE /Users/:id
|
|
2819
2799
|
*/
|
|
2820
|
-
|
|
2821
|
-
/** Optional named secrets-source configs available to this ESA instance. */
|
|
2822
|
-
secrets?: SecretsModuleConfig;
|
|
2823
|
-
ciam?: CIAMConfig;
|
|
2800
|
+
handler: (request: Request, config?: UsersInboundHandlerConfig) => Promise<Response>;
|
|
2824
2801
|
};
|
|
2825
2802
|
/**
|
|
2826
|
-
*
|
|
2803
|
+
* Core IAM service interface.
|
|
2804
|
+
*
|
|
2805
|
+
* - Core functions are user-related (outbound to external IAM)
|
|
2806
|
+
* - `groups_outbound` is available when `url` is configured
|
|
2807
|
+
* - `groups_inbound` is available when `groupStore` is configured
|
|
2808
|
+
* - `users_inbound` is available when `userStore` is configured
|
|
2827
2809
|
*/
|
|
2828
|
-
type
|
|
2829
|
-
sessionStore?: SessionStore<unknown>;
|
|
2830
|
-
userStore?: UserStore<unknown>;
|
|
2831
|
-
groupStore?: GroupStore<unknown>;
|
|
2832
|
-
magicLinkStore?: MagicLinkStore<unknown>;
|
|
2833
|
-
workloadTokenStore?: WorkloadTokenStore;
|
|
2834
|
-
};
|
|
2835
|
-
type ModifiableFrameworkConfig = FrameworkConfig & {
|
|
2836
|
-
setStores(stores: FrameworkStores): void;
|
|
2837
|
-
};
|
|
2838
|
-
/** Return type from the beforeChange hook passed to enterpriseStandard(). */
|
|
2839
|
-
type ESConfigChangeResult = {
|
|
2840
|
-
config?: RemoteConfig;
|
|
2841
|
-
frameworkConfig?: FrameworkConfig;
|
|
2842
|
-
};
|
|
2843
|
-
/** beforeChange callback invoked on every config application (initial load and updates). */
|
|
2844
|
-
type ESConfigChangeCallback = (config: RemoteConfig, frameworkConfig: ModifiableFrameworkConfig, oldConfig: RemoteConfig | undefined) => ESConfigChangeResult | void;
|
|
2845
|
-
type ConfigSource = {
|
|
2846
|
-
load(): Promise<RemoteConfig>;
|
|
2810
|
+
type IAM = IAMConfig & {
|
|
2847
2811
|
/**
|
|
2848
|
-
*
|
|
2849
|
-
*
|
|
2850
|
-
* @return an unsubscribe/cleanup function.
|
|
2812
|
+
* Create a new user/account in the external IAM provider
|
|
2813
|
+
* Only available when `url` is configured.
|
|
2851
2814
|
*/
|
|
2852
|
-
|
|
2815
|
+
createUser?: (user: User, options?: CreateUserOptions) => Promise<ScimResult<User>>;
|
|
2853
2816
|
/**
|
|
2854
|
-
*
|
|
2855
|
-
* For vault-backed sources this is the vault used to read RemoteConfig.
|
|
2817
|
+
* Get the configured external SCIM base URL
|
|
2856
2818
|
*/
|
|
2857
|
-
|
|
2819
|
+
getBaseUrl: () => string | undefined;
|
|
2858
2820
|
/**
|
|
2859
|
-
*
|
|
2860
|
-
*
|
|
2821
|
+
* Groups Outbound extension - create groups in external IAM provider.
|
|
2822
|
+
* Available when `url` is configured in IAMConfig.
|
|
2861
2823
|
*/
|
|
2862
|
-
|
|
2824
|
+
groups_outbound?: IAMGroupsOutbound;
|
|
2863
2825
|
/**
|
|
2864
|
-
*
|
|
2865
|
-
*
|
|
2826
|
+
* Groups Inbound extension - receive group provisioning from external IAM.
|
|
2827
|
+
* Available when `groupStore` is configured in IAMConfig.
|
|
2866
2828
|
*/
|
|
2867
|
-
|
|
2829
|
+
groups_inbound?: IAMGroupsInbound;
|
|
2830
|
+
/**
|
|
2831
|
+
* Users Inbound extension - receive user provisioning from external IAM.
|
|
2832
|
+
* Available when `userStore` is configured in IAMConfig.
|
|
2833
|
+
*/
|
|
2834
|
+
users_inbound?: IAMUsersInbound;
|
|
2835
|
+
/**
|
|
2836
|
+
* Framework-agnostic request handler for the IAM module.
|
|
2837
|
+
* Routes to discovery, users_inbound, or groups_inbound based on the request path.
|
|
2838
|
+
*/
|
|
2839
|
+
handler: (request: Request, config?: IAMHandlerConfig) => Promise<Response>;
|
|
2868
2840
|
};
|
|
2869
2841
|
/**
|
|
2870
2842
|
* Serializes a FrameworkConfig (or ESConfig) to a JSON-serializable object.
|
|
@@ -3533,4 +3505,4 @@ declare function deepEqualPlain(a: unknown, b: unknown): boolean;
|
|
|
3533
3505
|
* @returns A promise that resolves when the service is ready.
|
|
3534
3506
|
*/
|
|
3535
3507
|
declare function waitOn(url: string, test?: (resp: Response) => boolean | Promise<boolean>, pingInterval?: number, warnInterval?: number, timeout?: number): Promise<void>;
|
|
3536
|
-
export { workloadTokenResponseSchema, withValidate, waitOn, version, validationFailureResponse, userSchema, tokenResponseSchema, stripJsonComments, silentLogger, setActiveSession, serializeESConfig, sendTenantWebhook, parseJsonc, oidcCallbackSchema, normalizeTenantRoutingStrategy, normalizeTenantPathNamespace, must, mergeConfig, matchTenantPath, listSsoClientIdsFromCookies, list, jwtAssertionClaimsSchema, infoLogger, idTokenClaimsSchema, groupResourceSchema, getActiveSession, findTenantFromStateParam, defaultLogger, deepEqualPlain, decodeUser, debugLogger, consoleLogger, clearActiveSession, claimsToUser, buildTenantPath, X509Certificate, WorkloadValidators, WorkloadTokenStore, WorkloadTokenResponse, WorkloadIncomingOutgoing, WorkloadIdentity, WorkloadConfigMap, WorkloadConfig, WorkloadClient, Workload, VaultWebSocketSecretsConfig, VaultWebSocketAuthHeader, VaultSecretsConfig, VaultLfvSecretsConfig, ValidateResult, UsersInboundHandlerConfig, UserStore, UserSortOptions, UserSortField,
|
|
3508
|
+
export { workloadTokenResponseSchema, withValidate, waitOn, version, validationFailureResponse, userSchema, tokenResponseSchema, stripJsonComments, silentLogger, setActiveSession, serializeESConfig, sendTenantWebhook, parseJsonc, oidcCallbackSchema, normalizeTenantRoutingStrategy, normalizeTenantPathNamespace, must, mergeConfig, matchTenantPath, listSsoClientIdsFromCookies, list, jwtAssertionClaimsSchema, infoLogger, idTokenClaimsSchema, groupResourceSchema, getActiveSession, findTenantFromStateParam, defaultLogger, deepEqualPlain, decodeUser, debugLogger, consoleLogger, clearActiveSession, claimsToUser, buildTenantPath, X509Certificate, WorkloadValidators, WorkloadTokenStore, WorkloadTokenResponse, WorkloadIncomingOutgoing, WorkloadIdentity, WorkloadConfigMap, WorkloadConfig, WorkloadClient, Workload, VaultWebSocketSecretsConfig, VaultWebSocketAuthHeader, VaultSecretsConfig, VaultLfvSecretsConfig, ValidateResult, UsersInboundHandlerConfig, UserStoreOptions, UserStore, UserSortOptions, UserSortField, UserListOptions, User2 as User, UpsertTenantResponse, UpsertTenantRequest, TokenValidationResult, TokenResponse, TenantWebhookPayload, TenantValidators, TenantUserRegistration, TenantStoredConfigLocator, TenantStoreWithEsCache, TenantStoreWithESOptions, TenantStoreWithES, TenantStore, TenantStatus, TenantSortOptions, TenantSortField, TenantSecretsConfig, TenantRoutingStrategy, TenantRequestError, TenantRemoteConfigLocator, TenantPathRoutingStrategy, TenantPathNamespace, TenantPathMatch, TenantManagerStore, TenantListOptions, TenantJwtRoutingStrategy, TenantEsFactory, TenantDirectoryTenant, TenantDirectoryResponse, TenantDirectoryAccount, TenantConfigStoreRequest, TenantConfigSourceInput, TenantConfigLocator, TenantConfigEnv, StoredUser, StoredTenantRecord, StoredTenant, StoredGroup, StateCookie, StandardSchemaWithValidate, SortDirection, SingleTenantStoreOptions, SingleTenantStore, SessionStore, Session, ServerOnlyWorkloadConfig, SecretsValidators, SecretsSourceType, SecretsSourceMap, SecretsSourceConfig, SecretsSource, SecretsOperationOptions, SecretsModuleConfig, Secrets, SecretRequestSeverity, SecretLifecycleRequest, Secret, User as ScimUser, ScimServiceProviderConfig, ScimSchemaDefinition, ScimSchemaAttributeDefinition, ScimResult, ScimResourceTypeSchemaExtension, ScimResourceType, ScimListResponse, ScimError, ScimAuthenticationScheme, SSOValidators, SSOHandlerConfig, SSOConfig, SSOAppValidators, SSOAppRegistry, SSO, Role, ResolvedVaultLfvSecretsConfig, RemoteConfig, RegisterSSOAppResult, RegisterSSOAppPayload, RegisterSSOAppError, RegisterIAMAppResult, RegisterIAMAppPayload, RegisterIAMAppError, ReactiveHandle, Photo, PhoneNumber, OidcCallbackParams, Name, MultipleTenantsForUserError, MultiTenantStoreOptions, MultiTenantStore, ModifiableFrameworkConfig, MetaData, MagicLinkStore, MagicLink, LoginConfig, Logger, ListResult, LfvOtpResponse, LfvOtpRequest, LfvErrorResponse, LfvErrorCode, LfvActionRequestBase, LfvActionName, LfvActionAcceptedResponse, JwtBearerWorkloadConfig, JWTAssertionClaims, InMemoryTenantStoreOptions, InMemorySingleTenantStoreOptions, InMemorySingleTenantStore, InMemoryMultiTenantStoreOptions, InMemoryMultiTenantStore, IdTokenClaims, IAMValidators, IAMUsersInbound, IAMInboundUsersConfig, IAMInboundUserContext, IAMHandlerConfig, IAMGroupsOutbound, IAMGroupsInbound, IAMDiscoveryHandlerConfig, IAMDiscoveryContext, IAMDiscoveryConfig, IAMConfig, IAMAppValidators, IAMAppRole, IAMAppRegistry, IAM, GroupsInboundHandlerConfig, GroupStore, GroupSortOptions, GroupSortField, GroupResource, GroupMember, GroupListOptions, Group, GcpSecretsConfig, FrameworkWorkloadIncomingOutgoing, FrameworkWorkloadConfig, FrameworkStores, FrameworkSecretsSourceConfig, FrameworkSecretsModuleConfig, FrameworkConfig, EnvironmentType, EnterpriseUser, EnterpriseStandardFromConfig, EnterpriseStandardBase, EnterpriseStandard, EnterpriseExtension, Email, ESValidators, ESRoutingOptions, ESRouteModule, ESRouteFilterResult, ESResolvedRoute, ESModuleFromConfig, ESConfigChangeResult, ESConfigChangeOptions, ESConfigChangeCallback, ESConfig, DevSecretsConfig, DEFAULT_TENANT_UI_NAMESPACE, DEFAULT_TENANT_API_NAMESPACE, CreateUserOptions, CreateTenantResponse, CreateTenantRequest, CreateGroupOptions, ConfigSourceType, ConfigSource, ClientCredentialsWorkloadConfig, ChangeListener, CachedWorkloadToken, CIAMValidators, CIAMConfigFromCode, CIAMConfig, CIAM, BaseUser, AzureSecretsConfig, AwsSecretsConfig, AwsAuthMethod, ApplicationValidators, Address };
|