@enterprisestandard/core 0.0.15-beta.20260407.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 +1409 -1126
- package/dist/index.js +1 -1
- package/dist/server.d.ts +1194 -901
- package/dist/server.js +1 -1
- package/dist/shared/core-wpy88bze.js +12 -0
- package/package.json +3 -3
- package/dist/shared/core-5zkmzncy.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,175 +729,360 @@ interface IdTokenClaims {
|
|
|
758
729
|
*/
|
|
759
730
|
declare function idTokenClaimsSchema(vendor: string): StandardSchemaV12<Record<string, unknown>, IdTokenClaims>;
|
|
760
731
|
/**
|
|
761
|
-
*
|
|
762
|
-
*
|
|
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
|
+
* ```
|
|
763
792
|
*/
|
|
764
|
-
interface User2 extends BaseUser {
|
|
765
|
-
/**
|
|
766
|
-
* SSO/OIDC authentication data
|
|
767
|
-
*/
|
|
768
|
-
sso: {
|
|
769
|
-
/**
|
|
770
|
-
* ID Token claims from the identity provider
|
|
771
|
-
*/
|
|
772
|
-
profile: IdTokenClaims;
|
|
773
|
-
/**
|
|
774
|
-
* Tenant/organization information
|
|
775
|
-
*/
|
|
776
|
-
tenant: {
|
|
777
|
-
id: string;
|
|
778
|
-
name: string;
|
|
779
|
-
};
|
|
780
|
-
/**
|
|
781
|
-
* OAuth scopes granted
|
|
782
|
-
*/
|
|
783
|
-
scope?: string;
|
|
784
|
-
/**
|
|
785
|
-
* Token type (typically "Bearer")
|
|
786
|
-
*/
|
|
787
|
-
tokenType: string;
|
|
788
|
-
/**
|
|
789
|
-
* Session state from the identity provider
|
|
790
|
-
*/
|
|
791
|
-
sessionState?: string;
|
|
792
|
-
/**
|
|
793
|
-
* Token expiration time
|
|
794
|
-
*/
|
|
795
|
-
expires: Date;
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
793
|
/**
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
* Extends the SSO User type with:
|
|
802
|
-
* - Required `id` (the `sub` claim from the IdP)
|
|
803
|
-
* - Timestamps for tracking when users were first seen and last updated
|
|
804
|
-
* - Optional custom extended data
|
|
794
|
+
* Core session data tracked for each authenticated user session.
|
|
805
795
|
*
|
|
806
|
-
* @template TExtended - Type-safe custom data that consumers can add to
|
|
796
|
+
* @template TExtended - Type-safe custom data that consumers can add to sessions
|
|
807
797
|
*/
|
|
808
|
-
type
|
|
798
|
+
type Session<TExtended = object> = {
|
|
809
799
|
/**
|
|
810
|
-
*
|
|
811
|
-
* This is the
|
|
800
|
+
* Session ID from the Identity Provider (from `sid` claim in ID token).
|
|
801
|
+
* This is the unique identifier for the session.
|
|
812
802
|
*/
|
|
813
|
-
|
|
803
|
+
sid: string;
|
|
814
804
|
/**
|
|
815
|
-
*
|
|
816
|
-
*
|
|
805
|
+
* Subject identifier (user ID) from the Identity Provider.
|
|
806
|
+
* From the `sub` claim in the ID token.
|
|
817
807
|
*/
|
|
818
|
-
|
|
808
|
+
sub: string;
|
|
819
809
|
/**
|
|
820
|
-
* Timestamp when the
|
|
810
|
+
* Timestamp when the session was created.
|
|
821
811
|
*/
|
|
822
812
|
createdAt: Date;
|
|
823
813
|
/**
|
|
824
|
-
* Timestamp
|
|
814
|
+
* Timestamp of the last activity in this session.
|
|
815
|
+
* Can be updated to track session activity.
|
|
825
816
|
*/
|
|
826
|
-
|
|
817
|
+
lastActivityAt: Date;
|
|
818
|
+
/**
|
|
819
|
+
* Allow consumers to add runtime data to sessions.
|
|
820
|
+
*/
|
|
821
|
+
[key: string]: unknown;
|
|
827
822
|
} & TExtended;
|
|
828
823
|
/**
|
|
829
|
-
* Abstract interface for
|
|
824
|
+
* Abstract interface for session storage backends.
|
|
830
825
|
*
|
|
831
826
|
* Consumers can implement this interface to use different storage backends:
|
|
832
|
-
* -
|
|
833
|
-
* - Redis (for production with fast lookups)
|
|
827
|
+
* - Redis
|
|
834
828
|
* - Database (PostgreSQL, MySQL, etc.)
|
|
829
|
+
* - Distributed cache
|
|
830
|
+
* - Custom solutions
|
|
835
831
|
*
|
|
836
|
-
* @template TExtended - Type-safe custom data that consumers can add to
|
|
832
|
+
* @template TExtended - Type-safe custom data that consumers can add to sessions
|
|
837
833
|
*
|
|
838
834
|
* @example
|
|
839
835
|
* ```typescript
|
|
840
|
-
* // Custom
|
|
841
|
-
* type
|
|
842
|
-
*
|
|
843
|
-
*
|
|
836
|
+
* // Custom session data
|
|
837
|
+
* type MySessionData = {
|
|
838
|
+
* ipAddress: string;
|
|
839
|
+
* userAgent: string;
|
|
844
840
|
* };
|
|
845
841
|
*
|
|
846
842
|
* // Implement custom store
|
|
847
|
-
* class
|
|
848
|
-
* async
|
|
849
|
-
*
|
|
850
|
-
* return data ? JSON.parse(data) : null;
|
|
843
|
+
* class RedisSessionStore implements SessionStore<MySessionData> {
|
|
844
|
+
* async create(session: Session<MySessionData>): Promise<void> {
|
|
845
|
+
* await redis.set(`session:${session.sid}`, JSON.stringify(session));
|
|
851
846
|
* }
|
|
852
847
|
* // ... other methods
|
|
853
848
|
* }
|
|
854
849
|
* ```
|
|
855
850
|
*/
|
|
856
|
-
interface
|
|
857
|
-
/**
|
|
858
|
-
* Retrieve a user by their subject identifier (sub).
|
|
859
|
-
*
|
|
860
|
-
* @param sub - The user's unique identifier from the IdP
|
|
861
|
-
* @returns The user if found, null otherwise
|
|
862
|
-
*/
|
|
863
|
-
get(sub: string): Promise<StoredUser<TExtended> | null>;
|
|
851
|
+
interface SessionStore<TExtended = object> {
|
|
864
852
|
/**
|
|
865
|
-
*
|
|
853
|
+
* Create a new session in the store.
|
|
866
854
|
*
|
|
867
|
-
* @param
|
|
868
|
-
* @
|
|
855
|
+
* @param session - The session data to store
|
|
856
|
+
* @throws Error if session with same sid already exists
|
|
869
857
|
*/
|
|
870
|
-
|
|
858
|
+
create(session: Session<TExtended>): Promise<void>;
|
|
871
859
|
/**
|
|
872
|
-
* Retrieve a
|
|
860
|
+
* Retrieve a session by its IdP session ID (sid).
|
|
873
861
|
*
|
|
874
|
-
* @param
|
|
875
|
-
* @returns The
|
|
862
|
+
* @param sid - The session.sid from the Identity Provider
|
|
863
|
+
* @returns The session if found, null otherwise
|
|
876
864
|
*/
|
|
877
|
-
|
|
865
|
+
get(sid: string): Promise<Session<TExtended> | null>;
|
|
878
866
|
/**
|
|
879
|
-
*
|
|
867
|
+
* Update an existing session with partial data.
|
|
880
868
|
*
|
|
881
|
-
*
|
|
882
|
-
* Otherwise, a new user will be created.
|
|
869
|
+
* Commonly used to update lastActivityAt or add custom fields.
|
|
883
870
|
*
|
|
884
|
-
* @param
|
|
871
|
+
* @param sid - The session.sid to update
|
|
872
|
+
* @param data - Partial session data to merge
|
|
873
|
+
* @throws Error if session not found
|
|
885
874
|
*/
|
|
886
|
-
|
|
875
|
+
update(sid: string, data: Partial<Session<TExtended>>): Promise<void>;
|
|
887
876
|
/**
|
|
888
|
-
* Delete a
|
|
877
|
+
* Delete a session by its IdP session ID (sid).
|
|
889
878
|
*
|
|
890
|
-
*
|
|
891
|
-
*/
|
|
892
|
-
delete(sub: string): Promise<void>;
|
|
893
|
-
/**
|
|
894
|
-
* List users in the store with optional pagination and sort.
|
|
879
|
+
* Used for both normal logout and backchannel logout flows.
|
|
895
880
|
*
|
|
896
|
-
* @param
|
|
897
|
-
* @returns ListResult with total, count, items, size, page, pages
|
|
881
|
+
* @param sid - The session.sid to delete
|
|
898
882
|
*/
|
|
899
|
-
|
|
883
|
+
delete(sid: string): Promise<void>;
|
|
900
884
|
}
|
|
901
|
-
import { StandardSchemaV1 as StandardSchemaV14 } from "@standard-schema/spec";
|
|
902
|
-
import { StandardSchemaV1 as StandardSchemaV13 } from "@standard-schema/spec";
|
|
903
885
|
/**
|
|
904
|
-
*
|
|
905
|
-
*
|
|
906
|
-
* @see https://datatracker.ietf.org/doc/html/rfc9068
|
|
886
|
+
* Base user with simple, developer-friendly attributes.
|
|
887
|
+
* Extended by User (SSO) and EnterpriseUser (SCIM).
|
|
907
888
|
*/
|
|
908
|
-
interface
|
|
889
|
+
interface BaseUser {
|
|
909
890
|
/**
|
|
910
|
-
*
|
|
891
|
+
* Unique identifier for the user
|
|
911
892
|
*/
|
|
912
|
-
|
|
893
|
+
id?: string;
|
|
913
894
|
/**
|
|
914
|
-
* REQUIRED.
|
|
895
|
+
* REQUIRED. Unique identifier for login
|
|
915
896
|
*/
|
|
916
|
-
|
|
897
|
+
userName: string;
|
|
917
898
|
/**
|
|
918
|
-
*
|
|
919
|
-
* Note: Required for JWT assertions, but may be absent in OAuth2 access tokens
|
|
899
|
+
* REQUIRED. Simple display name
|
|
920
900
|
*/
|
|
921
|
-
|
|
901
|
+
name: string;
|
|
922
902
|
/**
|
|
923
|
-
* REQUIRED.
|
|
903
|
+
* REQUIRED. Primary email address
|
|
924
904
|
*/
|
|
925
|
-
|
|
905
|
+
email: string;
|
|
926
906
|
/**
|
|
927
|
-
*
|
|
907
|
+
* URL to user's avatar/profile picture
|
|
928
908
|
*/
|
|
929
|
-
|
|
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
|
+
/**
|
|
917
|
+
* Primary user type for SSO/OIDC applications.
|
|
918
|
+
* Extends BaseUser with SSO-specific data.
|
|
919
|
+
*/
|
|
920
|
+
interface User2 extends BaseUser {
|
|
921
|
+
/**
|
|
922
|
+
* SSO/OIDC authentication data
|
|
923
|
+
*/
|
|
924
|
+
sso: {
|
|
925
|
+
/**
|
|
926
|
+
* ID Token claims from the identity provider
|
|
927
|
+
*/
|
|
928
|
+
profile: IdTokenClaims;
|
|
929
|
+
/**
|
|
930
|
+
* Tenant/organization information
|
|
931
|
+
*/
|
|
932
|
+
tenant: {
|
|
933
|
+
id: string;
|
|
934
|
+
name: string;
|
|
935
|
+
};
|
|
936
|
+
/**
|
|
937
|
+
* OAuth scopes granted
|
|
938
|
+
*/
|
|
939
|
+
scope?: string;
|
|
940
|
+
/**
|
|
941
|
+
* Token type (typically "Bearer")
|
|
942
|
+
*/
|
|
943
|
+
tokenType: string;
|
|
944
|
+
/**
|
|
945
|
+
* Session state from the identity provider
|
|
946
|
+
*/
|
|
947
|
+
sessionState?: string;
|
|
948
|
+
/**
|
|
949
|
+
* Token expiration time
|
|
950
|
+
*/
|
|
951
|
+
expires: Date;
|
|
952
|
+
};
|
|
953
|
+
}
|
|
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";
|
|
972
|
+
/**
|
|
973
|
+
* Optional cookie name for tracking the active session across tenants.
|
|
974
|
+
* Defaults to 'es.active_session' when using the helper utilities.
|
|
975
|
+
*/
|
|
976
|
+
activeSessionCookieName?: string;
|
|
977
|
+
endSessionEndpoint?: string;
|
|
978
|
+
revocationEndpoint?: string;
|
|
979
|
+
sessionStore?: SessionStore<TSessionData>;
|
|
980
|
+
/**
|
|
981
|
+
* Optional user store for persisting user profiles from SSO authentication.
|
|
982
|
+
* When configured, users are automatically stored/updated on each login.
|
|
983
|
+
*/
|
|
984
|
+
userStore?: UserStore<TUserData>;
|
|
985
|
+
/**
|
|
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
|
|
991
|
+
*/
|
|
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";
|
|
1052
|
+
type ChangeListener = () => void;
|
|
1053
|
+
type ReactiveHandle = {
|
|
1054
|
+
beforeChange?(listener: ChangeListener): () => void;
|
|
1055
|
+
afterChange?(listener: ChangeListener): () => void;
|
|
1056
|
+
isAvailable?(): boolean;
|
|
1057
|
+
};
|
|
1058
|
+
import { StandardSchemaV1 as StandardSchemaV14 } from "@standard-schema/spec";
|
|
1059
|
+
/**
|
|
1060
|
+
* JWT Assertion Claims for OAuth2 JWT Bearer Grant (RFC 7523) and OAuth2 Access Tokens
|
|
1061
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7523
|
|
1062
|
+
* @see https://datatracker.ietf.org/doc/html/rfc9068
|
|
1063
|
+
*/
|
|
1064
|
+
interface JWTAssertionClaims {
|
|
1065
|
+
/**
|
|
1066
|
+
* REQUIRED. Issuer - the workload identity (e.g., SPIFFE ID) or authorization server
|
|
1067
|
+
*/
|
|
1068
|
+
iss: string;
|
|
1069
|
+
/**
|
|
1070
|
+
* REQUIRED. Subject - the workload identity or service account
|
|
1071
|
+
*/
|
|
1072
|
+
sub: string;
|
|
1073
|
+
/**
|
|
1074
|
+
* OPTIONAL. Audience - may be a string or array of strings
|
|
1075
|
+
* Note: Required for JWT assertions, but may be absent in OAuth2 access tokens
|
|
1076
|
+
*/
|
|
1077
|
+
aud?: string | string[];
|
|
1078
|
+
/**
|
|
1079
|
+
* REQUIRED. Expiration time (Unix timestamp)
|
|
1080
|
+
*/
|
|
1081
|
+
exp: number;
|
|
1082
|
+
/**
|
|
1083
|
+
* REQUIRED. Issued at time (Unix timestamp)
|
|
1084
|
+
*/
|
|
1085
|
+
iat: number;
|
|
930
1086
|
/**
|
|
931
1087
|
* OPTIONAL. JWT ID - unique identifier for this token
|
|
932
1088
|
* Note: Required for JWT assertions, optional for access tokens
|
|
@@ -946,7 +1102,7 @@ interface JWTAssertionClaims {
|
|
|
946
1102
|
* @param vendor - The name of the vendor creating this schema
|
|
947
1103
|
* @returns A StandardSchemaV1 instance for JWT Assertion Claims validation
|
|
948
1104
|
*/
|
|
949
|
-
declare function jwtAssertionClaimsSchema(vendor: string):
|
|
1105
|
+
declare function jwtAssertionClaimsSchema(vendor: string): StandardSchemaV14<Record<string, unknown>, JWTAssertionClaims>;
|
|
950
1106
|
/**
|
|
951
1107
|
* Workload Token Response from OAuth2 token endpoint
|
|
952
1108
|
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
|
|
@@ -982,7 +1138,7 @@ interface WorkloadTokenResponse {
|
|
|
982
1138
|
* @param vendor - The name of the vendor creating this schema
|
|
983
1139
|
* @returns A StandardSchemaV1 instance for Workload Token Response validation
|
|
984
1140
|
*/
|
|
985
|
-
declare function workloadTokenResponseSchema(vendor: string):
|
|
1141
|
+
declare function workloadTokenResponseSchema(vendor: string): StandardSchemaV14<Record<string, unknown>, WorkloadTokenResponse>;
|
|
986
1142
|
/**
|
|
987
1143
|
* Token Validation Result
|
|
988
1144
|
*/
|
|
@@ -1229,8 +1385,8 @@ type WorkloadConfigBase = {
|
|
|
1229
1385
|
validators?: WorkloadValidators;
|
|
1230
1386
|
};
|
|
1231
1387
|
type WorkloadValidators = {
|
|
1232
|
-
jwtAssertionClaims:
|
|
1233
|
-
tokenResponse:
|
|
1388
|
+
jwtAssertionClaims: StandardSchemaV15<unknown, JWTAssertionClaims>;
|
|
1389
|
+
tokenResponse: StandardSchemaV15<unknown, WorkloadTokenResponse>;
|
|
1234
1390
|
};
|
|
1235
1391
|
/**
|
|
1236
1392
|
* JWT Bearer Grant (RFC 7523) Configuration
|
|
@@ -1393,7 +1549,7 @@ type WorkloadIdentity = {
|
|
|
1393
1549
|
/**
|
|
1394
1550
|
* Workload Identity Authentication Interface
|
|
1395
1551
|
*/
|
|
1396
|
-
type Workload = WorkloadConfig & {
|
|
1552
|
+
type Workload = WorkloadConfig & ReactiveHandle & {
|
|
1397
1553
|
/**
|
|
1398
1554
|
* Returns a token for this workload configuration.
|
|
1399
1555
|
* The optional argument overrides the configured default scope.
|
|
@@ -1408,1159 +1564,1279 @@ type Workload = WorkloadConfig & {
|
|
|
1408
1564
|
/** Framework-agnostic request handler for the Workload module (token, validate, jwks, refresh). */
|
|
1409
1565
|
handler: (request: Request) => Promise<Response>;
|
|
1410
1566
|
};
|
|
1411
|
-
type WorkloadClient = Pick<Workload, "getToken" | "refreshToken" | "generateJWTAssertion" | "revokeToken">;
|
|
1412
|
-
/**
|
|
1413
|
-
* SCIM Error response structure
|
|
1414
|
-
*/
|
|
1415
|
-
interface ScimError {
|
|
1416
|
-
schemas: string[];
|
|
1417
|
-
status: string;
|
|
1418
|
-
scimType?: string;
|
|
1419
|
-
detail?: string;
|
|
1420
|
-
}
|
|
1421
|
-
/**
|
|
1422
|
-
* SCIM List Response for bulk operations
|
|
1423
|
-
*/
|
|
1424
|
-
interface ScimListResponse<T> {
|
|
1425
|
-
schemas: string[];
|
|
1426
|
-
totalResults: number;
|
|
1427
|
-
startIndex?: number;
|
|
1428
|
-
itemsPerPage?: number;
|
|
1429
|
-
Resources: T[];
|
|
1430
|
-
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Result of a SCIM operation
|
|
1433
|
-
*/
|
|
1434
|
-
interface ScimResult<T> {
|
|
1435
|
-
success: boolean;
|
|
1436
|
-
data?: T;
|
|
1437
|
-
error?: ScimError;
|
|
1438
|
-
status: number;
|
|
1439
|
-
}
|
|
1440
|
-
/**
|
|
1441
|
-
* Handler configuration for IAM
|
|
1442
|
-
*/
|
|
1443
|
-
interface IAMHandlerConfig {
|
|
1444
|
-
/**
|
|
1445
|
-
* Base path for the SCIM Users endpoints (e.g., '/api/iam/Users')
|
|
1446
|
-
*/
|
|
1447
|
-
usersUrl?: string;
|
|
1448
|
-
/**
|
|
1449
|
-
* Base path for the SCIM Groups endpoints (e.g., '/api/iam/Groups')
|
|
1450
|
-
*/
|
|
1451
|
-
groupsUrl?: string;
|
|
1452
|
-
}
|
|
1567
|
+
type WorkloadClient = Pick<Workload, "getToken" | "refreshToken" | "generateJWTAssertion" | "revokeToken" | "beforeChange" | "afterChange" | "isAvailable">;
|
|
1453
1568
|
/**
|
|
1454
|
-
*
|
|
1569
|
+
* Magic link data stored in the store.
|
|
1455
1570
|
*
|
|
1456
|
-
* -
|
|
1457
|
-
* - If `groupStore` is provided, groups_inbound is enabled (external IAM calls app)
|
|
1458
|
-
* - If `userStore` is provided, users_inbound is enabled (external IAM calls app)
|
|
1571
|
+
* @template TExtended - Type-safe custom data that consumers can add to magic links
|
|
1459
1572
|
*/
|
|
1460
|
-
type
|
|
1573
|
+
type MagicLink<TExtended = object> = {
|
|
1461
1574
|
/**
|
|
1462
|
-
*
|
|
1463
|
-
* If provided, enables outbound SCIM operations (app -> external IAM)
|
|
1575
|
+
* The magic link token (unique identifier)
|
|
1464
1576
|
*/
|
|
1465
|
-
|
|
1577
|
+
token: string;
|
|
1466
1578
|
/**
|
|
1467
|
-
*
|
|
1468
|
-
* When configured, the app can receive user CRUD operations via SCIM.
|
|
1579
|
+
* User information associated with this magic link
|
|
1469
1580
|
*/
|
|
1470
|
-
|
|
1581
|
+
user: BaseUser;
|
|
1471
1582
|
/**
|
|
1472
|
-
*
|
|
1473
|
-
* When configured, enables groups_inbound (external IAM -> app).
|
|
1583
|
+
* Timestamp when the magic link was created
|
|
1474
1584
|
*/
|
|
1475
|
-
|
|
1585
|
+
createdAt: Date;
|
|
1476
1586
|
/**
|
|
1477
|
-
*
|
|
1478
|
-
* `iam.handler`, with per-call values taking precedence.
|
|
1587
|
+
* Timestamp when the magic link expires
|
|
1479
1588
|
*/
|
|
1480
|
-
|
|
1481
|
-
groupsUrl?: string;
|
|
1482
|
-
};
|
|
1483
|
-
type IAMValidators = {
|
|
1484
|
-
user: StandardSchemaV15<unknown, User>;
|
|
1485
|
-
group: StandardSchemaV15<unknown, GroupResource>;
|
|
1486
|
-
};
|
|
1487
|
-
/**
|
|
1488
|
-
* Options for creating a user
|
|
1489
|
-
*/
|
|
1490
|
-
interface CreateUserOptions {
|
|
1589
|
+
expiresAt: Date;
|
|
1491
1590
|
/**
|
|
1492
|
-
*
|
|
1591
|
+
* Allow consumers to add runtime data to magic links
|
|
1493
1592
|
*/
|
|
1494
|
-
|
|
1495
|
-
}
|
|
1593
|
+
[key: string]: unknown;
|
|
1594
|
+
} & TExtended;
|
|
1496
1595
|
/**
|
|
1497
|
-
*
|
|
1498
|
-
|
|
1499
|
-
interface
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
*
|
|
1596
|
+
* Abstract interface for magic link storage backends.
|
|
1597
|
+
*
|
|
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
|
+
* ```
|
|
1511
1630
|
*/
|
|
1512
|
-
interface
|
|
1631
|
+
interface MagicLinkStore<TExtended = object> {
|
|
1513
1632
|
/**
|
|
1514
|
-
*
|
|
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
|
|
1515
1639
|
*/
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1518
|
-
/**
|
|
1519
|
-
* Handler configuration for users_inbound
|
|
1520
|
-
*/
|
|
1521
|
-
interface UsersInboundHandlerConfig {
|
|
1640
|
+
create(token: string, user: BaseUser, expiresAt: Date): Promise<void>;
|
|
1522
1641
|
/**
|
|
1523
|
-
*
|
|
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
|
|
1524
1646
|
*/
|
|
1525
|
-
|
|
1526
|
-
}
|
|
1527
|
-
/**
|
|
1528
|
-
* Groups Outbound extension - for creating groups in external IAM providers.
|
|
1529
|
-
* Enabled when `url` is configured in IAMConfig.
|
|
1530
|
-
*/
|
|
1531
|
-
type IAMGroupsOutbound = {
|
|
1647
|
+
get(token: string): Promise<MagicLink<TExtended> | null>;
|
|
1532
1648
|
/**
|
|
1533
|
-
*
|
|
1534
|
-
*
|
|
1535
|
-
*
|
|
1536
|
-
*
|
|
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
|
|
1537
1654
|
*/
|
|
1538
|
-
|
|
1655
|
+
delete(token: string): Promise<void>;
|
|
1656
|
+
}
|
|
1657
|
+
type Secret<T> = {
|
|
1658
|
+
data: T;
|
|
1659
|
+
metadata: MetaData;
|
|
1539
1660
|
};
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
type IAMGroupsInbound = {
|
|
1545
|
-
/**
|
|
1546
|
-
* Handle inbound SCIM requests for group management.
|
|
1547
|
-
* Routes: GET/POST /Groups, GET/PUT/PATCH/DELETE /Groups/:id
|
|
1548
|
-
*/
|
|
1549
|
-
handler: (request: Request, config?: GroupsInboundHandlerConfig) => Promise<Response>;
|
|
1661
|
+
type MetaData = {
|
|
1662
|
+
path: string;
|
|
1663
|
+
version: number;
|
|
1664
|
+
created: Date;
|
|
1550
1665
|
};
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
* Handle inbound SCIM requests for user management.
|
|
1558
|
-
* Routes: GET/POST /Users, GET/PUT/PATCH/DELETE /Users/:id
|
|
1559
|
-
*/
|
|
1560
|
-
handler: (request: Request, config?: UsersInboundHandlerConfig) => Promise<Response>;
|
|
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;
|
|
1561
1672
|
};
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
type IAM = IAMConfig & {
|
|
1571
|
-
/**
|
|
1572
|
-
* Create a new user/account in the external IAM provider
|
|
1573
|
-
* Only available when `url` is configured.
|
|
1574
|
-
*/
|
|
1575
|
-
createUser?: (user: User, options?: CreateUserOptions) => Promise<ScimResult<User>>;
|
|
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>;
|
|
1576
1681
|
/**
|
|
1577
|
-
*
|
|
1682
|
+
* Write a secret at the given path.
|
|
1683
|
+
* Implementations may throw for read-only or unsupported secret sources.
|
|
1578
1684
|
*/
|
|
1579
|
-
|
|
1685
|
+
putSecret: (path: string, value: Record<string, unknown>, options?: SecretsOperationOptions) => Promise<void>;
|
|
1580
1686
|
/**
|
|
1581
|
-
*
|
|
1582
|
-
*
|
|
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.
|
|
1583
1691
|
*/
|
|
1584
|
-
|
|
1692
|
+
subscribe<T>(path: string, onChange: (fullSecret: Secret<T>) => void): () => void;
|
|
1585
1693
|
/**
|
|
1586
|
-
*
|
|
1587
|
-
* Available when `groupStore` is configured in IAMConfig.
|
|
1694
|
+
* Deletes a secret at the given path.
|
|
1588
1695
|
*/
|
|
1589
|
-
|
|
1696
|
+
deleteSecret(path: string, options?: SecretsOperationOptions): Promise<void>;
|
|
1590
1697
|
/**
|
|
1591
|
-
*
|
|
1592
|
-
* Available when `userStore` is configured in IAMConfig.
|
|
1698
|
+
* Lists child paths under the given base path.
|
|
1593
1699
|
*/
|
|
1594
|
-
|
|
1700
|
+
listPaths(path: string, options?: SecretsOperationOptions): Promise<string[]>;
|
|
1595
1701
|
/**
|
|
1596
|
-
*
|
|
1597
|
-
* Routes to users_inbound or groups_inbound based on the request path.
|
|
1702
|
+
* Returns true when a secret path exists.
|
|
1598
1703
|
*/
|
|
1599
|
-
|
|
1600
|
-
};
|
|
1601
|
-
import { StandardSchemaV1 as StandardSchemaV16 } from "@standard-schema/spec";
|
|
1602
|
-
/**
|
|
1603
|
-
* Session management for tracking user sessions and enabling backchannel logout.
|
|
1604
|
-
*
|
|
1605
|
-
* Session stores are optional - the package works with JWT cookies alone.
|
|
1606
|
-
* Sessions are only required for backchannel logout functionality.
|
|
1607
|
-
*
|
|
1608
|
-
* ## Session Validation Strategies
|
|
1609
|
-
*
|
|
1610
|
-
* When using a session store, you can configure when sessions are validated:
|
|
1611
|
-
*
|
|
1612
|
-
* ### 'always' (default)
|
|
1613
|
-
* Validates session on every authenticated request.
|
|
1614
|
-
* - **Security**: Maximum - immediate session revocation
|
|
1615
|
-
* - **Performance**: InMemory ~0.00005ms, Redis ~1-2ms per request
|
|
1616
|
-
* - **Backchannel Logout**: Takes effect immediately
|
|
1617
|
-
* - **Use when**: Security is paramount, using InMemory or Redis backend
|
|
1618
|
-
*
|
|
1619
|
-
* ### 'refresh-only'
|
|
1620
|
-
* Validates session only during token refresh (typically every 5-15 minutes).
|
|
1621
|
-
* - **Security**: Good - 5-15 minute revocation window
|
|
1622
|
-
* - **Performance**: 99% reduction in session lookups
|
|
1623
|
-
* - **Backchannel Logout**: Takes effect within token TTL (5-15 min)
|
|
1624
|
-
* - **Use when**: Performance is critical AND delayed revocation is acceptable
|
|
1625
|
-
* - **WARNING**: Compromised sessions remain valid until next refresh
|
|
1626
|
-
*
|
|
1627
|
-
* ### 'disabled'
|
|
1628
|
-
* Never validates sessions against the store.
|
|
1629
|
-
* - **Security**: None - backchannel logout doesn't work
|
|
1630
|
-
* - **Performance**: No overhead
|
|
1631
|
-
* - **Use when**: Cookie-only mode without session store
|
|
1632
|
-
* - **WARNING**: Do not use with sessionStore configured
|
|
1633
|
-
*
|
|
1634
|
-
* ## Performance Characteristics
|
|
1635
|
-
*
|
|
1636
|
-
* | Backend | Lookup Time | Impact on Request | Recommendation |
|
|
1637
|
-
* |--------------|-------------|-------------------|------------------------|
|
|
1638
|
-
* | InMemory | <0.00005ms | Negligible | Use 'always' |
|
|
1639
|
-
* | Redis | 1-2ms | 2-4% increase | Use 'always' or test |
|
|
1640
|
-
* | Database | 5-20ms | 10-40% increase | Use Redis cache layer |
|
|
1641
|
-
*
|
|
1642
|
-
* ## Example Usage
|
|
1643
|
-
*
|
|
1644
|
-
* ```typescript
|
|
1645
|
-
* import { sso, InMemorySessionStore } from '@enterprisestandard/server';
|
|
1646
|
-
*
|
|
1647
|
-
* // Maximum security (default)
|
|
1648
|
-
* const secure = sso({
|
|
1649
|
-
* // ... other config
|
|
1650
|
-
* sessionStore: new InMemorySessionStore(),
|
|
1651
|
-
* session_validation: 'always', // Immediate revocation
|
|
1652
|
-
* });
|
|
1653
|
-
*
|
|
1654
|
-
* // High performance
|
|
1655
|
-
* const fast = sso({
|
|
1656
|
-
* // ... other config
|
|
1657
|
-
* sessionStore: new InMemorySessionStore(),
|
|
1658
|
-
* session_validation: {
|
|
1659
|
-
* strategy: 'refresh-only' // 5-15 min revocation delay
|
|
1660
|
-
* }
|
|
1661
|
-
* });
|
|
1662
|
-
* ```
|
|
1663
|
-
*/
|
|
1664
|
-
/**
|
|
1665
|
-
* Core session data tracked for each authenticated user session.
|
|
1666
|
-
*
|
|
1667
|
-
* @template TExtended - Type-safe custom data that consumers can add to sessions
|
|
1668
|
-
*/
|
|
1669
|
-
type Session<TExtended = object> = {
|
|
1704
|
+
exists(path: string, options?: SecretsOperationOptions): Promise<boolean>;
|
|
1670
1705
|
/**
|
|
1671
|
-
*
|
|
1672
|
-
* This is the unique identifier for the session.
|
|
1706
|
+
* Requests secret rotation for the given path.
|
|
1673
1707
|
*/
|
|
1674
|
-
|
|
1708
|
+
requestRotate(path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
1675
1709
|
/**
|
|
1676
|
-
*
|
|
1677
|
-
* From the `sub` claim in the ID token.
|
|
1710
|
+
* Requests secret revocation for the given path.
|
|
1678
1711
|
*/
|
|
1679
|
-
|
|
1712
|
+
requestRevoke(path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
1680
1713
|
/**
|
|
1681
|
-
*
|
|
1714
|
+
* Reads metadata for the given path.
|
|
1682
1715
|
*/
|
|
1683
|
-
|
|
1716
|
+
getMetadata(path: string, options?: SecretsOperationOptions): Promise<Record<string, unknown>>;
|
|
1717
|
+
};
|
|
1718
|
+
type SecretsValidators = {
|
|
1684
1719
|
/**
|
|
1685
|
-
*
|
|
1686
|
-
*
|
|
1720
|
+
* Optional hook to validate merged source configs before they are resolved.
|
|
1721
|
+
* Throw from this callback to reject invalid secrets source configuration.
|
|
1687
1722
|
*/
|
|
1688
|
-
|
|
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
|
+
};
|
|
1761
|
+
/**
|
|
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.
|
|
1764
|
+
*/
|
|
1765
|
+
type FrameworkSecretsSourceConfig = Partial<SecretsSourceConfig>;
|
|
1766
|
+
/**
|
|
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.
|
|
1769
|
+
*/
|
|
1770
|
+
type FrameworkSecretsModuleConfig = Record<string, FrameworkSecretsSourceConfig>;
|
|
1771
|
+
/**
|
|
1772
|
+
* TODO: Let's see if we can do some clean inference and remove this!!!
|
|
1773
|
+
*/
|
|
1774
|
+
type SecretsSourceMap = Record<string, SecretsSource>;
|
|
1775
|
+
type SecretsSourceConfig = DevSecretsConfig | GcpSecretsConfig | VaultSecretsConfig | AwsSecretsConfig | AzureSecretsConfig;
|
|
1776
|
+
/**
|
|
1777
|
+
* Raw module config keyed by source name.
|
|
1778
|
+
* The secrets module resolves this into a runtime SecretsSourceMap.
|
|
1779
|
+
*/
|
|
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;
|
|
1689
1796
|
/**
|
|
1690
|
-
*
|
|
1797
|
+
* Optional LFV signature verification key.
|
|
1798
|
+
* When omitted, LFV callbacks are accepted without signature verification.
|
|
1691
1799
|
*/
|
|
1692
|
-
|
|
1693
|
-
|
|
1800
|
+
verifyPublicKey?: string;
|
|
1801
|
+
eventsEndpoint: string;
|
|
1802
|
+
path?: string;
|
|
1803
|
+
/**
|
|
1804
|
+
* Timeout in milliseconds when waiting for a LFV delivery payload after a read request.
|
|
1805
|
+
* After this duration, an error is thrown.
|
|
1806
|
+
*/
|
|
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;
|
|
1812
|
+
/**
|
|
1813
|
+
* Optional logger for request/response tracing. Use `debugLogger` from `@enterprisestandard/core`
|
|
1814
|
+
* to get debug output with request_id for LFV operations.
|
|
1815
|
+
*/
|
|
1816
|
+
log?: Logger;
|
|
1817
|
+
};
|
|
1694
1818
|
/**
|
|
1695
|
-
*
|
|
1696
|
-
*
|
|
1697
|
-
* Consumers can implement this interface to use different storage backends:
|
|
1698
|
-
* - Redis
|
|
1699
|
-
* - Database (PostgreSQL, MySQL, etc.)
|
|
1700
|
-
* - Distributed cache
|
|
1701
|
-
* - Custom solutions
|
|
1702
|
-
*
|
|
1703
|
-
* @template TExtended - Type-safe custom data that consumers can add to sessions
|
|
1704
|
-
*
|
|
1705
|
-
* @example
|
|
1706
|
-
* ```typescript
|
|
1707
|
-
* // Custom session data
|
|
1708
|
-
* type MySessionData = {
|
|
1709
|
-
* ipAddress: string;
|
|
1710
|
-
* userAgent: string;
|
|
1711
|
-
* };
|
|
1712
|
-
*
|
|
1713
|
-
* // Implement custom store
|
|
1714
|
-
* class RedisSessionStore implements SessionStore<MySessionData> {
|
|
1715
|
-
* async create(session: Session<MySessionData>): Promise<void> {
|
|
1716
|
-
* await redis.set(`session:${session.sid}`, JSON.stringify(session));
|
|
1717
|
-
* }
|
|
1718
|
-
* // ... other methods
|
|
1719
|
-
* }
|
|
1720
|
-
* ```
|
|
1819
|
+
* Runtime-ready LFV source config.
|
|
1820
|
+
* Input config can be partially declared/merged, but LFV operations require these fields.
|
|
1721
1821
|
*/
|
|
1722
|
-
|
|
1822
|
+
type ResolvedVaultLfvSecretsConfig = Omit<VaultLfvSecretsConfig, "lfvServerUrl" | "clientId" | "path"> & {
|
|
1823
|
+
lfvServerUrl: string;
|
|
1824
|
+
clientId: string;
|
|
1825
|
+
path: string;
|
|
1826
|
+
};
|
|
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;
|
|
1723
1844
|
/**
|
|
1724
|
-
*
|
|
1725
|
-
*
|
|
1726
|
-
* @param session - The session data to store
|
|
1727
|
-
* @throws Error if session with same sid already exists
|
|
1845
|
+
* MINIMUM: 600_000 milliseconds (10 minutes). Polls the path every ttl milliseconds and calls onConfig when config changes.
|
|
1728
1846
|
*/
|
|
1729
|
-
|
|
1847
|
+
ttl?: number;
|
|
1848
|
+
};
|
|
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";
|
|
1730
1856
|
/**
|
|
1731
|
-
*
|
|
1857
|
+
* How to authenticate to Azure to fetch Key Vault access tokens.
|
|
1732
1858
|
*
|
|
1733
|
-
*
|
|
1734
|
-
*
|
|
1735
|
-
|
|
1736
|
-
get(sid: string): Promise<Session<TExtended> | null>;
|
|
1737
|
-
/**
|
|
1738
|
-
* Update an existing session with partial data.
|
|
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
|
|
1739
1862
|
*
|
|
1740
|
-
*
|
|
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
|
|
1741
1867
|
*
|
|
1742
|
-
*
|
|
1743
|
-
* @param data - Partial session data to merge
|
|
1744
|
-
* @throws Error if session not found
|
|
1868
|
+
* Env: ES_AZURE_AUTH_METHOD
|
|
1745
1869
|
*/
|
|
1746
|
-
|
|
1870
|
+
authMethod?: AwsAuthMethod;
|
|
1747
1871
|
/**
|
|
1748
|
-
*
|
|
1749
|
-
*
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1872
|
+
* Azure Entra tenant id (GUID), used for OAuth2 token exchange.
|
|
1873
|
+
* Env: ES_AZURE_TENANT_ID
|
|
1874
|
+
*/
|
|
1875
|
+
tenantId?: string;
|
|
1876
|
+
/**
|
|
1877
|
+
* Azure app registration client id (GUID).
|
|
1878
|
+
* Env: ES_AZURE_CLIENT_ID
|
|
1753
1879
|
*/
|
|
1754
|
-
delete(sid: string): Promise<void>;
|
|
1755
|
-
}
|
|
1756
|
-
type SSOConfig<
|
|
1757
|
-
TSessionData = Record<string, never>,
|
|
1758
|
-
TUserData = Record<string, never>
|
|
1759
|
-
> = {
|
|
1760
|
-
authority?: string;
|
|
1761
|
-
tokenUrl?: string;
|
|
1762
|
-
authorizationUrl?: string;
|
|
1763
1880
|
clientId?: string;
|
|
1881
|
+
/**
|
|
1882
|
+
* Azure app registration client secret.
|
|
1883
|
+
* Env: ES_AZURE_CLIENT_SECRET
|
|
1884
|
+
*/
|
|
1764
1885
|
clientSecret?: string;
|
|
1765
|
-
redirectUri?: string;
|
|
1766
|
-
responseType?: "code";
|
|
1767
|
-
scope?: string;
|
|
1768
|
-
silentRedirectUri?: string;
|
|
1769
|
-
jwksUri?: string;
|
|
1770
|
-
cookiesPrefix?: string;
|
|
1771
|
-
cookiesPath?: string;
|
|
1772
|
-
cookiesSecure?: boolean;
|
|
1773
|
-
cookiesSameSite?: "Strict" | "Lax";
|
|
1774
1886
|
/**
|
|
1775
|
-
*
|
|
1776
|
-
*
|
|
1887
|
+
* Path to the projected federated token file (AKS Workload Identity).
|
|
1888
|
+
* Env: ES_AZURE_FEDERATED_TOKEN_FILE or AZURE_FEDERATED_TOKEN_FILE
|
|
1777
1889
|
*/
|
|
1778
|
-
|
|
1779
|
-
endSessionEndpoint?: string;
|
|
1780
|
-
revocationEndpoint?: string;
|
|
1781
|
-
sessionStore?: SessionStore<TSessionData>;
|
|
1890
|
+
federatedTokenFile?: string;
|
|
1782
1891
|
/**
|
|
1783
|
-
*
|
|
1784
|
-
*
|
|
1892
|
+
* Managed Identity client id (user-assigned), optional.
|
|
1893
|
+
* Env: ES_AZURE_MANAGED_IDENTITY_CLIENT_ID
|
|
1785
1894
|
*/
|
|
1786
|
-
|
|
1895
|
+
managedIdentityClientId?: string;
|
|
1787
1896
|
/**
|
|
1788
|
-
*
|
|
1789
|
-
*
|
|
1790
|
-
*
|
|
1791
|
-
* Requires userStore to be configured.
|
|
1792
|
-
* @default false
|
|
1897
|
+
* IMDS API version (managed identity).
|
|
1898
|
+
* Env: ES_AZURE_IMDS_API_VERSION
|
|
1899
|
+
* Default: 2018-02-01
|
|
1793
1900
|
*/
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1901
|
+
imdsApiVersion?: string;
|
|
1902
|
+
/**
|
|
1903
|
+
* Key Vault URL, e.g. https://myvault.vault.azure.net
|
|
1904
|
+
* Env: ES_AZURE_KEY_VAULT_URL
|
|
1905
|
+
*/
|
|
1906
|
+
vaultUrl?: string;
|
|
1907
|
+
/**
|
|
1908
|
+
* Alternative to vaultUrl. If provided, vaultUrl is derived as:
|
|
1909
|
+
* https://${vaultName}.vault.azure.net
|
|
1910
|
+
* Env: ES_AZURE_KEY_VAULT_NAME
|
|
1911
|
+
*/
|
|
1912
|
+
vaultName?: string;
|
|
1913
|
+
/**
|
|
1914
|
+
* Key Vault API version.
|
|
1915
|
+
* Env: ES_AZURE_KEY_VAULT_API_VERSION
|
|
1916
|
+
* Default: 7.4
|
|
1917
|
+
*/
|
|
1918
|
+
apiVersion?: string;
|
|
1919
|
+
/**
|
|
1920
|
+
* Maps vault "path" to a Key Vault secret name.
|
|
1921
|
+
* Default: replaces `/` with `--` and trims leading/trailing `/`.
|
|
1922
|
+
*/
|
|
1923
|
+
secretNameTransform?: (path: string) => string;
|
|
1924
|
+
/**
|
|
1925
|
+
* Optional prefix added to all computed secret names.
|
|
1926
|
+
* Useful to namespace secrets by app/environment.
|
|
1927
|
+
*/
|
|
1928
|
+
secretNamePrefix?: string;
|
|
1929
|
+
/**
|
|
1930
|
+
* OAuth2 scope; default is Key Vault resource scope.
|
|
1931
|
+
* Default: https://vault.azure.net/.default
|
|
1932
|
+
*/
|
|
1933
|
+
scope?: string;
|
|
1934
|
+
/**
|
|
1935
|
+
* When set, the vault implements subscribe: poll this path every ttl seconds and call onConfig when config changes.
|
|
1936
|
+
*/
|
|
1937
|
+
ttl?: number;
|
|
1823
1938
|
};
|
|
1824
|
-
type
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
jwksUrl?: string;
|
|
1832
|
-
logoutUrl?: string;
|
|
1833
|
-
logoutBackChannelUrl?: string;
|
|
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;
|
|
1834
1946
|
};
|
|
1835
|
-
type
|
|
1836
|
-
|
|
1837
|
-
idTokenClaims: StandardSchemaV16<unknown, IdTokenClaims>;
|
|
1838
|
-
tokenResponse: StandardSchemaV16<unknown, TokenResponse>;
|
|
1947
|
+
type ApplicationValidators<TTenantValidators extends TenantValidators = TenantValidators> = ESValidators & {
|
|
1948
|
+
tenant: TTenantValidators;
|
|
1839
1949
|
};
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
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;
|
|
1852
1967
|
};
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
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;
|
|
1982
|
+
/**
|
|
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!!!
|
|
1987
|
+
*/
|
|
1988
|
+
workload?: WorkloadConfig | WorkloadConfigMap | WorkloadIncomingOutgoing;
|
|
1989
|
+
/** Optional named secrets-source configs available to this ESA instance. */
|
|
1990
|
+
secrets?: SecretsModuleConfig;
|
|
1991
|
+
ciam?: CIAMConfig;
|
|
1857
1992
|
};
|
|
1858
|
-
|
|
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;
|
|
2002
|
+
};
|
|
2003
|
+
type ModifiableFrameworkConfig = FrameworkConfig & {
|
|
2004
|
+
setStores(stores: FrameworkStores): void;
|
|
2005
|
+
};
|
|
2006
|
+
/** Return type from the beforeChange hook passed to enterpriseStandard(). */
|
|
2007
|
+
type ESConfigChangeResult = {
|
|
2008
|
+
config?: RemoteConfig;
|
|
2009
|
+
frameworkConfig?: FrameworkConfig;
|
|
2010
|
+
};
|
|
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>;
|
|
2015
|
+
/**
|
|
2016
|
+
* Called when the config changes.
|
|
2017
|
+
* @param onConfig - The callback to call when the config changes.
|
|
2018
|
+
* @return an unsubscribe/cleanup function.
|
|
2019
|
+
*/
|
|
2020
|
+
subscribe(onConfig: (config: RemoteConfig) => void): undefined | (() => void);
|
|
2021
|
+
/**
|
|
2022
|
+
* Default secret client for the config source itself.
|
|
2023
|
+
* For vault-backed sources this is the vault used to read RemoteConfig.
|
|
2024
|
+
*/
|
|
2025
|
+
secret: SecretsSource;
|
|
2026
|
+
/**
|
|
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`).
|
|
2029
|
+
*/
|
|
2030
|
+
log?: Logger;
|
|
2031
|
+
/**
|
|
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.
|
|
2034
|
+
*/
|
|
2035
|
+
validators?: ESValidators;
|
|
2036
|
+
};
|
|
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;
|
|
2048
|
+
};
|
|
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[];
|
|
2061
|
+
};
|
|
2062
|
+
type CreateTenantRequest = UpsertTenantRequest;
|
|
2063
|
+
type CreateTenantResponse = UpsertTenantResponse;
|
|
2064
|
+
/**
|
|
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.
|
|
2069
|
+
*/
|
|
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
|
+
};
|
|
2105
|
+
/**
|
|
2106
|
+
* Env-like tenant config variables used to build a ConfigSource at runtime.
|
|
2107
|
+
* These mirror the ES_* variables read by envConfig().
|
|
2108
|
+
*/
|
|
2109
|
+
type TenantConfigEnv = {
|
|
2110
|
+
ES_CONFIG_TYPE?: ConfigSourceType;
|
|
2111
|
+
ES_VAULT_URL?: string;
|
|
2112
|
+
ES_VAULT_TOKEN?: string;
|
|
2113
|
+
ES_VAULT_PATH?: string;
|
|
2114
|
+
ES_VAULT_TTL?: string;
|
|
2115
|
+
ES_VAULT_LFV_SERVER_URL?: string;
|
|
2116
|
+
ES_VAULT_LFV_CLIENT_ID?: string;
|
|
2117
|
+
ES_VAULT_LFV_SIGNATURE?: string;
|
|
2118
|
+
ES_VAULT_LFV_DELIVERY_ENDPOINT?: string;
|
|
2119
|
+
ES_VAULT_LFV_VERIFY_PUBLIC_KEY?: string;
|
|
2120
|
+
ES_VAULT_LFV_EVENTS_ENDPOINT?: string;
|
|
2121
|
+
ES_VAULT_LFV_DELIVERY_TIMEOUT?: string;
|
|
2122
|
+
ES_VAULT_LFV_RETRY_INTERVAL?: string;
|
|
2123
|
+
ES_VAULT_LFV_WARN_INTERVAL?: string;
|
|
2124
|
+
ES_VAULT_WEBSOCKET_URL?: string;
|
|
2125
|
+
ES_VAULT_WEBSOCKET_TOKEN?: string;
|
|
2126
|
+
ES_VAULT_WEBSOCKET_HEADER?: "X-Vault-Token" | "Authorization";
|
|
2127
|
+
ES_AZURE_API_VERSION?: string;
|
|
2128
|
+
ES_AZURE_SCOPE?: string;
|
|
2129
|
+
ES_AZURE_SECRET_NAME_PREFIX?: string;
|
|
2130
|
+
ES_AZURE_AUTH_METHOD?: AwsAuthMethod;
|
|
2131
|
+
ES_AZURE_TENANT_ID?: string;
|
|
2132
|
+
ES_AZURE_CLIENT_ID?: string;
|
|
2133
|
+
ES_AZURE_CLIENT_SECRET?: string;
|
|
2134
|
+
ES_AZURE_FEDERATED_TOKEN_FILE?: string;
|
|
2135
|
+
ES_AZURE_MANAGED_IDENTITY_CLIENT_ID?: string;
|
|
2136
|
+
ES_AZURE_IMDS_API_VERSION?: string;
|
|
2137
|
+
ES_AZURE_VAULT_URL?: string;
|
|
2138
|
+
ES_AZURE_VAULT_NAME?: string;
|
|
2139
|
+
ES_AZURE_TTL?: string;
|
|
2140
|
+
ES_AWS_WEBHOOK_URL?: string;
|
|
2141
|
+
ES_AWS_TTL?: string;
|
|
2142
|
+
ES_GCP_TTL?: string;
|
|
2143
|
+
};
|
|
2144
|
+
type TenantSecretsConfig = (VaultSecretsConfig & {
|
|
2145
|
+
path: string;
|
|
2146
|
+
retryInterval?: number;
|
|
2147
|
+
}) | (AwsSecretsConfig & {
|
|
2148
|
+
ttl?: number;
|
|
2149
|
+
}) | AzureSecretsConfig | (GcpSecretsConfig & {
|
|
2150
|
+
ttl?: number;
|
|
2151
|
+
});
|
|
2152
|
+
type TenantStoredConfigLocator = {
|
|
2153
|
+
/** Indicates that the tenant config descriptor is stored securely outside the tenant record. */
|
|
2154
|
+
type: "stored";
|
|
2155
|
+
/** Root secure source type used to fetch the stored tenant config descriptor. */
|
|
2156
|
+
sourceType: "vault";
|
|
2157
|
+
/** Path to the stored tenant config descriptor. */
|
|
1859
2158
|
path: string;
|
|
1860
|
-
version: number;
|
|
1861
|
-
created: Date;
|
|
1862
2159
|
};
|
|
1863
|
-
type
|
|
1864
|
-
|
|
1865
|
-
type
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
2160
|
+
type TenantRemoteConfigLocator = {
|
|
2161
|
+
/** Indicates that the tenant RemoteConfig already exists at this secure source path. */
|
|
2162
|
+
type: "remoteConfig";
|
|
2163
|
+
/** Secure source type used to load the RemoteConfig document directly. */
|
|
2164
|
+
sourceType: "vault";
|
|
2165
|
+
/** Path to the tenant RemoteConfig document. */
|
|
2166
|
+
path: string;
|
|
1869
2167
|
};
|
|
1870
|
-
type
|
|
1871
|
-
|
|
1872
|
-
|
|
2168
|
+
type TenantConfigLocator = TenantStoredConfigLocator | TenantRemoteConfigLocator;
|
|
2169
|
+
type TenantConfigSourceInput = TenantConfigLocator | ConfigSource;
|
|
2170
|
+
type TenantBaseRecord = {
|
|
2171
|
+
tenantId: string;
|
|
2172
|
+
companyId: string;
|
|
2173
|
+
companyName: string;
|
|
2174
|
+
environmentType: EnvironmentType;
|
|
2175
|
+
email?: string;
|
|
2176
|
+
webhookUrl?: string;
|
|
2177
|
+
callbackUrl?: string;
|
|
2178
|
+
tenantUrl?: string;
|
|
2179
|
+
status: TenantStatus;
|
|
2180
|
+
error?: string;
|
|
2181
|
+
actionUrl?: string;
|
|
2182
|
+
requestToken?: string;
|
|
2183
|
+
expiresAt?: string;
|
|
2184
|
+
createdAt: Date;
|
|
2185
|
+
updatedAt: Date;
|
|
2186
|
+
/** Persisted tenant config metadata, or a runtime ConfigSource for internal-only tenants. */
|
|
2187
|
+
configSource: TenantConfigSourceInput;
|
|
2188
|
+
/** Runtime helper that returns a ConfigSource for this tenant. */
|
|
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
|
+
> = {
|
|
2202
|
+
es: EnterpriseStandard;
|
|
2203
|
+
tenantId: string;
|
|
2204
|
+
request: TRequest;
|
|
2205
|
+
configData: TenantSecretsConfig;
|
|
2206
|
+
existingTenant: StoredTenant<TTenant> | undefined;
|
|
1873
2207
|
};
|
|
1874
|
-
type
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
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"];
|
|
2237
|
+
};
|
|
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;
|
|
1878
2321
|
/**
|
|
1879
|
-
*
|
|
1880
|
-
*
|
|
2322
|
+
* Optional SCIM email collection.
|
|
2323
|
+
* The simple `email` field still stores the primary email for convenience.
|
|
1881
2324
|
*/
|
|
1882
|
-
|
|
2325
|
+
emails?: Email[];
|
|
1883
2326
|
/**
|
|
1884
|
-
*
|
|
1885
|
-
*
|
|
1886
|
-
* that subscription was notified (same version must not trigger a second call).
|
|
1887
|
-
* @returns a unsubscribe/cleanup function.
|
|
2327
|
+
* Optional SCIM nickname.
|
|
2328
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1888
2329
|
*/
|
|
1889
|
-
|
|
2330
|
+
nickName?: string;
|
|
1890
2331
|
/**
|
|
1891
|
-
*
|
|
2332
|
+
* Optional SCIM account state.
|
|
2333
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1892
2334
|
*/
|
|
1893
|
-
|
|
2335
|
+
active?: boolean;
|
|
1894
2336
|
/**
|
|
1895
|
-
*
|
|
2337
|
+
* Optional SCIM job title.
|
|
2338
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1896
2339
|
*/
|
|
1897
|
-
|
|
2340
|
+
title?: string;
|
|
1898
2341
|
/**
|
|
1899
|
-
*
|
|
2342
|
+
* Optional SCIM preferred language.
|
|
2343
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1900
2344
|
*/
|
|
1901
|
-
|
|
2345
|
+
preferredLanguage?: string;
|
|
1902
2346
|
/**
|
|
1903
|
-
*
|
|
2347
|
+
* Optional SCIM locale.
|
|
2348
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1904
2349
|
*/
|
|
1905
|
-
|
|
2350
|
+
locale?: string;
|
|
1906
2351
|
/**
|
|
1907
|
-
*
|
|
2352
|
+
* Optional SCIM timezone.
|
|
2353
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1908
2354
|
*/
|
|
1909
|
-
|
|
2355
|
+
timezone?: string;
|
|
1910
2356
|
/**
|
|
1911
|
-
*
|
|
2357
|
+
* Optional SCIM phone numbers.
|
|
2358
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1912
2359
|
*/
|
|
1913
|
-
|
|
1914
|
-
};
|
|
1915
|
-
type SecretsValidators = {
|
|
2360
|
+
phoneNumbers?: PhoneNumber[];
|
|
1916
2361
|
/**
|
|
1917
|
-
* Optional
|
|
1918
|
-
*
|
|
2362
|
+
* Optional SCIM instant messaging addresses.
|
|
2363
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1919
2364
|
*/
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
listSecretsSources(): string[];
|
|
1927
|
-
/** Gets a named secrets source client. Throws when missing. */
|
|
1928
|
-
getSecretsSource(sourceName: string): SecretsSource;
|
|
1929
|
-
/** Reads a secret from a named secrets source client. */
|
|
1930
|
-
getSecret<T>(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<T>;
|
|
1931
|
-
/** Reads full secret data + metadata from a named secrets source client. */
|
|
1932
|
-
getFullSecret<T>(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<Secret<T>>;
|
|
1933
|
-
/** Writes a secret to a named secrets source client. */
|
|
1934
|
-
putSecret(sourceName: string, path: string, value: Record<string, unknown>, options?: SecretsOperationOptions): Promise<void>;
|
|
1935
|
-
/** Deletes a secret from a named secrets source client. */
|
|
1936
|
-
deleteSecret(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<void>;
|
|
1937
|
-
/** Lists child paths under a base path for a named secrets source client. */
|
|
1938
|
-
listPaths(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<string[]>;
|
|
1939
|
-
/** Returns true when a path exists for a named secrets source client. */
|
|
1940
|
-
exists(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<boolean>;
|
|
1941
|
-
/** Requests rotation for a secret path in a named secrets source client. */
|
|
1942
|
-
requestRotate(sourceName: string, path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
1943
|
-
/** Requests revocation for a secret path in a named secrets source client. */
|
|
1944
|
-
requestRevoke(sourceName: string, path: string, request?: SecretLifecycleRequest, options?: SecretsOperationOptions): Promise<void>;
|
|
1945
|
-
/** Reads metadata for a secret path in a named secrets source client. */
|
|
1946
|
-
getMetadata(sourceName: string, path: string, options?: SecretsOperationOptions): Promise<Record<string, unknown>>;
|
|
1947
|
-
/** Subscribes to secret changes on a named secrets source client. */
|
|
1948
|
-
subscribe<T>(sourceName: string, path: string, onChange: (fullSecret: Secret<T>) => void): () => void;
|
|
1949
|
-
/** Returns true when request matches any configured LFV delivery path. */
|
|
1950
|
-
isLfvDeliveryRequest?(request: Request): boolean;
|
|
1951
|
-
/** Returns true when request matches any configured LFV events path. */
|
|
1952
|
-
isLfvEventsRequest?(request: Request): boolean;
|
|
1953
|
-
/** Handles LFV delivery callbacks for configured LFV sources. */
|
|
1954
|
-
handleLfvDelivery?(request: Request): Promise<Response>;
|
|
1955
|
-
/** Handles LFV events callbacks for configured LFV sources. */
|
|
1956
|
-
handleLfvEvents?(request: Request): Promise<Response>;
|
|
1957
|
-
};
|
|
1958
|
-
/**
|
|
1959
|
-
* Partial secrets source config used in framework/app code to declare expected source names.
|
|
1960
|
-
* ConfigSource-backed values may still provide the actual source details at runtime.
|
|
1961
|
-
*/
|
|
1962
|
-
type FrameworkSecretsSourceConfig = Partial<SecretsSourceConfig>;
|
|
1963
|
-
/**
|
|
1964
|
-
* Framework-level named secrets source declarations keyed by source name.
|
|
1965
|
-
* Values may be partial or empty when the app only wants to declare expected names/types.
|
|
1966
|
-
*/
|
|
1967
|
-
type FrameworkSecretsModuleConfig = Record<string, FrameworkSecretsSourceConfig>;
|
|
1968
|
-
/**
|
|
1969
|
-
* TODO: Let's see if we can do some clean inference and remove this!!!
|
|
1970
|
-
*/
|
|
1971
|
-
type SecretsSourceMap = Record<string, SecretsSource>;
|
|
1972
|
-
type SecretsSourceConfig = DevSecretsConfig | GcpSecretsConfig | VaultSecretsConfig | AwsSecretsConfig | AzureSecretsConfig;
|
|
1973
|
-
/**
|
|
1974
|
-
* Raw module config keyed by source name.
|
|
1975
|
-
* The secrets module resolves this into a runtime SecretsSourceMap.
|
|
1976
|
-
*/
|
|
1977
|
-
type SecretsModuleConfig = Record<string, SecretsSourceConfig>;
|
|
1978
|
-
type DevSecretsConfig = {
|
|
1979
|
-
type: "dev";
|
|
1980
|
-
ioniteUrl: string;
|
|
1981
|
-
};
|
|
1982
|
-
type GcpSecretsConfig = {
|
|
1983
|
-
type: "gcp";
|
|
1984
|
-
};
|
|
1985
|
-
type VaultLfvSecretsConfig = {
|
|
1986
|
-
/** LFV server base URL for OTP/action endpoints. */
|
|
1987
|
-
lfvServerUrl?: string;
|
|
1988
|
-
/** LFV client id used for OTP issuance. */
|
|
1989
|
-
clientId?: string;
|
|
1990
|
-
/** Signature value for X-LFV-Signature header. */
|
|
1991
|
-
signature?: string;
|
|
1992
|
-
deliveryEndpoint: string;
|
|
2365
|
+
ims?: Array<{
|
|
2366
|
+
value: string;
|
|
2367
|
+
display?: string;
|
|
2368
|
+
type?: string;
|
|
2369
|
+
primary?: boolean;
|
|
2370
|
+
}>;
|
|
1993
2371
|
/**
|
|
1994
|
-
* Optional
|
|
1995
|
-
*
|
|
2372
|
+
* Optional SCIM photos.
|
|
2373
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
1996
2374
|
*/
|
|
1997
|
-
|
|
1998
|
-
eventsEndpoint: string;
|
|
1999
|
-
path?: string;
|
|
2375
|
+
photos?: Photo[];
|
|
2000
2376
|
/**
|
|
2001
|
-
*
|
|
2002
|
-
*
|
|
2377
|
+
* Optional SCIM addresses.
|
|
2378
|
+
* Commonly set by IAM inbound SCIM provisioning flows.
|
|
2003
2379
|
*/
|
|
2004
|
-
|
|
2005
|
-
/** Retry interval in milliseconds between LFV transport retry attempts. */
|
|
2006
|
-
retryInterval?: number;
|
|
2007
|
-
/** Warning interval in milliseconds for LFV retry logs. Set to 0 to disable warnings. */
|
|
2008
|
-
warnInterval?: number;
|
|
2380
|
+
addresses?: Address[];
|
|
2009
2381
|
/**
|
|
2010
|
-
* Optional
|
|
2011
|
-
*
|
|
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.
|
|
2012
2389
|
*/
|
|
2013
|
-
|
|
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;
|
|
2014
2435
|
};
|
|
2015
2436
|
/**
|
|
2016
|
-
*
|
|
2017
|
-
*
|
|
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
|
+
* ```
|
|
2018
2463
|
*/
|
|
2019
|
-
|
|
2020
|
-
lfvServerUrl: string;
|
|
2021
|
-
clientId: string;
|
|
2022
|
-
path: string;
|
|
2023
|
-
};
|
|
2024
|
-
type VaultWebSocketAuthHeader = "X-Vault-Token" | "Authorization";
|
|
2025
|
-
type VaultWebSocketSecretsConfig = {
|
|
2026
|
-
/** Websocket URL for vault command execution and live secret subscriptions. */
|
|
2027
|
-
url?: string;
|
|
2028
|
-
/** Token used during websocket connect/auth. */
|
|
2029
|
-
token?: string;
|
|
2030
|
-
/** Header name used to send the websocket token. Defaults to X-Vault-Token. */
|
|
2031
|
-
header?: VaultWebSocketAuthHeader;
|
|
2032
|
-
};
|
|
2033
|
-
type VaultSecretsConfig = {
|
|
2034
|
-
type: "vault";
|
|
2035
|
-
url?: string;
|
|
2036
|
-
token?: string;
|
|
2037
|
-
/** Optional LFV transport capability for reads/lifecycle operations. */
|
|
2038
|
-
lfv?: VaultLfvSecretsConfig;
|
|
2039
|
-
/** Optional websocket capability for vault commands and live subscriptions. */
|
|
2040
|
-
websocket?: VaultWebSocketSecretsConfig;
|
|
2464
|
+
interface UserStore<TExtended = object> {
|
|
2041
2465
|
/**
|
|
2042
|
-
*
|
|
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
|
|
2043
2473
|
*/
|
|
2044
|
-
|
|
2045
|
-
};
|
|
2046
|
-
type AwsSecretsConfig = {
|
|
2047
|
-
type: "aws";
|
|
2048
|
-
webhookUrl: string;
|
|
2049
|
-
};
|
|
2050
|
-
type AwsAuthMethod = "client_secret" | "workload_identity" | "managed_identity";
|
|
2051
|
-
type AzureSecretsConfig = {
|
|
2052
|
-
type: "azure";
|
|
2474
|
+
get(sub: string): Promise<StoredUser<TExtended> | undefined>;
|
|
2053
2475
|
/**
|
|
2054
|
-
*
|
|
2476
|
+
* Retrieve a user by their username.
|
|
2055
2477
|
*
|
|
2056
|
-
*
|
|
2057
|
-
*
|
|
2058
|
-
|
|
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.
|
|
2059
2484
|
*
|
|
2060
|
-
* If
|
|
2061
|
-
*
|
|
2062
|
-
* - else client_secret if a clientSecret is present
|
|
2063
|
-
* - else managed_identity
|
|
2485
|
+
* If a user with the same `id` (sub) exists, it will be updated.
|
|
2486
|
+
* Otherwise, a new user will be created.
|
|
2064
2487
|
*
|
|
2065
|
-
*
|
|
2488
|
+
* @param user - The user data to store
|
|
2066
2489
|
*/
|
|
2067
|
-
|
|
2490
|
+
upsert(user: StoredUser<TExtended>): Promise<StoredUser<TExtended>>;
|
|
2068
2491
|
/**
|
|
2069
|
-
*
|
|
2070
|
-
*
|
|
2492
|
+
* Delete a user by their subject identifier (sub).
|
|
2493
|
+
*
|
|
2494
|
+
* @param sub - The user's unique identifier to delete
|
|
2071
2495
|
*/
|
|
2072
|
-
|
|
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 {
|
|
2073
2537
|
/**
|
|
2074
|
-
*
|
|
2075
|
-
* Env: ES_AZURE_CLIENT_ID
|
|
2538
|
+
* Base path for the SCIM Users endpoints (e.g., '/api/iam/Users')
|
|
2076
2539
|
*/
|
|
2077
|
-
|
|
2540
|
+
usersUrl?: string;
|
|
2078
2541
|
/**
|
|
2079
|
-
*
|
|
2080
|
-
* Env: ES_AZURE_CLIENT_SECRET
|
|
2542
|
+
* Base path for the SCIM Groups endpoints (e.g., '/api/iam/Groups')
|
|
2081
2543
|
*/
|
|
2082
|
-
|
|
2544
|
+
groupsUrl?: string;
|
|
2083
2545
|
/**
|
|
2084
|
-
*
|
|
2085
|
-
* Env: ES_AZURE_FEDERATED_TOKEN_FILE or AZURE_FEDERATED_TOKEN_FILE
|
|
2546
|
+
* Handler overrides for SCIM discovery endpoints (e.g., '/api/iam/ServiceProviderConfig')
|
|
2086
2547
|
*/
|
|
2087
|
-
|
|
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 = {
|
|
2088
2558
|
/**
|
|
2089
|
-
*
|
|
2090
|
-
*
|
|
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)
|
|
2091
2561
|
*/
|
|
2092
|
-
|
|
2562
|
+
url?: string;
|
|
2093
2563
|
/**
|
|
2094
|
-
*
|
|
2095
|
-
*
|
|
2096
|
-
* Default: 2018-02-01
|
|
2564
|
+
* Store for inbound user provisioning from external IAM providers.
|
|
2565
|
+
* When configured, the app can receive user CRUD operations via SCIM.
|
|
2097
2566
|
*/
|
|
2098
|
-
|
|
2567
|
+
userStore?: UserStore;
|
|
2099
2568
|
/**
|
|
2100
|
-
*
|
|
2101
|
-
*
|
|
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.
|
|
2102
2572
|
*/
|
|
2103
|
-
|
|
2573
|
+
inboundUsers?: IAMInboundUsersConfig;
|
|
2104
2574
|
/**
|
|
2105
|
-
*
|
|
2106
|
-
*
|
|
2107
|
-
* Env: ES_AZURE_KEY_VAULT_NAME
|
|
2575
|
+
* Store for inbound group provisioning from external IAM providers.
|
|
2576
|
+
* When configured, enables groups_inbound (external IAM -> app).
|
|
2108
2577
|
*/
|
|
2109
|
-
|
|
2578
|
+
groupStore?: GroupStore;
|
|
2110
2579
|
/**
|
|
2111
|
-
*
|
|
2112
|
-
*
|
|
2113
|
-
* Default: 7.4
|
|
2580
|
+
* Optional handler defaults. These are merged with per-call overrides in
|
|
2581
|
+
* `iam.handler`, with per-call values taking precedence.
|
|
2114
2582
|
*/
|
|
2115
|
-
|
|
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 {
|
|
2116
2592
|
/**
|
|
2117
|
-
*
|
|
2118
|
-
* Default: replaces `/` with `--` and trims leading/trailing `/`.
|
|
2593
|
+
* Current stored user when replacing or patching an existing resource.
|
|
2119
2594
|
*/
|
|
2120
|
-
|
|
2595
|
+
existing?: StoredUser;
|
|
2121
2596
|
/**
|
|
2122
|
-
*
|
|
2123
|
-
* Useful to namespace secrets by app/environment.
|
|
2597
|
+
* Operation mode for the inbound mapper.
|
|
2124
2598
|
*/
|
|
2125
|
-
|
|
2599
|
+
mode: "create" | "replace" | "patch";
|
|
2600
|
+
}
|
|
2601
|
+
interface IAMInboundUsersConfig {
|
|
2126
2602
|
/**
|
|
2127
|
-
*
|
|
2128
|
-
* Default: https://vault.azure.net/.default
|
|
2603
|
+
* Replace the default validated SCIM -> StoredUser mapper.
|
|
2129
2604
|
*/
|
|
2130
|
-
|
|
2605
|
+
mapValidatedScimToStoredUser?: (validated: User, context: IAMInboundUserContext) => StoredUser | Promise<StoredUser>;
|
|
2131
2606
|
/**
|
|
2132
|
-
*
|
|
2607
|
+
* Replace the default StoredUser -> SCIM response mapper.
|
|
2133
2608
|
*/
|
|
2134
|
-
|
|
2135
|
-
};
|
|
2136
|
-
type EnvironmentType = "POC" | "DEV" | "QA" | "PROD";
|
|
2137
|
-
type TenantStatus = "pending" | "processing" | "completed" | "failed" | "action_required";
|
|
2138
|
-
interface UpsertTenantRequest {
|
|
2139
|
-
tenantId: string;
|
|
2140
|
-
companyId: string;
|
|
2141
|
-
companyName: string;
|
|
2142
|
-
environmentType: EnvironmentType;
|
|
2143
|
-
email?: string;
|
|
2144
|
-
webhookUrl?: string;
|
|
2145
|
-
callbackUrl?: string;
|
|
2146
|
-
configSource: TenantSecretsConfig;
|
|
2147
|
-
}
|
|
2148
|
-
type UpsertTenantResponse = {
|
|
2149
|
-
tenantUrl?: string;
|
|
2150
|
-
status: Exclude<TenantStatus, "action_required">;
|
|
2151
|
-
error?: string;
|
|
2152
|
-
refs?: RefUrls[];
|
|
2153
|
-
} | {
|
|
2154
|
-
status: "action_required";
|
|
2155
|
-
actionUrl: string;
|
|
2156
|
-
requestToken: string;
|
|
2157
|
-
expiresAt: string;
|
|
2158
|
-
refs?: RefUrls[];
|
|
2159
|
-
};
|
|
2160
|
-
type CreateTenantRequest = UpsertTenantRequest;
|
|
2161
|
-
type CreateTenantResponse = UpsertTenantResponse;
|
|
2162
|
-
/**
|
|
2163
|
-
* The audience of the reference URL.
|
|
2164
|
-
* - 'human' for human-readable documentation such as user guides, documentation, etc.
|
|
2165
|
-
* - 'ai' for AI-generated reference such as llms.txt, AI-optimized markdown, etc.
|
|
2166
|
-
* - 'spec' for specifications and code-gen ready docs such as OpenAPI, GraphQL, etc.
|
|
2167
|
-
*/
|
|
2168
|
-
type Audience = "human" | "ai" | "spec";
|
|
2169
|
-
interface RefUrls {
|
|
2170
|
-
audience: Audience;
|
|
2171
|
-
url: string;
|
|
2172
|
-
description: string;
|
|
2173
|
-
createdAt: Date;
|
|
2174
|
-
updatedAt: Date;
|
|
2609
|
+
mapStoredUserToScim?: (stored: StoredUser) => User | Promise<User>;
|
|
2175
2610
|
}
|
|
2176
|
-
interface
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
expiresAt?: string;
|
|
2184
|
-
error?: string;
|
|
2611
|
+
interface ScimAuthenticationScheme {
|
|
2612
|
+
type: string;
|
|
2613
|
+
name: string;
|
|
2614
|
+
description?: string;
|
|
2615
|
+
specUri?: string;
|
|
2616
|
+
documentationUri?: string;
|
|
2617
|
+
primary?: boolean;
|
|
2185
2618
|
}
|
|
2186
|
-
|
|
2187
|
-
|
|
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
|
+
};
|
|
2188
2648
|
}
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
constructor(userId: string, tenantIds: string[], options?: ErrorOptions);
|
|
2649
|
+
interface ScimResourceTypeSchemaExtension {
|
|
2650
|
+
schema: string;
|
|
2651
|
+
required: boolean;
|
|
2193
2652
|
}
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
type TenantSecretsConfig = (VaultSecretsConfig & {
|
|
2241
|
-
path: string;
|
|
2242
|
-
retryInterval?: number;
|
|
2243
|
-
}) | (AwsSecretsConfig & {
|
|
2244
|
-
ttl?: number;
|
|
2245
|
-
}) | AzureSecretsConfig | (GcpSecretsConfig & {
|
|
2246
|
-
ttl?: number;
|
|
2247
|
-
});
|
|
2248
|
-
type TenantStoredConfigLocator = {
|
|
2249
|
-
/** Indicates that the tenant config descriptor is stored securely outside the tenant record. */
|
|
2250
|
-
type: "stored";
|
|
2251
|
-
/** Root secure source type used to fetch the stored tenant config descriptor. */
|
|
2252
|
-
sourceType: "vault";
|
|
2253
|
-
/** Path to the stored tenant config descriptor. */
|
|
2254
|
-
path: string;
|
|
2255
|
-
};
|
|
2256
|
-
type TenantRemoteConfigLocator = {
|
|
2257
|
-
/** Indicates that the tenant RemoteConfig already exists at this secure source path. */
|
|
2258
|
-
type: "remoteConfig";
|
|
2259
|
-
/** Secure source type used to load the RemoteConfig document directly. */
|
|
2260
|
-
sourceType: "vault";
|
|
2261
|
-
/** Path to the tenant RemoteConfig document. */
|
|
2262
|
-
path: string;
|
|
2263
|
-
};
|
|
2264
|
-
type TenantConfigLocator = TenantStoredConfigLocator | TenantRemoteConfigLocator;
|
|
2265
|
-
type TenantConfigSourceInput = TenantConfigLocator | ConfigSource;
|
|
2266
|
-
type TenantBaseRecord = {
|
|
2267
|
-
tenantId: string;
|
|
2268
|
-
companyId: string;
|
|
2269
|
-
companyName: string;
|
|
2270
|
-
environmentType: EnvironmentType;
|
|
2271
|
-
email?: string;
|
|
2272
|
-
webhookUrl?: string;
|
|
2273
|
-
callbackUrl?: string;
|
|
2274
|
-
tenantUrl?: string;
|
|
2275
|
-
status: TenantStatus;
|
|
2276
|
-
error?: string;
|
|
2277
|
-
actionUrl?: string;
|
|
2278
|
-
requestToken?: string;
|
|
2279
|
-
expiresAt?: string;
|
|
2280
|
-
createdAt: Date;
|
|
2281
|
-
updatedAt: Date;
|
|
2282
|
-
/** Persisted tenant config metadata, or a runtime ConfigSource for internal-only tenants. */
|
|
2283
|
-
configSource: TenantConfigSourceInput;
|
|
2284
|
-
/** Runtime helper that returns a ConfigSource for this tenant. */
|
|
2285
|
-
config: (source?: SecretsSource) => ConfigSource;
|
|
2286
|
-
};
|
|
2287
|
-
type StoredTenant<TExtended extends object = Record<string, never>> = TenantBaseRecord & TExtended;
|
|
2288
|
-
type StoredTenantRecord<TExtended extends object = Record<string, never>> = Omit<StoredTenant<TExtended>, "config">;
|
|
2289
|
-
type TenantEsFactory<TExtended extends object = Record<string, never>> = (tenant: StoredTenant<TExtended>) => EnterpriseStandard;
|
|
2290
|
-
type TenantConfigStoreRequest<TExtended extends object = Record<string, never>> = {
|
|
2291
|
-
es: EnterpriseStandard;
|
|
2292
|
-
tenantId: string;
|
|
2293
|
-
request: UpsertTenantRequest;
|
|
2294
|
-
configData: TenantSecretsConfig;
|
|
2295
|
-
existingTenant: StoredTenant<TExtended> | null;
|
|
2296
|
-
};
|
|
2297
|
-
type TenantStoreWithESOptions<TExtended extends object = Record<string, never>> = {
|
|
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 {
|
|
2298
2699
|
/**
|
|
2299
|
-
*
|
|
2300
|
-
*
|
|
2700
|
+
* Public IAM base path used for SCIM discovery. When omitted, the SDK derives
|
|
2701
|
+
* it from `usersUrl` or `groupsUrl`.
|
|
2301
2702
|
*/
|
|
2302
|
-
|
|
2703
|
+
basePath?: string;
|
|
2303
2704
|
/**
|
|
2304
|
-
* Optional
|
|
2305
|
-
* If omitted, getEs() throws.
|
|
2705
|
+
* Optional documentation URI advertised in ServiceProviderConfig.
|
|
2306
2706
|
*/
|
|
2307
|
-
|
|
2308
|
-
};
|
|
2309
|
-
type TenantMetadataRecord = Omit<TenantBaseRecord, "tenantId" | "config" | "configSource" | "status" | "createdAt" | "updatedAt">;
|
|
2310
|
-
type TenantExtendedUpsertFields<TExtended extends object> = string extends keyof TExtended ? unknown : Partial<TExtended>;
|
|
2311
|
-
type TenantStoreUpsertRecord<TExtended extends object = Record<string, never>> = Pick<TenantBaseRecord, "tenantId" | "configSource"> & Partial<TenantMetadataRecord> & Partial<Pick<TenantBaseRecord, "status" | "createdAt" | "updatedAt">> & TenantExtendedUpsertFields<TExtended>;
|
|
2312
|
-
type TenantUserRegistration<TMode extends UserMode = UserMode> = {
|
|
2313
|
-
userMode: TMode;
|
|
2314
|
-
registerUserTenantId?(userId: string, tenantId: string | null | undefined): void | Promise<void>;
|
|
2315
|
-
};
|
|
2316
|
-
type TenantStoreBase<
|
|
2317
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2318
|
-
TExtended extends object = Record<string, never>
|
|
2319
|
-
> = TenantUserRegistration<TMode> & {
|
|
2320
|
-
storeConfig(config: TenantConfigStoreRequest<TExtended>): Promise<TenantConfigSourceInput>;
|
|
2321
|
-
get(tenantId: string): Promise<StoredTenant<TExtended> | null>;
|
|
2322
|
-
list(options?: TenantListOptions): Promise<ListResult<StoredTenant<TExtended>>>;
|
|
2323
|
-
upsert(tenant: TenantStoreUpsertRecord<TExtended>): Promise<StoredTenant<TExtended>>;
|
|
2324
|
-
delete(tenantId: string): Promise<void>;
|
|
2325
|
-
};
|
|
2326
|
-
type TenantLookupMethods<
|
|
2327
|
-
TMode extends UserMode,
|
|
2328
|
-
TExtended extends object
|
|
2329
|
-
> = TMode extends "singleTenantOnly" ? {
|
|
2330
|
-
findTenantByUserId(userId: string): Promise<StoredTenant<TExtended> | null>;
|
|
2331
|
-
findTenantsByUserId?: never;
|
|
2332
|
-
} : {
|
|
2333
|
-
findTenantByUserId?: never;
|
|
2334
|
-
findTenantsByUserId(userId: string): Promise<StoredTenant<TExtended>[]>;
|
|
2335
|
-
};
|
|
2336
|
-
type TenantStore<
|
|
2337
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2338
|
-
TExtended extends object = Record<string, never>
|
|
2339
|
-
> = TenantStoreBase<TMode, TExtended> & TenantLookupMethods<TMode, TExtended>;
|
|
2340
|
-
type TenantStoreWithES<
|
|
2341
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2342
|
-
TExtended extends object = Record<string, never>
|
|
2343
|
-
> = TenantStore<TMode, TExtended> & {
|
|
2344
|
-
ttl: number;
|
|
2345
|
-
getEs(tenantId: string): Promise<EnterpriseStandard | null>;
|
|
2346
|
-
getCachedTenantIds(): string[];
|
|
2347
|
-
};
|
|
2348
|
-
type InMemoryTenantStoreOptions<
|
|
2349
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2350
|
-
TExtended extends object = Record<string, never>
|
|
2351
|
-
> = TenantStoreWithESOptions<TExtended> & {
|
|
2352
|
-
userMode: TMode;
|
|
2353
|
-
};
|
|
2354
|
-
declare class InMemoryTenantStore<
|
|
2355
|
-
TMode extends UserMode = "singleTenantOnly",
|
|
2356
|
-
TExtended extends object = Record<string, never>
|
|
2357
|
-
> {
|
|
2358
|
-
private tenants;
|
|
2359
|
-
private tenantEsMap;
|
|
2360
|
-
private userTenantIds;
|
|
2361
|
-
readonly ttl: number;
|
|
2362
|
-
readonly userMode: TMode;
|
|
2363
|
-
private readonly createEs?;
|
|
2364
|
-
readonly findTenantByUserId: TMode extends "singleTenantOnly" ? (userId: string) => Promise<StoredTenant<TExtended> | null> : never;
|
|
2365
|
-
readonly findTenantsByUserId: TMode extends "multipleTenantsPerUser" ? (userId: string) => Promise<StoredTenant<TExtended>[]> : never;
|
|
2366
|
-
constructor(options: InMemoryTenantStoreOptions<TMode, TExtended>);
|
|
2367
|
-
get(tenantId: string): Promise<StoredTenant<TExtended> | null>;
|
|
2368
|
-
list(options?: TenantListOptions): Promise<ListResult<StoredTenant<TExtended>>>;
|
|
2369
|
-
upsert(tenant: TenantStoreUpsertRecord<TExtended>): Promise<StoredTenant<TExtended>>;
|
|
2370
|
-
delete(tenantId: string): Promise<void>;
|
|
2371
|
-
getEs(tenantId: string): Promise<EnterpriseStandard>;
|
|
2372
|
-
getCachedTenantIds(): string[];
|
|
2373
|
-
registerUserTenantId(userId: string, tenantId: string | null | undefined): Promise<void>;
|
|
2374
|
-
private findSingleTenantByUserId;
|
|
2375
|
-
private findMultipleTenantsByUserId;
|
|
2376
|
-
private resolveTenantsByUserId;
|
|
2377
|
-
}
|
|
2378
|
-
declare function sendTenantWebhook(webhookUrl: string, payload: TenantWebhookPayload, log: Logger): Promise<void>;
|
|
2379
|
-
/**
|
|
2380
|
-
* Magic link data stored in the store.
|
|
2381
|
-
*
|
|
2382
|
-
* @template TExtended - Type-safe custom data that consumers can add to magic links
|
|
2383
|
-
*/
|
|
2384
|
-
type MagicLink<TExtended = object> = {
|
|
2707
|
+
documentationUri?: string;
|
|
2385
2708
|
/**
|
|
2386
|
-
*
|
|
2709
|
+
* Override the default ServiceProviderConfig response.
|
|
2387
2710
|
*/
|
|
2388
|
-
|
|
2711
|
+
buildServiceProviderConfig?: (context: IAMDiscoveryContext, defaults: ScimServiceProviderConfig) => ScimServiceProviderConfig | Promise<ScimServiceProviderConfig>;
|
|
2389
2712
|
/**
|
|
2390
|
-
*
|
|
2713
|
+
* Override the default ResourceTypes response.
|
|
2391
2714
|
*/
|
|
2392
|
-
|
|
2715
|
+
buildResourceTypes?: (context: IAMDiscoveryContext, defaults: ScimResourceType[]) => ScimResourceType[] | Promise<ScimResourceType[]>;
|
|
2393
2716
|
/**
|
|
2394
|
-
*
|
|
2717
|
+
* Override the default Schemas response.
|
|
2395
2718
|
*/
|
|
2396
|
-
|
|
2719
|
+
buildSchemas?: (context: IAMDiscoveryContext, defaults: ScimSchemaDefinition[]) => ScimSchemaDefinition[] | Promise<ScimSchemaDefinition[]>;
|
|
2720
|
+
}
|
|
2721
|
+
/**
|
|
2722
|
+
* Options for creating a user
|
|
2723
|
+
*/
|
|
2724
|
+
interface CreateUserOptions {
|
|
2397
2725
|
/**
|
|
2398
|
-
*
|
|
2726
|
+
* External identifier for the user
|
|
2399
2727
|
*/
|
|
2400
|
-
|
|
2728
|
+
externalId?: string;
|
|
2729
|
+
}
|
|
2730
|
+
/**
|
|
2731
|
+
* Options for creating a group
|
|
2732
|
+
*/
|
|
2733
|
+
interface CreateGroupOptions {
|
|
2401
2734
|
/**
|
|
2402
|
-
*
|
|
2735
|
+
* External identifier for the group
|
|
2403
2736
|
*/
|
|
2404
|
-
|
|
2405
|
-
|
|
2737
|
+
externalId?: string;
|
|
2738
|
+
/**
|
|
2739
|
+
* Initial members to add to the group
|
|
2740
|
+
*/
|
|
2741
|
+
members?: GroupMember[];
|
|
2742
|
+
}
|
|
2406
2743
|
/**
|
|
2407
|
-
*
|
|
2408
|
-
*
|
|
2409
|
-
* Consumers can implement this interface to use different storage backends:
|
|
2410
|
-
* - In-memory (for development/testing)
|
|
2411
|
-
* - Redis (for production with fast lookups and automatic expiration)
|
|
2412
|
-
* - Database (PostgreSQL, MySQL, etc.)
|
|
2413
|
-
*
|
|
2414
|
-
* @template TExtended - Type-safe custom data that consumers can add to magic links
|
|
2415
|
-
*
|
|
2416
|
-
* @example
|
|
2417
|
-
* ```typescript
|
|
2418
|
-
* // Custom magic link data
|
|
2419
|
-
* type MyMagicLinkData = {
|
|
2420
|
-
* source: string;
|
|
2421
|
-
* metadata: Record<string, unknown>;
|
|
2422
|
-
* };
|
|
2423
|
-
*
|
|
2424
|
-
* // Implement custom store
|
|
2425
|
-
* class RedisMagicLinkStore implements MagicLinkStore<MyMagicLinkData> {
|
|
2426
|
-
* async create(token: string, user: BaseUser, expiresAt: Date): Promise<void> {
|
|
2427
|
-
* const magicLink: MagicLink<MyMagicLinkData> = {
|
|
2428
|
-
* token,
|
|
2429
|
-
* user,
|
|
2430
|
-
* createdAt: new Date(),
|
|
2431
|
-
* expiresAt,
|
|
2432
|
-
* source: 'api',
|
|
2433
|
-
* metadata: {},
|
|
2434
|
-
* };
|
|
2435
|
-
* const ttl = Math.floor((expiresAt.getTime() - Date.now()) / 1000);
|
|
2436
|
-
* await redis.setex(`magic-link:${token}`, ttl, JSON.stringify(magicLink));
|
|
2437
|
-
* }
|
|
2438
|
-
* // ... other methods
|
|
2439
|
-
* }
|
|
2440
|
-
* ```
|
|
2744
|
+
* Handler configuration for groups_inbound
|
|
2441
2745
|
*/
|
|
2442
|
-
interface
|
|
2746
|
+
interface GroupsInboundHandlerConfig {
|
|
2443
2747
|
/**
|
|
2444
|
-
*
|
|
2445
|
-
*
|
|
2446
|
-
* @param token - The magic link token (unique identifier)
|
|
2447
|
-
* @param user - The user information to associate with this magic link
|
|
2448
|
-
* @param expiresAt - When the magic link expires
|
|
2449
|
-
* @throws Error if magic link with same token already exists
|
|
2748
|
+
* Base path for the SCIM Groups endpoints (e.g., '/api/iam/Groups')
|
|
2450
2749
|
*/
|
|
2451
|
-
|
|
2750
|
+
basePath?: string;
|
|
2751
|
+
}
|
|
2752
|
+
/**
|
|
2753
|
+
* Handler configuration for users_inbound
|
|
2754
|
+
*/
|
|
2755
|
+
interface UsersInboundHandlerConfig {
|
|
2452
2756
|
/**
|
|
2453
|
-
*
|
|
2454
|
-
*
|
|
2455
|
-
* @param token - The magic link token
|
|
2456
|
-
* @returns The magic link if found and not expired, null otherwise
|
|
2757
|
+
* Base path for the SCIM Users endpoints (e.g., '/api/iam/Users')
|
|
2457
2758
|
*/
|
|
2458
|
-
|
|
2759
|
+
basePath?: string;
|
|
2760
|
+
}
|
|
2761
|
+
interface IAMDiscoveryHandlerConfig {
|
|
2459
2762
|
/**
|
|
2460
|
-
*
|
|
2461
|
-
*
|
|
2462
|
-
* Used after a magic link has been consumed (one-time use).
|
|
2463
|
-
*
|
|
2464
|
-
* @param token - The magic link token to delete
|
|
2763
|
+
* Base path for the IAM discovery endpoints (e.g., '/api/iam')
|
|
2465
2764
|
*/
|
|
2466
|
-
|
|
2765
|
+
basePath?: string;
|
|
2467
2766
|
}
|
|
2468
|
-
type ConfigSourceType = "vault" | "azure" | "aws" | "gcp";
|
|
2469
|
-
type ESValidators = {
|
|
2470
|
-
sso: SSOValidators;
|
|
2471
|
-
iam: IAMValidators;
|
|
2472
|
-
workload: WorkloadValidators;
|
|
2473
|
-
ciam: CIAMValidators;
|
|
2474
|
-
secrets?: SecretsValidators;
|
|
2475
|
-
};
|
|
2476
|
-
type ApplicationValidators = ESValidators & {
|
|
2477
|
-
tenant: TenantValidators;
|
|
2478
|
-
};
|
|
2479
2767
|
/**
|
|
2480
|
-
*
|
|
2481
|
-
*
|
|
2482
|
-
* argument to enterpriseStandard(source, config).
|
|
2483
|
-
* Set a module to `null` to explicitly disable it; then the corresponding property on the
|
|
2484
|
-
* EnterpriseStandard instance is typed as `never`. Omit a module to allow it to be supplied
|
|
2485
|
-
* 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.
|
|
2486
2770
|
*/
|
|
2487
|
-
type
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
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>>;
|
|
2495
2779
|
};
|
|
2496
2780
|
/**
|
|
2497
|
-
*
|
|
2498
|
-
*
|
|
2499
|
-
* 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.
|
|
2500
2783
|
*/
|
|
2501
|
-
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
|
+
};
|
|
2502
2791
|
/**
|
|
2503
|
-
*
|
|
2792
|
+
* Users Inbound extension - for receiving user provisioning from external IAM providers.
|
|
2793
|
+
* Enabled when `userStore` is configured in IAMConfig.
|
|
2504
2794
|
*/
|
|
2505
|
-
type
|
|
2506
|
-
/** Optional app/tenant identifier for this ESA (e.g. from vault path or config). */
|
|
2507
|
-
tenantId?: string;
|
|
2508
|
-
sso?: SSOConfig;
|
|
2509
|
-
iam?: IAMConfig;
|
|
2795
|
+
type IAMUsersInbound = {
|
|
2510
2796
|
/**
|
|
2511
|
-
*
|
|
2512
|
-
*
|
|
2513
|
-
* Legacy: { default: { jwksUri, issuer }, TNT_*: { ... } } (flat map).
|
|
2514
|
-
* 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
|
|
2515
2799
|
*/
|
|
2516
|
-
|
|
2517
|
-
/** Optional named secrets-source configs available to this ESA instance. */
|
|
2518
|
-
secrets?: SecretsModuleConfig;
|
|
2519
|
-
ciam?: CIAMConfig;
|
|
2800
|
+
handler: (request: Request, config?: UsersInboundHandlerConfig) => Promise<Response>;
|
|
2520
2801
|
};
|
|
2521
2802
|
/**
|
|
2522
|
-
*
|
|
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
|
|
2523
2809
|
*/
|
|
2524
|
-
type
|
|
2525
|
-
sessionStore?: SessionStore<unknown>;
|
|
2526
|
-
userStore?: UserStore<unknown>;
|
|
2527
|
-
groupStore?: GroupStore<unknown>;
|
|
2528
|
-
magicLinkStore?: MagicLinkStore<unknown>;
|
|
2529
|
-
workloadTokenStore?: WorkloadTokenStore;
|
|
2530
|
-
};
|
|
2531
|
-
type ModifiableFrameworkConfig = FrameworkConfig & {
|
|
2532
|
-
setStores(stores: FrameworkStores): void;
|
|
2533
|
-
};
|
|
2534
|
-
/** Return type from the beforeChange hook passed to enterpriseStandard(). */
|
|
2535
|
-
type ESConfigChangeResult = {
|
|
2536
|
-
config?: RemoteConfig;
|
|
2537
|
-
frameworkConfig?: FrameworkConfig;
|
|
2538
|
-
};
|
|
2539
|
-
/** beforeChange callback invoked on every config application (initial load and updates). */
|
|
2540
|
-
type ESConfigChangeCallback = (config: RemoteConfig, frameworkConfig: ModifiableFrameworkConfig, oldConfig: RemoteConfig | undefined) => ESConfigChangeResult | void;
|
|
2541
|
-
type ConfigSource = {
|
|
2542
|
-
load(): Promise<RemoteConfig>;
|
|
2810
|
+
type IAM = IAMConfig & {
|
|
2543
2811
|
/**
|
|
2544
|
-
*
|
|
2545
|
-
*
|
|
2546
|
-
* @return an unsubscribe/cleanup function.
|
|
2812
|
+
* Create a new user/account in the external IAM provider
|
|
2813
|
+
* Only available when `url` is configured.
|
|
2547
2814
|
*/
|
|
2548
|
-
|
|
2815
|
+
createUser?: (user: User, options?: CreateUserOptions) => Promise<ScimResult<User>>;
|
|
2549
2816
|
/**
|
|
2550
|
-
*
|
|
2551
|
-
* For vault-backed sources this is the vault used to read RemoteConfig.
|
|
2817
|
+
* Get the configured external SCIM base URL
|
|
2552
2818
|
*/
|
|
2553
|
-
|
|
2819
|
+
getBaseUrl: () => string | undefined;
|
|
2554
2820
|
/**
|
|
2555
|
-
*
|
|
2556
|
-
*
|
|
2821
|
+
* Groups Outbound extension - create groups in external IAM provider.
|
|
2822
|
+
* Available when `url` is configured in IAMConfig.
|
|
2557
2823
|
*/
|
|
2558
|
-
|
|
2824
|
+
groups_outbound?: IAMGroupsOutbound;
|
|
2559
2825
|
/**
|
|
2560
|
-
*
|
|
2561
|
-
*
|
|
2826
|
+
* Groups Inbound extension - receive group provisioning from external IAM.
|
|
2827
|
+
* Available when `groupStore` is configured in IAMConfig.
|
|
2562
2828
|
*/
|
|
2563
|
-
|
|
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>;
|
|
2564
2840
|
};
|
|
2565
2841
|
/**
|
|
2566
2842
|
* Serializes a FrameworkConfig (or ESConfig) to a JSON-serializable object.
|
|
@@ -2599,7 +2875,8 @@ type WorkloadModuleFromConfig<C extends FrameworkConfig> = Exclude<C["workload"]
|
|
|
2599
2875
|
type EnterpriseStandardFromConfig<C extends FrameworkConfig = FrameworkConfig> = EnterpriseStandardStrict<C>;
|
|
2600
2876
|
/** Base shape shared by all EnterpriseStandard variants (modules optional for backward compatibility). */
|
|
2601
2877
|
type EnterpriseStandardBase = {
|
|
2602
|
-
logger
|
|
2878
|
+
/** Effective framework logger for this instance (from framework `log` or `defaultLogger`). */
|
|
2879
|
+
log: Logger;
|
|
2603
2880
|
/** App/tenant identifier when provided by ConfigSource (e.g. vault). */
|
|
2604
2881
|
tenantId?: string;
|
|
2605
2882
|
/** Most recent remote config applied to this instance (from ConfigSource, after beforeChange if any). */
|
|
@@ -2627,7 +2904,7 @@ type EnterpriseStandardBase = {
|
|
|
2627
2904
|
};
|
|
2628
2905
|
/** Config-driven module types: null in config → never; otherwise module type (non-optional). */
|
|
2629
2906
|
type EnterpriseStandardStrict<C extends FrameworkConfig> = {
|
|
2630
|
-
|
|
2907
|
+
log: Logger;
|
|
2631
2908
|
tenantId?: string;
|
|
2632
2909
|
config?: RemoteConfig;
|
|
2633
2910
|
secret: SecretsSource;
|
|
@@ -3213,6 +3490,12 @@ declare function mergeConfig<T extends Record<string, unknown>>(fromVault: T | u
|
|
|
3213
3490
|
declare function stripJsonComments(content: string): string;
|
|
3214
3491
|
declare function parseJsonc<T>(content: string): T;
|
|
3215
3492
|
/**
|
|
3493
|
+
* Deep equality for JSON-like values used in config snapshots.
|
|
3494
|
+
* Treats object key order as irrelevant and treats missing and `undefined`
|
|
3495
|
+
* object properties as equal by ignoring `undefined` keys on both sides.
|
|
3496
|
+
*/
|
|
3497
|
+
declare function deepEqualPlain(a: unknown, b: unknown): boolean;
|
|
3498
|
+
/**
|
|
3216
3499
|
* Waits for a HTTP service to be ready by polling its URL.
|
|
3217
3500
|
* Connection errors (e.g. connection refused) are treated as "not ready" and retried.
|
|
3218
3501
|
* @param url - The URL to poll.
|
|
@@ -3222,4 +3505,4 @@ declare function parseJsonc<T>(content: string): T;
|
|
|
3222
3505
|
* @returns A promise that resolves when the service is ready.
|
|
3223
3506
|
*/
|
|
3224
3507
|
declare function waitOn(url: string, test?: (resp: Response) => boolean | Promise<boolean>, pingInterval?: number, warnInterval?: number, timeout?: number): Promise<void>;
|
|
3225
|
-
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, 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 };
|