@redis/entraid 5.0.0-next.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # @redis/entraid
2
+
3
+ Secure token-based authentication for Redis clients using Microsoft Entra ID (formerly Azure Active Directory).
4
+
5
+ ## Features
6
+
7
+ - Token-based authentication using Microsoft Entra ID
8
+ - Automatic token refresh before expiration
9
+ - Automatic re-authentication of all connections after token refresh
10
+ - Support for multiple authentication flows:
11
+ - Managed identities (system-assigned and user-assigned)
12
+ - Service principals (with or without certificates)
13
+ - Authorization Code with PKCE flow
14
+ - Built-in retry mechanisms for transient failures
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @redis/client
20
+ npm install @redis/entraid
21
+ ```
22
+
23
+ ## Getting Started
24
+
25
+ The first step to using @redis/entraid is choosing the right credentials provider for your authentication needs. The `EntraIdCredentialsProviderFactory` class provides several factory methods to create the appropriate provider:
26
+
27
+ - `createForSystemAssignedManagedIdentity`: Use when your application runs in Azure with a system-assigned managed identity
28
+ - `createForUserAssignedManagedIdentity`: Use when your application runs in Azure with a user-assigned managed identity
29
+ - `createForClientCredentials`: Use when authenticating with a service principal using client secret
30
+ - `createForClientCredentialsWithCertificate`: Use when authenticating with a service principal using a certificate
31
+ - `createForAuthorizationCodeWithPKCE`: Use for interactive authentication flows in user applications
32
+
33
+ ## Usage Examples
34
+
35
+ ### Service Principal Authentication
36
+
37
+ ```typescript
38
+ import { createClient } from '@redis/client';
39
+ import { EntraIdCredentialsProviderFactory } from '@redis/entraid';
40
+
41
+ const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({
42
+ clientId: 'your-client-id',
43
+ clientSecret: 'your-client-secret',
44
+ authorityConfig: {
45
+ type: 'multi-tenant',
46
+ tenantId: 'your-tenant-id'
47
+ },
48
+ tokenManagerConfig: {
49
+ expirationRefreshRatio: 0.8 // Refresh token after 80% of its lifetime
50
+ }
51
+ });
52
+
53
+ const client = createClient({
54
+ url: 'redis://your-host',
55
+ credentialsProvider: provider
56
+ });
57
+
58
+ await client.connect();
59
+ ```
60
+
61
+ ### System-Assigned Managed Identity
62
+
63
+ ```typescript
64
+ const provider = EntraIdCredentialsProviderFactory.createForSystemAssignedManagedIdentity({
65
+ clientId: 'your-client-id',
66
+ tokenManagerConfig: {
67
+ expirationRefreshRatio: 0.8
68
+ }
69
+ });
70
+ ```
71
+
72
+ ### User-Assigned Managed Identity
73
+
74
+ ```typescript
75
+ const provider = EntraIdCredentialsProviderFactory.createForUserAssignedManagedIdentity({
76
+ clientId: 'your-client-id',
77
+ userAssignedClientId: 'your-user-assigned-client-id',
78
+ tokenManagerConfig: {
79
+ expirationRefreshRatio: 0.8
80
+ }
81
+ });
82
+ ```
83
+
84
+ ## Important Limitations
85
+
86
+ ### RESP2 PUB/SUB Limitations
87
+
88
+ When using RESP2 (Redis Serialization Protocol 2), there are important limitations with PUB/SUB:
89
+
90
+ - **No Re-Authentication in PUB/SUB Mode**: In RESP2, once a connection enters PUB/SUB mode, the socket is blocked and cannot process out-of-band commands like AUTH. This means that connections in PUB/SUB mode cannot be re-authenticated when tokens are refreshed.
91
+ - **Connection Eviction**: As a result, PUB/SUB connections will be evicted by the Redis proxy when their tokens expire. The client will need to establish new connections with fresh tokens.
92
+
93
+ ### Transaction Safety
94
+
95
+ When using token-based authentication, special care must be taken with Redis transactions. The token manager runs in the background and may attempt to re-authenticate connections at any time by sending AUTH commands. This can interfere with manually constructed transactions.
96
+
97
+ #### ✅ Recommended: Use the Official Transaction API
98
+
99
+ Always use the official transaction API provided by the client:
100
+
101
+ ```typescript
102
+ // Correct way to handle transactions
103
+ const multi = client.multi();
104
+ multi.set('key1', 'value1');
105
+ multi.set('key2', 'value2');
106
+ await multi.exec();
107
+ ```
108
+
109
+ #### ❌ Avoid: Manual Transaction Construction
110
+
111
+ Do not manually construct transactions by sending individual MULTI/EXEC commands:
112
+
113
+ ```typescript
114
+ // Incorrect and potentially dangerous
115
+ await client.sendCommand(['MULTI']);
116
+ await client.sendCommand(['SET', 'key1', 'value1']);
117
+ await client.sendCommand(['SET', 'key2', 'value2']);
118
+ await client.sendCommand(['EXEC']); // Risk of AUTH command being injected before EXEC
119
+ ```
120
+
121
+ ## Error Handling
122
+
123
+ The provider includes built-in retry mechanisms for transient errors:
124
+
125
+ ```typescript
126
+ const provider = EntraIdCredentialsProviderFactory.createForClientCredentials({
127
+ // ... other config ...
128
+ tokenManagerConfig: {
129
+ retry: {
130
+ maxAttempts: 3,
131
+ initialDelayMs: 100,
132
+ maxDelayMs: 1000,
133
+ backoffMultiplier: 2
134
+ }
135
+ }
136
+ });
137
+ ```
@@ -0,0 +1,125 @@
1
+ import { AuthenticationResult, PublicClientApplication } from '@azure/msal-node';
2
+ import { RetryPolicy, TokenManagerConfig, ReAuthenticationError } from '@redis/client/dist/lib/authx';
3
+ import { EntraidCredentialsProvider } from './entraid-credentials-provider';
4
+ /**
5
+ * This class is used to create credentials providers for different types of authentication flows.
6
+ */
7
+ export declare class EntraIdCredentialsProviderFactory {
8
+ #private;
9
+ /**
10
+ * This method is used to create a ManagedIdentityProvider for both system-assigned and user-assigned managed identities.
11
+ *
12
+ * @param params
13
+ * @param userAssignedClientId For user-assigned managed identities, the developer needs to pass either the client ID,
14
+ * full resource identifier, or the object ID of the managed identity when creating ManagedIdentityApplication.
15
+ *
16
+ */
17
+ static createManagedIdentityProvider(params: CredentialParams, userAssignedClientId?: string): EntraidCredentialsProvider;
18
+ /**
19
+ * This method is used to create a credentials provider for system-assigned managed identities.
20
+ * @param params
21
+ */
22
+ static createForSystemAssignedManagedIdentity(params: CredentialParams): EntraidCredentialsProvider;
23
+ /**
24
+ * This method is used to create a credentials provider for user-assigned managed identities.
25
+ * It will include the client ID as the userAssignedClientId in the ManagedIdentityConfiguration.
26
+ * @param params
27
+ */
28
+ static createForUserAssignedManagedIdentity(params: CredentialParams & {
29
+ userAssignedClientId: string;
30
+ }): EntraidCredentialsProvider;
31
+ /**
32
+ * This method is used to create a credentials provider for service principals using certificate.
33
+ * @param params
34
+ */
35
+ static createForClientCredentialsWithCertificate(params: ClientCredentialsWithCertificateParams): EntraidCredentialsProvider;
36
+ /**
37
+ * This method is used to create a credentials provider for service principals using client secret.
38
+ * @param params
39
+ */
40
+ static createForClientCredentials(params: ClientSecretCredentialsParams): EntraidCredentialsProvider;
41
+ /**
42
+ * This method is used to create a credentials provider for the Authorization Code Flow with PKCE.
43
+ * @param params
44
+ */
45
+ static createForAuthorizationCodeWithPKCE(params: AuthCodePKCEParams): {
46
+ getPKCECodes: () => Promise<{
47
+ verifier: string;
48
+ challenge: string;
49
+ challengeMethod: string;
50
+ }>;
51
+ getAuthCodeUrl: (pkceCodes: {
52
+ challenge: string;
53
+ challengeMethod: string;
54
+ }) => Promise<string>;
55
+ createCredentialsProvider: (params: PKCEParams) => EntraidCredentialsProvider;
56
+ };
57
+ static getAuthority(config: AuthorityConfig): string;
58
+ }
59
+ export type AuthorityConfig = {
60
+ type: 'multi-tenant';
61
+ tenantId: string;
62
+ } | {
63
+ type: 'custom';
64
+ authorityUrl: string;
65
+ } | {
66
+ type: 'default';
67
+ };
68
+ export type PKCEParams = {
69
+ code: string;
70
+ verifier: string;
71
+ clientInfo?: string;
72
+ };
73
+ export type CredentialParams = {
74
+ clientId: string;
75
+ scopes?: string[];
76
+ authorityConfig?: AuthorityConfig;
77
+ tokenManagerConfig: TokenManagerConfig;
78
+ onReAuthenticationError?: (error: ReAuthenticationError) => void;
79
+ };
80
+ export type AuthCodePKCEParams = CredentialParams & {
81
+ redirectUri: string;
82
+ };
83
+ export type ClientSecretCredentialsParams = CredentialParams & {
84
+ clientSecret: string;
85
+ };
86
+ export type ClientCredentialsWithCertificateParams = CredentialParams & {
87
+ certificate: {
88
+ thumbprint: string;
89
+ privateKey: string;
90
+ x5c?: string;
91
+ };
92
+ };
93
+ /**
94
+ * The most important part of the RetryPolicy is the `isRetryable` function. This function is used to determine if a request should be retried based
95
+ * on the error returned from the identity provider. The default for is to retry on network errors only.
96
+ */
97
+ export declare const DEFAULT_RETRY_POLICY: RetryPolicy;
98
+ export declare const DEFAULT_TOKEN_MANAGER_CONFIG: TokenManagerConfig;
99
+ /**
100
+ * This class is used to help with the Authorization Code Flow with PKCE.
101
+ * It provides methods to generate PKCE codes, get the authorization URL, and create the credential provider.
102
+ */
103
+ export declare class AuthCodeFlowHelper {
104
+ readonly client: PublicClientApplication;
105
+ readonly scopes: string[];
106
+ readonly redirectUri: string;
107
+ private constructor();
108
+ getAuthCodeUrl(pkceCodes: {
109
+ challenge: string;
110
+ challengeMethod: string;
111
+ }): Promise<string>;
112
+ acquireTokenByCode(params: PKCEParams): Promise<AuthenticationResult>;
113
+ static generatePKCE(): Promise<{
114
+ verifier: string;
115
+ challenge: string;
116
+ challengeMethod: string;
117
+ }>;
118
+ static create(params: {
119
+ clientId: string;
120
+ redirectUri: string;
121
+ scopes?: string[];
122
+ authorityConfig?: AuthorityConfig;
123
+ }): AuthCodeFlowHelper;
124
+ }
125
+ //# sourceMappingURL=entra-id-credentials-provider-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entra-id-credentials-provider-factory.d.ts","sourceRoot":"","sources":["../../lib/entra-id-credentials-provider-factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthCodeFlowHelper = exports.DEFAULT_TOKEN_MANAGER_CONFIG = exports.DEFAULT_RETRY_POLICY = exports.EntraIdCredentialsProviderFactory = void 0;
4
+ const msal_common_1 = require("@azure/msal-common");
5
+ const msal_node_1 = require("@azure/msal-node");
6
+ const authx_1 = require("@redis/client/dist/lib/authx");
7
+ const entraid_credentials_provider_1 = require("./entraid-credentials-provider");
8
+ const msal_identity_provider_1 = require("./msal-identity-provider");
9
+ /**
10
+ * This class is used to create credentials providers for different types of authentication flows.
11
+ */
12
+ class EntraIdCredentialsProviderFactory {
13
+ /**
14
+ * This method is used to create a ManagedIdentityProvider for both system-assigned and user-assigned managed identities.
15
+ *
16
+ * @param params
17
+ * @param userAssignedClientId For user-assigned managed identities, the developer needs to pass either the client ID,
18
+ * full resource identifier, or the object ID of the managed identity when creating ManagedIdentityApplication.
19
+ *
20
+ */
21
+ static createManagedIdentityProvider(params, userAssignedClientId) {
22
+ const config = {
23
+ // For user-assigned identity, include the client ID
24
+ ...(userAssignedClientId && {
25
+ managedIdentityIdParams: {
26
+ userAssignedClientId
27
+ }
28
+ }),
29
+ system: {
30
+ loggerOptions
31
+ }
32
+ };
33
+ const client = new msal_node_1.ManagedIdentityApplication(config);
34
+ const idp = new msal_identity_provider_1.MSALIdentityProvider(() => client.acquireToken({
35
+ resource: params.scopes?.[0] ?? REDIS_SCOPE,
36
+ forceRefresh: true
37
+ }).then(x => x === null ? Promise.reject('Token is null') : x));
38
+ return new entraid_credentials_provider_1.EntraidCredentialsProvider(new authx_1.TokenManager(idp, params.tokenManagerConfig), idp, { onReAuthenticationError: params.onReAuthenticationError, credentialsMapper: OID_CREDENTIALS_MAPPER });
39
+ }
40
+ /**
41
+ * This method is used to create a credentials provider for system-assigned managed identities.
42
+ * @param params
43
+ */
44
+ static createForSystemAssignedManagedIdentity(params) {
45
+ return this.createManagedIdentityProvider(params);
46
+ }
47
+ /**
48
+ * This method is used to create a credentials provider for user-assigned managed identities.
49
+ * It will include the client ID as the userAssignedClientId in the ManagedIdentityConfiguration.
50
+ * @param params
51
+ */
52
+ static createForUserAssignedManagedIdentity(params) {
53
+ return this.createManagedIdentityProvider(params, params.userAssignedClientId);
54
+ }
55
+ static #createForClientCredentials(authConfig, params) {
56
+ const config = {
57
+ auth: {
58
+ ...authConfig,
59
+ authority: this.getAuthority(params.authorityConfig ?? { type: 'default' })
60
+ },
61
+ system: {
62
+ loggerOptions
63
+ }
64
+ };
65
+ const client = new msal_node_1.ConfidentialClientApplication(config);
66
+ const idp = new msal_identity_provider_1.MSALIdentityProvider(() => client.acquireTokenByClientCredential({
67
+ skipCache: true,
68
+ scopes: params.scopes ?? [REDIS_SCOPE_DEFAULT]
69
+ }).then(x => x === null ? Promise.reject('Token is null') : x));
70
+ return new entraid_credentials_provider_1.EntraidCredentialsProvider(new authx_1.TokenManager(idp, params.tokenManagerConfig), idp, {
71
+ onReAuthenticationError: params.onReAuthenticationError,
72
+ credentialsMapper: OID_CREDENTIALS_MAPPER
73
+ });
74
+ }
75
+ /**
76
+ * This method is used to create a credentials provider for service principals using certificate.
77
+ * @param params
78
+ */
79
+ static createForClientCredentialsWithCertificate(params) {
80
+ return this.#createForClientCredentials({
81
+ clientId: params.clientId,
82
+ clientCertificate: params.certificate
83
+ }, params);
84
+ }
85
+ /**
86
+ * This method is used to create a credentials provider for service principals using client secret.
87
+ * @param params
88
+ */
89
+ static createForClientCredentials(params) {
90
+ return this.#createForClientCredentials({
91
+ clientId: params.clientId,
92
+ clientSecret: params.clientSecret
93
+ }, params);
94
+ }
95
+ /**
96
+ * This method is used to create a credentials provider for the Authorization Code Flow with PKCE.
97
+ * @param params
98
+ */
99
+ static createForAuthorizationCodeWithPKCE(params) {
100
+ const requiredScopes = ['user.read', 'offline_access'];
101
+ const scopes = [...new Set([...(params.scopes || []), ...requiredScopes])];
102
+ const authFlow = AuthCodeFlowHelper.create({
103
+ clientId: params.clientId,
104
+ redirectUri: params.redirectUri,
105
+ scopes: scopes,
106
+ authorityConfig: params.authorityConfig
107
+ });
108
+ return {
109
+ getPKCECodes: AuthCodeFlowHelper.generatePKCE,
110
+ getAuthCodeUrl: (pkceCodes) => authFlow.getAuthCodeUrl(pkceCodes),
111
+ createCredentialsProvider: (pkceParams) => {
112
+ // This is used to store the initial credentials account to be used
113
+ // for silent token acquisition after the initial token acquisition.
114
+ let initialCredentialsAccount = null;
115
+ const idp = new msal_identity_provider_1.MSALIdentityProvider(async () => {
116
+ if (!initialCredentialsAccount) {
117
+ let authResult = await authFlow.acquireTokenByCode(pkceParams);
118
+ initialCredentialsAccount = authResult.account;
119
+ return authResult;
120
+ }
121
+ else {
122
+ return authFlow.client.acquireTokenSilent({
123
+ forceRefresh: true,
124
+ account: initialCredentialsAccount,
125
+ scopes
126
+ });
127
+ }
128
+ });
129
+ const tm = new authx_1.TokenManager(idp, params.tokenManagerConfig);
130
+ return new entraid_credentials_provider_1.EntraidCredentialsProvider(tm, idp, { onReAuthenticationError: params.onReAuthenticationError });
131
+ }
132
+ };
133
+ }
134
+ static getAuthority(config) {
135
+ switch (config.type) {
136
+ case 'multi-tenant':
137
+ return `https://login.microsoftonline.com/${config.tenantId}`;
138
+ case 'custom':
139
+ return config.authorityUrl;
140
+ case 'default':
141
+ return 'https://login.microsoftonline.com/common';
142
+ default:
143
+ throw new Error('Invalid authority configuration');
144
+ }
145
+ }
146
+ }
147
+ exports.EntraIdCredentialsProviderFactory = EntraIdCredentialsProviderFactory;
148
+ const REDIS_SCOPE_DEFAULT = 'https://redis.azure.com/.default';
149
+ const REDIS_SCOPE = 'https://redis.azure.com';
150
+ const loggerOptions = {
151
+ loggerCallback(loglevel, message, containsPii) {
152
+ if (!containsPii)
153
+ console.log(message);
154
+ },
155
+ piiLoggingEnabled: false,
156
+ logLevel: msal_node_1.LogLevel.Error
157
+ };
158
+ /**
159
+ * The most important part of the RetryPolicy is the `isRetryable` function. This function is used to determine if a request should be retried based
160
+ * on the error returned from the identity provider. The default for is to retry on network errors only.
161
+ */
162
+ exports.DEFAULT_RETRY_POLICY = {
163
+ // currently only retry on network errors
164
+ isRetryable: (error) => error instanceof msal_common_1.NetworkError,
165
+ maxAttempts: 10,
166
+ initialDelayMs: 100,
167
+ maxDelayMs: 100000,
168
+ backoffMultiplier: 2,
169
+ jitterPercentage: 0.1
170
+ };
171
+ exports.DEFAULT_TOKEN_MANAGER_CONFIG = {
172
+ retry: exports.DEFAULT_RETRY_POLICY,
173
+ expirationRefreshRatio: 0.7 // Refresh token when 70% of the token has expired
174
+ };
175
+ /**
176
+ * This class is used to help with the Authorization Code Flow with PKCE.
177
+ * It provides methods to generate PKCE codes, get the authorization URL, and create the credential provider.
178
+ */
179
+ class AuthCodeFlowHelper {
180
+ client;
181
+ scopes;
182
+ redirectUri;
183
+ constructor(client, scopes, redirectUri) {
184
+ this.client = client;
185
+ this.scopes = scopes;
186
+ this.redirectUri = redirectUri;
187
+ }
188
+ async getAuthCodeUrl(pkceCodes) {
189
+ const authCodeUrlParameters = {
190
+ scopes: this.scopes,
191
+ redirectUri: this.redirectUri,
192
+ codeChallenge: pkceCodes.challenge,
193
+ codeChallengeMethod: pkceCodes.challengeMethod
194
+ };
195
+ return this.client.getAuthCodeUrl(authCodeUrlParameters);
196
+ }
197
+ async acquireTokenByCode(params) {
198
+ const tokenRequest = {
199
+ code: params.code,
200
+ scopes: this.scopes,
201
+ redirectUri: this.redirectUri,
202
+ codeVerifier: params.verifier,
203
+ clientInfo: params.clientInfo
204
+ };
205
+ return this.client.acquireTokenByCode(tokenRequest);
206
+ }
207
+ static async generatePKCE() {
208
+ const cryptoProvider = new msal_node_1.CryptoProvider();
209
+ const { verifier, challenge } = await cryptoProvider.generatePkceCodes();
210
+ return {
211
+ verifier,
212
+ challenge,
213
+ challengeMethod: 'S256'
214
+ };
215
+ }
216
+ static create(params) {
217
+ const config = {
218
+ auth: {
219
+ clientId: params.clientId,
220
+ authority: EntraIdCredentialsProviderFactory.getAuthority(params.authorityConfig ?? { type: 'default' })
221
+ },
222
+ system: {
223
+ loggerOptions
224
+ }
225
+ };
226
+ return new AuthCodeFlowHelper(new msal_node_1.PublicClientApplication(config), params.scopes ?? ['user.read'], params.redirectUri);
227
+ }
228
+ }
229
+ exports.AuthCodeFlowHelper = AuthCodeFlowHelper;
230
+ const OID_CREDENTIALS_MAPPER = (token) => {
231
+ // Client credentials flow is app-only authentication (no user context),
232
+ // so only access token is provided without user-specific claims (uniqueId, idToken, ...)
233
+ // this means that we need to extract the oid from the access token manually
234
+ const accessToken = JSON.parse(Buffer.from(token.accessToken.split('.')[1], 'base64').toString());
235
+ return ({
236
+ username: accessToken.oid,
237
+ password: token.accessToken
238
+ });
239
+ };
240
+ //# sourceMappingURL=entra-id-credentials-provider-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entra-id-credentials-provider-factory.js","sourceRoot":"","sources":["../../lib/entra-id-credentials-provider-factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { AuthenticationResult } from '@azure/msal-common/node';
2
+ import { BasicAuth, StreamingCredentialsProvider, IdentityProvider, TokenManager, ReAuthenticationError, StreamingCredentialsListener, Disposable } from '@redis/client/dist/lib/authx';
3
+ /**
4
+ * A streaming credentials provider that uses the Entraid identity provider to provide credentials.
5
+ * Please use one of the factory functions in `entraid-credetfactories.ts` to create an instance of this class for the different
6
+ * type of authentication flows.
7
+ */
8
+ export declare class EntraidCredentialsProvider implements StreamingCredentialsProvider {
9
+ #private;
10
+ readonly tokenManager: TokenManager<AuthenticationResult>;
11
+ readonly idp: IdentityProvider<AuthenticationResult>;
12
+ private readonly options;
13
+ readonly type = "streaming-credentials-provider";
14
+ constructor(tokenManager: TokenManager<AuthenticationResult>, idp: IdentityProvider<AuthenticationResult>, options?: {
15
+ onReAuthenticationError?: (error: ReAuthenticationError) => void;
16
+ credentialsMapper?: (token: AuthenticationResult) => BasicAuth;
17
+ onRetryableError?: (error: string) => void;
18
+ });
19
+ subscribe(listener: StreamingCredentialsListener<BasicAuth>): Promise<[BasicAuth, Disposable]>;
20
+ onReAuthenticationError: (error: ReAuthenticationError) => void;
21
+ hasActiveSubscriptions(): boolean;
22
+ getSubscriptionsCount(): number;
23
+ getTokenManager(): TokenManager<AuthenticationResult>;
24
+ getCurrentCredentials(): BasicAuth | null;
25
+ }
26
+ //# sourceMappingURL=entraid-credentials-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entraid-credentials-provider.d.ts","sourceRoot":"","sources":["../../lib/entraid-credentials-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EACL,SAAS,EAAE,4BAA4B,EAAE,gBAAgB,EAAE,YAAY,EACvE,qBAAqB,EAAE,4BAA4B,EAAmB,UAAU,EACjF,MAAM,8BAA8B,CAAC;AAEtC;;;;GAIG;AACH,qBAAa,0BAA2B,YAAW,4BAA4B;;aAe3D,YAAY,EAAE,YAAY,CAAC,oBAAoB,CAAC;aAChD,GAAG,EAAE,gBAAgB,CAAC,oBAAoB,CAAC;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhB1B,QAAQ,CAAC,IAAI,oCAAoC;gBAc/B,YAAY,EAAE,YAAY,CAAC,oBAAoB,CAAC,EAChD,GAAG,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,EAC1C,OAAO,GAAE;QACxB,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;QACjE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,SAAS,CAAC;QAC/D,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;KACvC;IAMF,SAAS,CACb,QAAQ,EAAE,4BAA4B,CAAC,SAAS,CAAC,GAChD,OAAO,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IA6BnC,uBAAuB,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;IA6CzD,sBAAsB,IAAI,OAAO;IAIjC,qBAAqB,IAAI,MAAM;IAI/B,eAAe;IAIf,qBAAqB,IAAI,SAAS,GAAG,IAAI;CAKjD"}
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EntraidCredentialsProvider = void 0;
4
+ /**
5
+ * A streaming credentials provider that uses the Entraid identity provider to provide credentials.
6
+ * Please use one of the factory functions in `entraid-credetfactories.ts` to create an instance of this class for the different
7
+ * type of authentication flows.
8
+ */
9
+ class EntraidCredentialsProvider {
10
+ tokenManager;
11
+ idp;
12
+ options;
13
+ type = 'streaming-credentials-provider';
14
+ #listeners = new Set();
15
+ #tokenManagerDisposable = null;
16
+ #isStarting = false;
17
+ #pendingSubscribers = [];
18
+ constructor(tokenManager, idp, options = {}) {
19
+ this.tokenManager = tokenManager;
20
+ this.idp = idp;
21
+ this.options = options;
22
+ this.onReAuthenticationError = options.onReAuthenticationError ?? DEFAULT_ERROR_HANDLER;
23
+ this.#credentialsMapper = options.credentialsMapper ?? DEFAULT_CREDENTIALS_MAPPER;
24
+ }
25
+ async subscribe(listener) {
26
+ const currentToken = this.tokenManager.getCurrentToken();
27
+ if (currentToken) {
28
+ return [this.#credentialsMapper(currentToken.value), this.#createDisposable(listener)];
29
+ }
30
+ if (this.#isStarting) {
31
+ return new Promise((resolve, reject) => {
32
+ this.#pendingSubscribers.push({ resolve, reject, pendingListener: listener });
33
+ });
34
+ }
35
+ this.#isStarting = true;
36
+ try {
37
+ const initialToken = await this.#startTokenManagerAndObtainInitialToken();
38
+ this.#pendingSubscribers.forEach(({ resolve, pendingListener }) => {
39
+ resolve([this.#credentialsMapper(initialToken.value), this.#createDisposable(pendingListener)]);
40
+ });
41
+ this.#pendingSubscribers = [];
42
+ return [this.#credentialsMapper(initialToken.value), this.#createDisposable(listener)];
43
+ }
44
+ finally {
45
+ this.#isStarting = false;
46
+ }
47
+ }
48
+ onReAuthenticationError;
49
+ #credentialsMapper;
50
+ #createTokenManagerListener(subscribers) {
51
+ return {
52
+ onError: (error) => {
53
+ if (!error.isRetryable) {
54
+ subscribers.forEach(listener => listener.onError(error));
55
+ }
56
+ else {
57
+ this.options.onRetryableError?.(error.message);
58
+ }
59
+ },
60
+ onNext: (token) => {
61
+ const credentials = this.#credentialsMapper(token.value);
62
+ subscribers.forEach(listener => listener.onNext(credentials));
63
+ }
64
+ };
65
+ }
66
+ #createDisposable(listener) {
67
+ this.#listeners.add(listener);
68
+ return {
69
+ dispose: () => {
70
+ this.#listeners.delete(listener);
71
+ if (this.#listeners.size === 0 && this.#tokenManagerDisposable) {
72
+ this.#tokenManagerDisposable.dispose();
73
+ this.#tokenManagerDisposable = null;
74
+ }
75
+ }
76
+ };
77
+ }
78
+ async #startTokenManagerAndObtainInitialToken() {
79
+ const initialResponse = await this.idp.requestToken();
80
+ const token = this.tokenManager.wrapAndSetCurrentToken(initialResponse.token, initialResponse.ttlMs);
81
+ this.#tokenManagerDisposable = this.tokenManager.start(this.#createTokenManagerListener(this.#listeners), this.tokenManager.calculateRefreshTime(token));
82
+ return token;
83
+ }
84
+ hasActiveSubscriptions() {
85
+ return this.#tokenManagerDisposable !== null && this.#listeners.size > 0;
86
+ }
87
+ getSubscriptionsCount() {
88
+ return this.#listeners.size;
89
+ }
90
+ getTokenManager() {
91
+ return this.tokenManager;
92
+ }
93
+ getCurrentCredentials() {
94
+ const currentToken = this.tokenManager.getCurrentToken();
95
+ return currentToken ? this.#credentialsMapper(currentToken.value) : null;
96
+ }
97
+ }
98
+ exports.EntraidCredentialsProvider = EntraidCredentialsProvider;
99
+ const DEFAULT_CREDENTIALS_MAPPER = (token) => ({
100
+ username: token.uniqueId,
101
+ password: token.accessToken
102
+ });
103
+ const DEFAULT_ERROR_HANDLER = (error) => console.error('ReAuthenticationError', error);
104
+ //# sourceMappingURL=entraid-credentials-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entraid-credentials-provider.js","sourceRoot":"","sources":["../../lib/entraid-credentials-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './entra-id-credentials-provider-factory';
2
+ export * from './entraid-credentials-provider';
3
+ export * from './msal-identity-provider';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./entra-id-credentials-provider-factory"), exports);
18
+ __exportStar(require("./entraid-credentials-provider"), exports);
19
+ __exportStar(require("./msal-identity-provider"), exports);
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0EAAwD;AACxD,iEAA+C;AAC/C,2DAAyC"}
@@ -0,0 +1,8 @@
1
+ import { AuthenticationResult } from '@azure/msal-node';
2
+ import { IdentityProvider, TokenResponse } from '@redis/client/dist/lib/authx';
3
+ export declare class MSALIdentityProvider implements IdentityProvider<AuthenticationResult> {
4
+ private readonly getToken;
5
+ constructor(getToken: () => Promise<AuthenticationResult>);
6
+ requestToken(): Promise<TokenResponse<AuthenticationResult>>;
7
+ }
8
+ //# sourceMappingURL=msal-identity-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msal-identity-provider.d.ts","sourceRoot":"","sources":["../../lib/msal-identity-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE/E,qBAAa,oBAAqB,YAAW,gBAAgB,CAAC,oBAAoB,CAAC;IACjF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;gBAEnD,QAAQ,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC;IAInD,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC;CAgBnE"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MSALIdentityProvider = void 0;
4
+ class MSALIdentityProvider {
5
+ getToken;
6
+ constructor(getToken) {
7
+ this.getToken = getToken;
8
+ }
9
+ async requestToken() {
10
+ try {
11
+ const result = await this.getToken();
12
+ if (!result?.accessToken || !result?.expiresOn) {
13
+ throw new Error('Invalid token response');
14
+ }
15
+ return {
16
+ token: result,
17
+ ttlMs: result.expiresOn.getTime() - Date.now()
18
+ };
19
+ }
20
+ catch (error) {
21
+ throw error;
22
+ }
23
+ }
24
+ }
25
+ exports.MSALIdentityProvider = MSALIdentityProvider;
26
+ //# sourceMappingURL=msal-identity-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msal-identity-provider.js","sourceRoot":"","sources":["../../lib/msal-identity-provider.ts"],"names":[],"mappings":";;;AAKA,MAAa,oBAAoB;IACd,QAAQ,CAAsC;IAE/D,YAAY,QAA6C;QACvD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YAErC,IAAI,CAAC,MAAM,EAAE,WAAW,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;gBAC/C,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC5C,CAAC;YACD,OAAO;gBACL,KAAK,EAAE,MAAM;gBACb,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;aAC/C,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CAEF;AAvBD,oDAuBC"}
@@ -0,0 +1,18 @@
1
+ import { StreamingCredentialsProvider } from '@redis/client/dist/lib/authx';
2
+ import TestUtils from '@redis/test-utils';
3
+ export declare const testUtils: TestUtils;
4
+ export declare const GLOBAL: {
5
+ CLUSTERS: {
6
+ PASSWORD_WITH_REPLICAS: {
7
+ serverArguments: string[];
8
+ numberOfMasters: number;
9
+ numberOfReplicas: number;
10
+ clusterConfiguration: {
11
+ defaults: {
12
+ credentialsProvider: StreamingCredentialsProvider;
13
+ };
14
+ };
15
+ };
16
+ };
17
+ };
18
+ //# sourceMappingURL=test-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../lib/test-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,4BAA4B,EAA+B,MAAM,8BAA8B,CAAC;AAC3H,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAG1C,eAAO,MAAM,SAAS,WAIpB,CAAC;AAgCH,eAAO,MAAM,MAAM;;;;;;;;;;;;;CAIlB,CAAA"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GLOBAL = exports.testUtils = void 0;
7
+ const authx_1 = require("@redis/client/dist/lib/authx");
8
+ const test_utils_1 = __importDefault(require("@redis/test-utils"));
9
+ const entraid_credentials_provider_1 = require("./entraid-credentials-provider");
10
+ exports.testUtils = new test_utils_1.default({
11
+ dockerImageName: 'redis/redis-stack',
12
+ dockerImageVersionArgument: 'redis-version',
13
+ defaultDockerVersion: '7.4.0-v1'
14
+ });
15
+ const DEBUG_MODE_ARGS = exports.testUtils.isVersionGreaterThan([7]) ?
16
+ ['--enable-debug-command', 'yes'] :
17
+ [];
18
+ const idp = {
19
+ requestToken() {
20
+ // @ts-ignore
21
+ return Promise.resolve({
22
+ ttlMs: 100000,
23
+ token: {
24
+ accessToken: 'password'
25
+ }
26
+ });
27
+ }
28
+ };
29
+ const tokenManager = new authx_1.TokenManager(idp, { expirationRefreshRatio: 0.8 });
30
+ const entraIdCredentialsProvider = new entraid_credentials_provider_1.EntraidCredentialsProvider(tokenManager, idp);
31
+ const PASSWORD_WITH_REPLICAS = {
32
+ serverArguments: ['--requirepass', 'password', ...DEBUG_MODE_ARGS],
33
+ numberOfMasters: 2,
34
+ numberOfReplicas: 1,
35
+ clusterConfiguration: {
36
+ defaults: {
37
+ credentialsProvider: entraIdCredentialsProvider
38
+ }
39
+ }
40
+ };
41
+ exports.GLOBAL = {
42
+ CLUSTERS: {
43
+ PASSWORD_WITH_REPLICAS
44
+ }
45
+ };
46
+ //# sourceMappingURL=test-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../lib/test-utils.ts"],"names":[],"mappings":";;;;;;AACA,wDAA2H;AAC3H,mEAA0C;AAC1C,iFAA4E;AAE/D,QAAA,SAAS,GAAG,IAAI,oBAAS,CAAC;IACrC,eAAe,EAAE,mBAAmB;IACpC,0BAA0B,EAAE,eAAe;IAC3C,oBAAoB,EAAE,UAAU;CACjC,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,iBAAS,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC,CAAC;IACnC,EAAE,CAAC;AAEL,MAAM,GAAG,GAA2C;IAClD,YAAY;QACV,aAAa;QACb,OAAO,OAAO,CAAC,OAAO,CAAC;YACrB,KAAK,EAAE,MAAM;YACb,KAAK,EAAE;gBACL,WAAW,EAAE,UAAU;aACxB;SACF,CAAC,CAAA;IACJ,CAAC;CACF,CAAA;AAED,MAAM,YAAY,GAAG,IAAI,oBAAY,CAAuB,GAAG,EAAE,EAAE,sBAAsB,EAAE,GAAG,EAAE,CAAC,CAAC;AAClG,MAAM,0BAA0B,GAAiC,IAAI,yDAA0B,CAAC,YAAY,EAAE,GAAG,CAAC,CAAA;AAElH,MAAM,sBAAsB,GAAG;IAC7B,eAAe,EAAE,CAAC,eAAe,EAAE,UAAU,EAAE,GAAG,eAAe,CAAC;IAClE,eAAe,EAAE,CAAC;IAClB,gBAAgB,EAAE,CAAC;IACnB,oBAAoB,EAAE;QACpB,QAAQ,EAAE;YACR,mBAAmB,EAAE,0BAA0B;SAChD;KACF;CACF,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,QAAQ,EAAE;QACR,sBAAsB;KACvB;CACF,CAAA"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@redis/entraid",
3
+ "version": "5.0.0-next.6",
4
+ "license": "MIT",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "files": [
8
+ "dist/",
9
+ "!dist/tsconfig.tsbuildinfo"
10
+ ],
11
+ "scripts": {
12
+ "clean": "rimraf dist",
13
+ "build": "npm run clean && tsc",
14
+ "start:auth-pkce": "tsx --tsconfig tsconfig.samples.json ./samples/auth-code-pkce/index.ts",
15
+ "test-integration": "mocha -r tsx --tsconfig tsconfig.integration-tests.json './integration-tests/**/*.spec.ts'",
16
+ "test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'"
17
+ },
18
+ "dependencies": {
19
+ "@azure/msal-node": "^2.16.1"
20
+ },
21
+ "peerDependencies": {
22
+ "@redis/client": "^5.0.0-next.6"
23
+ },
24
+ "devDependencies": {
25
+ "@types/express": "^4.17.21",
26
+ "@types/express-session": "^1.18.0",
27
+ "@types/node": "^22.9.0",
28
+ "dotenv": "^16.3.1",
29
+ "express": "^4.21.1",
30
+ "express-session": "^1.18.1",
31
+ "@redis/test-utils": "*"
32
+ },
33
+ "engines": {
34
+ "node": ">= 18"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git://github.com/redis/node-redis.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/redis/node-redis/issues"
42
+ },
43
+ "homepage": "https://github.com/redis/node-redis/tree/master/packages/entraid",
44
+ "keywords": [
45
+ "redis"
46
+ ]
47
+ }