Merge pull request #32499 from UsainBloot/auth/auth0-organizations

[Auth] Auth0 Provider - Add organization option
This commit is contained in:
Fredrik Adelöw
2026-01-23 15:46:17 +01:00
committed by GitHub
8 changed files with 130 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend-module-auth0-provider': minor
---
feat: Added organization option to authorization params of the strategy
+2
View File
@@ -44,6 +44,7 @@ auth:
audience: ${AUTH_AUTH0_AUDIENCE}
connection: ${AUTH_AUTH0_CONNECTION}
connectionScope: ${AUTH_AUTH0_CONNECTION_SCOPE}
organization: ${AUTH_AUTH0_ORGANIZATION_ID}
## uncomment to set lifespan of user session
# sessionDuration: { hours: 24 } # supports `ms` library format (e.g. '24h', '2 days'), ISO duration, "human duration" as used in code
session:
@@ -69,6 +70,7 @@ Auth0 requires a session, so you need to give the session a secret key.
- `connection`: Social identity provider name. To check the available social connections, please visit [Auth0 Social Connections](https://marketplace.auth0.com/features/social-connections).
- `connectionScope`: Additional scopes in the interactive token request. It should always be used in combination with the `connection` parameter.
- `sessionDuration`: Lifespan of the user session.
- `organization`: Specify a specific organization ID to be targeted as part of the login flow.
### Resolvers
@@ -32,6 +32,7 @@ export interface Config {
audience?: string;
connection?: string;
connectionScope?: string;
organization?: string;
sessionDuration?: HumanDuration | string;
};
};
@@ -37,6 +37,7 @@
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/plugin-auth-node": "workspace:^",
"express": "^4.22.0",
"passport": "^0.7.0",
"passport-auth0": "^1.4.3",
"passport-oauth2": "^1.6.1"
},
@@ -46,6 +47,7 @@
"@backstage/cli": "workspace:^",
"@backstage/plugin-auth-backend": "workspace:^",
"@backstage/types": "workspace:^",
"@types/passport": "^1.0.3",
"@types/passport-auth0": "^1.0.5",
"@types/passport-oauth2": "^1.4.15",
"supertest": "^7.0.0"
@@ -35,6 +35,7 @@ export const auth0Authenticator = createOAuthAuthenticator({
const connection = config.getOptionalString('connection');
const connectionScope = config.getOptionalString('connectionScope');
const callbackURL = config.getOptionalString('callbackUrl') ?? callbackUrl;
const organization = config.getOptionalString('organization');
// Due to passport-auth0 forcing options.state = true,
// passport-oauth2 requires express-session to be installed
// so that the 'state' parameter of the oauth2 flow can be stored.
@@ -58,6 +59,7 @@ export const auth0Authenticator = createOAuthAuthenticator({
callbackURL,
domain,
store,
organization,
// We need passReqToCallback set to false to get params, but there's
// no matching type signature for that, so instead behold this beauty
passReqToCallback: false as true,
@@ -25,10 +25,13 @@ export interface Auth0StrategyOptionsWithRequest {
domain: string;
passReqToCallback: true;
store: StateStore;
organization?: string;
}
/** @public */
export class Auth0Strategy extends Auth0InternalStrategy {
private organization: string | undefined;
constructor(
options: Auth0StrategyOptionsWithRequest,
verify: Auth0InternalStrategy.VerifyFunction,
@@ -41,5 +44,18 @@ export class Auth0Strategy extends Auth0InternalStrategy {
apiUrl: `https://${options.domain}/api`,
};
super(optionsWithURLs, verify);
this.organization = options.organization;
}
authorizationParams(options: Record<string, any>): Record<string, any> {
const params = super.authorizationParams(options);
if (this.organization) {
return {
...params,
organization: this.organization,
};
}
return params;
}
}
@@ -0,0 +1,100 @@
/*
* Copyright 2026 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'passport-auth0' {
import passport from 'passport';
import express from 'express';
class StrategyInternal extends passport.Strategy {
constructor(
options: StrategyInternal.StrategyOptionWithRequest,
verify: StrategyInternal.VerifyFunctionWithRequest,
);
constructor(
options: StrategyInternal.StrategyOption,
verify: StrategyInternal.VerifyFunction,
);
name: string;
authenticate(req: express.Request, options?: object): void;
authorizationParams(options: Record<string, any>): Record<string, any>;
}
namespace StrategyInternal {
interface Profile extends passport.Profile {
id: string;
displayName: string;
gender?: string | undefined;
ageRange?:
| {
min: number;
max?: number | undefined;
}
| undefined;
profileUrl?: string | undefined;
username?: string | undefined;
birthday: string;
_raw: string;
_json: any;
}
interface AuthenticateOptions extends passport.AuthenticateOptions {
authType?: string | undefined;
}
interface StrategyOption {
clientID: string;
clientSecret: string;
callbackURL: string;
domain: string;
scopeSeparator?: string | undefined;
enableProof?: boolean | undefined;
profileFields?: string[] | undefined;
state?: boolean | undefined;
}
interface StrategyOptionWithRequest extends StrategyOption {
passReqToCallback: true;
}
interface ExtraVerificationParams {
audience?: string | undefined;
connection?: string | undefined;
prompt?: string | undefined;
}
type VerifyFunction = (
accessToken: string,
refreshToken: string,
extraParams: ExtraVerificationParams,
profile: Profile,
done: (error: any, user?: any, info?: any) => void,
) => void;
type VerifyFunctionWithRequest = (
req: express.Request,
accessToken: string,
refreshToken: string,
extraParams: ExtraVerificationParams,
profile: Profile,
done: (error: any, user?: any, info?: any) => void,
) => void;
export import Strategy = StrategyInternal;
}
export = StrategyInternal;
}
+2
View File
@@ -4283,9 +4283,11 @@ __metadata:
"@backstage/plugin-auth-backend": "workspace:^"
"@backstage/plugin-auth-node": "workspace:^"
"@backstage/types": "workspace:^"
"@types/passport": "npm:^1.0.3"
"@types/passport-auth0": "npm:^1.0.5"
"@types/passport-oauth2": "npm:^1.4.15"
express: "npm:^4.22.0"
passport: "npm:^0.7.0"
passport-auth0: "npm:^1.4.3"
passport-oauth2: "npm:^1.6.1"
supertest: "npm:^7.0.0"