From 7a7e10388257f12c9fda022d6aba8502844926a6 Mon Sep 17 00:00:00 2001 From: Spencer Post Date: Fri, 29 Mar 2024 14:53:48 -0600 Subject: [PATCH] support scope and scopes in config Signed-off-by: Spencer Post --- app-config.yaml | 2 +- .../src/authenticator.ts | 3 +- .../src/module.test.ts | 58 ++++++++++++++++++- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 7950768350..c016260bf8 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -396,7 +396,7 @@ auth: development: clientId: ${AUTH_ATLASSIAN_CLIENT_ID} clientSecret: ${AUTH_ATLASSIAN_CLIENT_SECRET} - scopes: ${AUTH_ATLASSIAN_SCOPES} + scope: ${AUTH_ATLASSIAN_SCOPES} myproxy: {} guest: {} diff --git a/plugins/auth-backend-module-atlassian-provider/src/authenticator.ts b/plugins/auth-backend-module-atlassian-provider/src/authenticator.ts index 0538d8d742..b31de13134 100644 --- a/plugins/auth-backend-module-atlassian-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-atlassian-provider/src/authenticator.ts @@ -30,7 +30,8 @@ export const atlassianAuthenticator = createOAuthAuthenticator({ const clientId = config.getString('clientId'); const clientSecret = config.getString('clientSecret'); const scope = - config.getOptionalString('scope') || + config.getOptionalString('scope') ?? + config.getOptionalString('scopes') ?? 'offline_access read:jira-work read:jira-user'; const baseUrl = 'https://auth.atlassian.com'; diff --git a/plugins/auth-backend-module-atlassian-provider/src/module.test.ts b/plugins/auth-backend-module-atlassian-provider/src/module.test.ts index 213df4b0fc..08b84ad165 100644 --- a/plugins/auth-backend-module-atlassian-provider/src/module.test.ts +++ b/plugins/auth-backend-module-atlassian-provider/src/module.test.ts @@ -75,7 +75,7 @@ describe('authModuleAtlassianProvider', () => { nonce: decodeURIComponent(nonceCookie.value), }); }); - it('should start with and use custom scopes', async () => { + it('should start with and use custom scopes from scope config field', async () => { const { server } = await startTestBackend({ features: [ import('@backstage/plugin-auth-backend'), @@ -126,6 +126,62 @@ describe('authModuleAtlassianProvider', () => { scope: 'offline_access read:filter:jira read:jira-work', }); + expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({ + env: 'development', + nonce: decodeURIComponent(nonceCookie.value), + }); + }); + it('should start with and use custom scopes from scopes config field for backward compatibility', async () => { + const { server } = await startTestBackend({ + features: [ + import('@backstage/plugin-auth-backend'), + authModuleAtlassianProvider, + mockServices.rootConfig.factory({ + data: { + app: { + baseUrl: 'http://localhost:3000', + }, + auth: { + providers: { + atlassian: { + development: { + clientId: 'my-client-id', + clientSecret: 'my-client-secret', + scopes: 'offline_access read:filter:jira read:jira-work', + }, + }, + }, + }, + }, + }), + ], + }); + + const agent = request.agent(server); + + const res = await agent.get('/api/auth/atlassian/start?env=development'); + + expect(res.status).toEqual(302); + + const nonceCookie = agent.jar.getCookie('atlassian-nonce', { + domain: 'localhost', + path: '/api/auth/atlassian/handler', + script: false, + secure: false, + }); + expect(nonceCookie).toBeDefined(); + + const startUrl = new URL(res.get('location')); + expect(startUrl.origin).toBe('https://auth.atlassian.com'); + expect(startUrl.pathname).toBe('/authorize'); + expect(Object.fromEntries(startUrl.searchParams)).toEqual({ + response_type: 'code', + client_id: 'my-client-id', + redirect_uri: `http://localhost:${server.port()}/api/auth/atlassian/handler/frame`, + state: expect.any(String), + scope: 'offline_access read:filter:jira read:jira-work', + }); + expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({ env: 'development', nonce: decodeURIComponent(nonceCookie.value),