chore: Throw error when organization declared in strategy does not match organization in request
Signed-off-by: Jack Palmer <jackpalmer@spotify.com>
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/plugin-auth-node": "workspace:^",
|
||||
"express": "^4.22.0",
|
||||
"passport": "^0.7.0",
|
||||
|
||||
@@ -54,9 +54,7 @@ describe('authModuleAuth0Provider', () => {
|
||||
|
||||
const agent = request.agent(server);
|
||||
|
||||
const res = await agent.get(
|
||||
'/api/auth/auth0/start?env=development&organization=foo-organization&invitation=foo-invitation',
|
||||
);
|
||||
const res = await agent.get('/api/auth/auth0/start?env=development');
|
||||
|
||||
expect(res.status).toEqual(302);
|
||||
|
||||
@@ -80,8 +78,6 @@ describe('authModuleAuth0Provider', () => {
|
||||
accessType: 'offline',
|
||||
connection: 'connection',
|
||||
connection_scope: 'connectionScope',
|
||||
organization: 'foo-organization',
|
||||
invitation: 'foo-invitation',
|
||||
nonce: expect.any(String),
|
||||
state: expect.any(String),
|
||||
});
|
||||
@@ -91,4 +87,97 @@ describe('authModuleAuth0Provider', () => {
|
||||
nonce: decodeURIComponent(nonceCookie.value),
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass through organization and invitation parameters to the authorization URL', async () => {
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
authPlugin,
|
||||
authModuleAuth0Provider,
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
app: {
|
||||
baseUrl: 'http://localhost:3000',
|
||||
},
|
||||
auth: {
|
||||
providers: {
|
||||
auth0: {
|
||||
development: {
|
||||
clientId: 'clientId',
|
||||
clientSecret: 'clientSecret',
|
||||
domain: 'domain',
|
||||
connection: 'connection',
|
||||
connectionScope: 'connectionScope',
|
||||
organization: 'foo-organization',
|
||||
},
|
||||
},
|
||||
},
|
||||
session: {
|
||||
secret: 'secret',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const agent = request.agent(server);
|
||||
|
||||
const res = await agent.get(
|
||||
'/api/auth/auth0/start?env=development&organization=foo-organization&invitation=foo-invitation',
|
||||
);
|
||||
|
||||
const startUrl = new URL(res.get('location'));
|
||||
expect(startUrl.origin).toBe('https://domain');
|
||||
expect(startUrl.pathname).toBe('/authorize');
|
||||
expect(Object.fromEntries(startUrl.searchParams)).toEqual(
|
||||
expect.objectContaining({
|
||||
organization: 'foo-organization',
|
||||
invitation: 'foo-invitation',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the organization in the request does not match the organization configured in the strategy', async () => {
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
authPlugin,
|
||||
authModuleAuth0Provider,
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
app: {
|
||||
baseUrl: 'http://localhost:3000',
|
||||
},
|
||||
auth: {
|
||||
providers: {
|
||||
auth0: {
|
||||
development: {
|
||||
clientId: 'clientId',
|
||||
clientSecret: 'clientSecret',
|
||||
domain: 'domain',
|
||||
connection: 'connection',
|
||||
connectionScope: 'connectionScope',
|
||||
organization: 'bar-organization',
|
||||
},
|
||||
},
|
||||
},
|
||||
session: {
|
||||
secret: 'secret',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const agent = request.agent(server);
|
||||
|
||||
const res = await agent.get(
|
||||
'/api/auth/auth0/start?env=development&organization=foo-organization&invitation=foo-invitation',
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(409);
|
||||
expect(res.text).toContain(
|
||||
'Organization mismatch. The organization provided in the request does not match the organization configured in the strategy.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import Auth0InternalStrategy from 'passport-auth0';
|
||||
import type { StateStore } from 'passport-oauth2';
|
||||
import type express from 'express';
|
||||
import { ConflictError } from '@backstage/errors';
|
||||
|
||||
/** @public */
|
||||
export interface Auth0StrategyOptionsWithRequest {
|
||||
@@ -51,6 +52,17 @@ export class Auth0Strategy extends Auth0InternalStrategy {
|
||||
authenticate(req: express.Request, options: Record<string, any>): void {
|
||||
const { organization, invitation } = req.query;
|
||||
|
||||
// Throw an error if the organization in the request does not match the organization configured in the strategy
|
||||
if (
|
||||
organization &&
|
||||
this.organization &&
|
||||
organization !== this.organization
|
||||
) {
|
||||
throw new ConflictError(
|
||||
'Organization mismatch. The organization provided in the request does not match the organization configured in the strategy.',
|
||||
);
|
||||
}
|
||||
|
||||
super.authenticate(req, {
|
||||
...options,
|
||||
...(organization ? { organization } : {}),
|
||||
|
||||
@@ -4284,6 +4284,7 @@ __metadata:
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/plugin-auth-backend": "workspace:^"
|
||||
"@backstage/plugin-auth-node": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user