diff --git a/.changeset/mighty-tips-flow.md b/.changeset/mighty-tips-flow.md index d876942fde..514d514588 100644 --- a/.changeset/mighty-tips-flow.md +++ b/.changeset/mighty-tips-flow.md @@ -9,3 +9,8 @@ authorization code for a non-Microsoft Graph scope, the user profile will not be fetched. Similarly no user profile or photo data will be fetched by the backend if the `/refresh` endpoint is called with the `scope` query parameter strictly containing scopes for resources besides Microsoft Graph. + +Furthermore, the `offline_access` scope will be requested by default, even when +it is not mentioned in the argument to `getAccessToken`. This means that any +Azure access token can be automatically refreshed, even if the user has not +signed in via Azure. diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index f2df03d806..4ce4533fe1 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -441,7 +441,7 @@ export class LocalStorageFeatureFlags implements FeatureFlagsApi { // @public export class MicrosoftAuth { // (undocumented) - static create(options: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T; + static create(options: OAuth2CreateOptions): typeof microsoftAuthApiRef.T; // (undocumented) getAccessToken( scope?: string | string[], diff --git a/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.test.ts b/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.test.ts index 61a0a5b64a..0d2e016b41 100644 --- a/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.test.ts +++ b/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.test.ts @@ -125,11 +125,11 @@ describe('MicrosoftAuth', () => { ); }); - it('gets access token for other azure resources via popup', async () => { + it('gets access + refresh token for other azure resources via popup', async () => { await sut.getAccessToken('azure-resource/scope'); expect(mockRequester).toHaveBeenCalledWith( - new Set(['azure-resource/scope']), + new Set(['azure-resource/scope', 'offline_access']), ); }); @@ -137,7 +137,7 @@ describe('MicrosoftAuth', () => { await sut.getAccessToken('api://customApiClientId/some.scope'); expect(mockRequester).toHaveBeenCalledWith( - new Set(['api://customApiClientId/some.scope']), + new Set(['api://customApiClientId/some.scope', 'offline_access']), ); }); }); diff --git a/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts b/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts index 1b2fc20f37..fa74979c6b 100644 --- a/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.ts @@ -22,8 +22,7 @@ import { DiscoveryApi, OAuthRequestApi, } from '@backstage/core-plugin-api'; -import { OAuth2 } from '../oauth2'; -import { OAuthApiCreateOptions } from '../types'; +import { OAuth2, OAuth2CreateOptions } from '../oauth2'; const DEFAULT_PROVIDER = { id: 'microsoft', @@ -43,13 +42,14 @@ export default class MicrosoftAuth { private provider: AuthProviderInfo; private oauthRequestApi: OAuthRequestApi; private discoveryApi: DiscoveryApi; + private scopeTransform: (scopes: string[]) => string[]; private static MicrosoftGraphID = '00000003-0000-0000-c000-000000000000'; - static create(options: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T { + static create(options: OAuth2CreateOptions): typeof microsoftAuthApiRef.T { return new MicrosoftAuth(options); } - private constructor(options: OAuthApiCreateOptions) { + private constructor(options: OAuth2CreateOptions) { const { configApi, environment = 'development', @@ -63,6 +63,7 @@ export default class MicrosoftAuth { 'email', 'User.Read', ], + scopeTransform = scopes => scopes.concat('offline_access'), } = options; this.configApi = configApi; @@ -70,6 +71,7 @@ export default class MicrosoftAuth { this.provider = provider; this.oauthRequestApi = oauthRequestApi; this.discoveryApi = discoveryApi; + this.scopeTransform = scopeTransform; this.oauth2 = { [MicrosoftAuth.MicrosoftGraphID]: OAuth2.create({ @@ -78,6 +80,7 @@ export default class MicrosoftAuth { oauthRequestApi: this.oauthRequestApi, provider: this.provider, environment: this.environment, + scopeTransform: this.scopeTransform, defaultScopes, }), }; @@ -132,6 +135,7 @@ export default class MicrosoftAuth { oauthRequestApi: this.oauthRequestApi, provider: this.provider, environment: this.environment, + scopeTransform: this.scopeTransform, }); } return this.oauth2[aud].getAccessToken(scope, options);