Skip to content

Commit 0ce5e7a

Browse files
committed
feat(auth): add Entra ID identity provider integration
Introduces Entra ID (former Azure AD) authentication support with multiple authentication flows and automated token lifecycle management. Key additions: - Add EntraIdCredentialsProvider for handling Entra ID authentication flows - Implement MSALIdentityProvider to integrate with MSAL/EntraID authentication library - Add support for multiple authentication methods: - Managed identities (system and user-assigned) - Client credentials with certificate - Client credentials with secret - Authorization Code flow with PKCE - Add factory class with builder methods for each authentication flow - Include sample Express server implementation for Authorization Code flow - Add comprehensive configuration options for authority and token management
1 parent e7d6f5b commit 0ce5e7a

12 files changed

+1899
-39
lines changed

package-lock.json

+945-28
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/authx/lib/token-manager.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export interface TokenManagerConfig {
3535
retry?: RetryPolicy;
3636
}
3737

38-
3938
/**
4039
* IDPError is an error that occurs while calling the underlying IdentityProvider.
4140
*
@@ -88,7 +87,7 @@ export class TokenManager<T> {
8887

8988
constructor(
9089
private readonly identityProvider: IdentityProvider<T>,
91-
private readonly config: TokenManagerConfig,
90+
private readonly config: TokenManagerConfig
9291
) {
9392
if (this.config.expirationRefreshRatio > 1) {
9493
throw new Error('expirationRefreshRatio must be less than or equal to 1');
@@ -186,25 +185,29 @@ export class TokenManager<T> {
186185
if (!this.listener) {
187186
throw new Error('TokenManager is not running, but a new token was received');
188187
}
189-
const token = this.wrapNativeToken(nativeToken, ttlMs);
190-
this.currentToken = token;
188+
const token = this.wrapAndSetCurrentToken(nativeToken, ttlMs);
191189
this.listener.onNext(token);
192190

193191
this.scheduleNextRefresh(this.calculateRefreshTime(token));
194192
}
195193

196194
/**
197-
* Wraps a native token obtained from identity provider.
198-
* @param nativeToken
199-
* @param ttlMs
195+
* Creates a Token object from a native token and sets it as the current token.
196+
*
197+
* @param nativeToken - The raw token received from the identity provider
198+
* @param ttlMs - Time-to-live in milliseconds for the token
199+
*
200+
* @returns A new Token instance containing the wrapped native token and expiration details
201+
*
200202
*/
201-
public wrapNativeToken(nativeToken: T, ttlMs: number): Token<T> {
203+
public wrapAndSetCurrentToken(nativeToken: T, ttlMs: number): Token<T> {
202204
const now = Date.now();
203205
const token = new Token(
204206
nativeToken,
205207
now + ttlMs,
206208
now
207209
);
210+
this.currentToken = token;
208211
return token;
209212
}
210213

@@ -228,7 +231,7 @@ export class TokenManager<T> {
228231
* @param token The token to calculate the refresh time for.
229232
* @param now The current time in milliseconds. Defaults to Date.now().
230233
*/
231-
public calculateRefreshTime(token: Token<T>, now:number = Date.now()): number {
234+
public calculateRefreshTime(token: Token<T>, now: number = Date.now()): number {
232235
const ttlMs = token.getTtlMs(now);
233236
return Math.floor(ttlMs * this.config.expirationRefreshRatio);
234237
}

packages/client/lib/client/index.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -380,11 +380,18 @@ export default class RedisClient<
380380
}
381381

382382
/**
383-
* TODO: Implement re-authentication to support refreshing credentials without reconnecting
384383
* @param credentials
385384
*/
386385
private reAuthenticate = async (credentials: BasicAuth) => {
387-
throw new Error('Not implemented');
386+
// Re-authentication is not supported on RESP2 with PubSub active
387+
if (!(this.isPubSubActive && !this.#options?.RESP)) {
388+
await this.sendCommand(
389+
parseArgs(COMMANDS.AUTH, {
390+
username: credentials.username,
391+
password: credentials.password ?? ''
392+
})
393+
);
394+
}
388395
}
389396

390397
private subscribeForStreamingCredentials(cp: StreamingCredentialsProvider): Promise<[BasicAuth, Disposable]> {

packages/entraid/README.md

Whitespace-only changes.

0 commit comments

Comments
 (0)