Merge pull request #23897 from postsa/postsa/atlassian-auth-provider-fix
(BUGFIX: plugin-atlassian-auth-provider) pass scope to strategy
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend-module-atlassian-provider': patch
|
||||
---
|
||||
|
||||
Read scopes from config and pass to AtlassianProvider as they are required
|
||||
+1
-1
@@ -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: {}
|
||||
|
||||
|
||||
@@ -46,16 +46,16 @@ auth:
|
||||
development:
|
||||
clientId: ${AUTH_ATLASSIAN_CLIENT_ID}
|
||||
clientSecret: ${AUTH_ATLASSIAN_CLIENT_SECRET}
|
||||
scopes: ${AUTH_ATLASSIAN_SCOPES}
|
||||
scope: ${AUTH_ATLASSIAN_SCOPES}
|
||||
```
|
||||
|
||||
The Atlassian provider is a structure with three configuration keys:
|
||||
|
||||
- `clientId`: The Key you generated in the developer console.
|
||||
- `clientSecret`: The Secret tied to the generated Key.
|
||||
- `scopes`: List of scopes the app has permissions for, separated by spaces.
|
||||
- `scope`: List of scopes the app has permissions for, separated by spaces.
|
||||
|
||||
**NOTE:** the scopes `offline_access` and `read:me` are provided by default.
|
||||
**NOTE:** the scopes `offline_access`, `read:jira-work`, and `read:jira-user` are provided by default.
|
||||
|
||||
## Adding the provider to the Backstage frontend
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface Config {
|
||||
clientSecret: string;
|
||||
audience?: string;
|
||||
callbackUrl?: string;
|
||||
scope?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -29,6 +29,10 @@ export const atlassianAuthenticator = createOAuthAuthenticator({
|
||||
initialize({ callbackUrl, config }) {
|
||||
const clientId = config.getString('clientId');
|
||||
const clientSecret = config.getString('clientSecret');
|
||||
const scope =
|
||||
config.getOptionalString('scope') ??
|
||||
config.getOptionalString('scopes') ??
|
||||
'offline_access read:jira-work read:jira-user';
|
||||
const baseUrl = 'https://auth.atlassian.com';
|
||||
|
||||
return PassportOAuthAuthenticatorHelper.from(
|
||||
@@ -41,6 +45,7 @@ export const atlassianAuthenticator = createOAuthAuthenticator({
|
||||
authorizationURL: `${baseUrl}/authorize`,
|
||||
tokenURL: `${baseUrl}/oauth/token`,
|
||||
profileURL: `${baseUrl}/api/v4/user`,
|
||||
scope,
|
||||
},
|
||||
(
|
||||
accessToken: string,
|
||||
|
||||
@@ -67,6 +67,119 @@ describe('authModuleAtlassianProvider', () => {
|
||||
client_id: 'my-client-id',
|
||||
redirect_uri: `http://localhost:${server.port()}/api/auth/atlassian/handler/frame`,
|
||||
state: expect.any(String),
|
||||
scope: 'offline_access read:jira-work read:jira-user',
|
||||
});
|
||||
|
||||
expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({
|
||||
env: 'development',
|
||||
nonce: decodeURIComponent(nonceCookie.value),
|
||||
});
|
||||
});
|
||||
it('should start with and use custom scopes from scope config field', 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',
|
||||
scope: '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),
|
||||
});
|
||||
});
|
||||
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({
|
||||
|
||||
Reference in New Issue
Block a user