Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/apps/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,9 +622,9 @@ export class App<TPlugin extends IPlugin = IPlugin> {
/// Token
///

protected async getBotToken() {
protected async getBotToken(skipCache?: boolean) {
if (!this.tokenManager) return;
return await this.tokenManager.getBotToken();
return await this.tokenManager.getBotToken(skipCache);
}

protected async getUserToken(
Expand All @@ -640,8 +640,8 @@ export class App<TPlugin extends IPlugin = IPlugin> {
return res.token;
}

protected async getAppGraphToken(tenantId?: string) {
protected async getAppGraphToken(tenantId?: string, skipCache?: boolean) {
if (!this.tokenManager) return;
return await this.tokenManager.getGraphToken(tenantId);
return await this.tokenManager.getGraphToken(tenantId, skipCache);
}
}
62 changes: 58 additions & 4 deletions packages/apps/src/token-manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ describe('TokenManager', () => {
);

expect(mockAcquireTokenByClientCredential).toHaveBeenCalledWith({
scopes: ['https://api.botframework.com/.default']
scopes: ['https://api.botframework.com/.default'],
skipCache: false
});

expect(token).not.toBeNull();
Expand Down Expand Up @@ -141,7 +142,8 @@ describe('TokenManager', () => {
const token = await tokenManager.getGraphToken();

expect(mockAcquireTokenByClientCredential).toHaveBeenCalledWith({
scopes: ['https://graph.microsoft.com/.default']
scopes: ['https://graph.microsoft.com/.default'],
skipCache: false
});

expect(token).not.toBeNull();
Expand Down Expand Up @@ -216,6 +218,56 @@ describe('TokenManager', () => {
});
});

describe('skipCache support', () => {
it('getBotToken should pass skipCache to MSAL when true', async () => {
mockAcquireTokenByClientCredential.mockResolvedValue(createMockAuthResult('fresh-bot-token'));

const tokenManager = new TokenManager(mockOptions, logger);
await tokenManager.getBotToken(true);

expect(mockAcquireTokenByClientCredential).toHaveBeenCalledWith({
scopes: ['https://api.botframework.com/.default'],
skipCache: true
});
});

it('getBotToken should not set skipCache when called without argument', async () => {
mockAcquireTokenByClientCredential.mockResolvedValue(createMockAuthResult('cached-bot-token'));

const tokenManager = new TokenManager(mockOptions, logger);
await tokenManager.getBotToken();

expect(mockAcquireTokenByClientCredential).toHaveBeenCalledWith({
scopes: ['https://api.botframework.com/.default'],
skipCache: false
});
});

it('getGraphToken should pass skipCache to MSAL when true', async () => {
mockAcquireTokenByClientCredential.mockResolvedValue(createMockAuthResult('fresh-graph-token'));

const tokenManager = new TokenManager(mockOptions, logger);
await tokenManager.getGraphToken(undefined, true);

expect(mockAcquireTokenByClientCredential).toHaveBeenCalledWith({
scopes: ['https://graph.microsoft.com/.default'],
skipCache: true
});
});

it('getGraphToken should not set skipCache when called without argument', async () => {
mockAcquireTokenByClientCredential.mockResolvedValue(createMockAuthResult('cached-graph-token'));

const tokenManager = new TokenManager(mockOptions, logger);
await tokenManager.getGraphToken();

expect(mockAcquireTokenByClientCredential).toHaveBeenCalledWith({
scopes: ['https://graph.microsoft.com/.default'],
skipCache: false
});
});
});

describe('TokenCredentials provider', () => {
it('should use token provider for bot token', async () => {
const mockTokenProvider = jest.fn().mockResolvedValue('mock-provider-token');
Expand Down Expand Up @@ -407,7 +459,8 @@ describe('TokenManager', () => {
);

expect(mockConfidentialAcquireToken).toHaveBeenCalledWith({
scopes: ['https://api.botframework.com/.default']
scopes: ['https://api.botframework.com/.default'],
skipCache: false
});

expect(token).not.toBeNull();
Expand Down Expand Up @@ -452,7 +505,8 @@ describe('TokenManager', () => {
);

expect(mockConfidentialAcquireToken).toHaveBeenCalledWith({
scopes: ['https://api.botframework.com/.default']
scopes: ['https://api.botframework.com/.default'],
skipCache: false
});

expect(token).not.toBeNull();
Expand Down
22 changes: 11 additions & 11 deletions packages/apps/src/token-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ export class TokenManager {
this.credentials = this.initializeCredentials(options);
}

async getBotToken(): Promise<IToken | null> {
return await this.getToken(DEFAULT_BOT_TOKEN_SCOPE, this.resolveTenantId(undefined, DEFAULT_TENANT_FOR_BOT_TOKEN));
async getBotToken(skipCache?: boolean): Promise<IToken | null> {
return await this.getToken(DEFAULT_BOT_TOKEN_SCOPE, this.resolveTenantId(undefined, DEFAULT_TENANT_FOR_BOT_TOKEN), skipCache);
}

async getGraphToken(tenantId?: string): Promise<IToken | null> {
return await this.getToken(DEFAULT_GRAPH_TOKEN_SCOPE, this.resolveTenantId(tenantId, DEFAULT_TENANT_FOR_GRAPH_TOKEN));
async getGraphToken(tenantId?: string, skipCache?: boolean): Promise<IToken | null> {
return await this.getToken(DEFAULT_GRAPH_TOKEN_SCOPE, this.resolveTenantId(tenantId, DEFAULT_TENANT_FOR_GRAPH_TOKEN), skipCache);
}

private initializeCredentials(options: TokenManagerOptions): Credentials | undefined {
Expand Down Expand Up @@ -117,26 +117,26 @@ export class TokenManager {
return undefined;
}

private async getToken(scope: string, tenantId: string): Promise<IToken | null> {
private async getToken(scope: string, tenantId: string, skipCache?: boolean): Promise<IToken | null> {
if (!this.credentials) {
return null;
}

if (isClientCredentials(this.credentials)) {
return this.getTokenWithClientCredentials(this.credentials, scope, tenantId);
return this.getTokenWithClientCredentials(this.credentials, scope, tenantId, skipCache);
} else if (isTokenCredentials(this.credentials)) {
return this.getTokenWithTokenProvider(this.credentials, scope, tenantId);
} else if (isFederatedIdentityCredentials(this.credentials)) {
return this.getTokenWithFederatedCredentials(this.credentials, scope, tenantId);
return this.getTokenWithFederatedCredentials(this.credentials, scope, tenantId, skipCache);
} else {
return this.getTokenWithManagedIdentity(this.credentials, scope);
}

}

private async getTokenWithClientCredentials(credentials: ClientCredentials, scope: string, tenantId: string): Promise<IToken | null> {
private async getTokenWithClientCredentials(credentials: ClientCredentials, scope: string, tenantId: string, skipCache?: boolean): Promise<IToken | null> {
const confidentialClient = this.getConfidentialClient(credentials, tenantId);
const result = await confidentialClient.acquireTokenByClientCredential({ scopes: [scope] });
const result = await confidentialClient.acquireTokenByClientCredential({ scopes: [scope], skipCache: skipCache ?? false });
return this.handleTokenResponse(result);
}

Expand All @@ -155,7 +155,7 @@ export class TokenManager {
return this.handleTokenResponse(result);
}

private async getTokenWithFederatedCredentials(credentials: FederatedIdentityCredentials, scope: string, tenantId: string) {
private async getTokenWithFederatedCredentials(credentials: FederatedIdentityCredentials, scope: string, tenantId: string, skipCache?: boolean) {
const managedIdentityClient = this.getManagedIdentityClient(credentials);
const managedIdentityTokenRes = await managedIdentityClient.acquireToken({ resource: 'api://AzureADTokenExchange' });
const confidentialClient = new ConfidentialClientApplication({
Expand All @@ -168,7 +168,7 @@ export class TokenManager {
loggerOptions: this.buildLoggerOptions()
}
});
const result = await confidentialClient.acquireTokenByClientCredential({ scopes: [scope] });
const result = await confidentialClient.acquireTokenByClientCredential({ scopes: [scope], skipCache: skipCache ?? false });
return this.handleTokenResponse(result);
}

Expand Down
Loading