Merge pull request #32694 from UsainBloot/auth/auth0-invitation-param

[Auth] Auth0 Strategy - Support organization invite parameters
This commit is contained in:
Patrik Oldsberg
2026-02-06 10:19:22 +01:00
committed by GitHub
5 changed files with 130 additions and 5 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend-module-auth0-provider': patch
---
Add support for organizational invites in auth0 strategy
@@ -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",
@@ -87,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(400);
expect(res.text).toContain(
'Organization mismatch. The organization provided in the request does not match the organization configured in the strategy.',
);
});
});
@@ -16,6 +16,8 @@
import Auth0InternalStrategy from 'passport-auth0';
import type { StateStore } from 'passport-oauth2';
import type express from 'express';
import { InputError } from '@backstage/errors';
/** @public */
export interface Auth0StrategyOptionsWithRequest {
@@ -47,15 +49,38 @@ export class Auth0Strategy extends Auth0InternalStrategy {
this.organization = options.organization;
}
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 InputError(
'Organization mismatch. The organization provided in the request does not match the organization configured in the strategy.',
);
}
super.authenticate(req, {
...options,
...(organization ? { organization } : {}),
...(invitation ? { invitation } : {}),
});
}
authorizationParams(options: Record<string, any>): Record<string, any> {
const params = super.authorizationParams(options);
if (this.organization) {
return {
...params,
organization: this.organization,
};
if (options.organization || this.organization) {
params.organization = options.organization || this.organization;
}
if (options.invitation) {
params.invitation = options.invitation;
}
return params;
}
}
+1
View File
@@ -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:^"