feat: migrate auth0 provider to nbs

Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
Camila Belo
2024-09-02 15:27:11 +02:00
parent b1d16d2422
commit d908d8c246
22 changed files with 686 additions and 218 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-node': minor
---
Accepts an optional options object in the `PassportOAuthAuthenticatorHelper.authenticate` method.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': patch
---
Migrated the `Auth0` auth provider to be implemented using the new `@backstage/plugin-auth-backend-module-auth0-provider` module.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend-module-auth0-provider': minor
---
New module for `@backstage/plugin-auth-backend` that adds a Auth0 auth provider.
+75
View File
@@ -0,0 +1,75 @@
---
id: provider--old
title: Auth0 Authentication Provider
sidebar_label: Auth0
description: Adding Auth0 as an authentication provider in Backstage
---
:::info
This documentation is written for the old backend which has been replaced by
[the new backend system](../../backend-system/index.md), being the default since
Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new
backend system, you may want to read [its own article](./provider.md)
instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)!
:::
The Backstage `core-plugin-api` package comes with an Auth0 authentication
provider that can authenticate users using OAuth.
## Create an Auth0 Application
1. Log in to the [Auth0 dashboard](https://manage.auth0.com/dashboard/)
2. Navigate to **Applications**
3. Create an Application
- Name: Backstage (or your custom app name)
- Application type: Single Page Web Application
4. Click on the Settings tab
5. Add under `Application URIs` > `Allowed Callback URLs`:
`http://localhost:7007/api/auth/auth0/handler/frame`
6. Click `Save Changes`
## Configuration
The provider configuration can then be added to your `app-config.yaml` under the
root `auth` configuration:
```yaml
auth:
environment: development
providers:
auth0:
development:
clientId: ${AUTH_AUTH0_CLIENT_ID}
clientSecret: ${AUTH_AUTH0_CLIENT_SECRET}
domain: ${AUTH_AUTH0_DOMAIN_ID}
audience: ${AUTH_AUTH0_AUDIENCE}
connection: ${AUTH_AUTH0_CONNECTION}
connectionScope: ${AUTH_AUTH0_CONNECTION_SCOPE}
session:
secret: ${AUTH_SESSION_SECRET}
```
The Auth0 provider is a structure with these configuration keys:
- `clientId`: The Application client ID, found on the Auth0 Application page
- `clientSecret`: The Application client secret, found on the Auth0 Application
page
- `domain`: The Application domain, found on the Auth0 Application page
It additionally relies on the following configuration to function:
- `session.secret`: The session secret is a key used for signing and/or encrypting cookies set by the application to maintain session state. In this case, 'your session secret' should be replaced with a long, complex, and unique string that only your application knows.
Auth0 requires a session, so you need to give the session a secret key.
## Optional Configuration
- `audience`: The intended recipients of the token
- `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
## Adding the provider to the Backstage frontend
To add the provider to the frontend, add the `auth0AuthApi` reference and
`SignInPage` component as shown in
[Adding the provider to the sign-in page](../index.md#sign-in-configuration).
+42 -1
View File
@@ -5,6 +5,13 @@ sidebar_label: Auth0
description: Adding Auth0 as an authentication provider in Backstage
---
:::info
This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage
[version 1.24](../../releases/v1.24.0.md). If you are still on the old backend
system, you may want to read [its own article](./provider--old.md)
instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)!
:::
The Backstage `core-plugin-api` package comes with an Auth0 authentication
provider that can authenticate users using OAuth.
@@ -54,12 +61,46 @@ It additionally relies on the following configuration to function:
Auth0 requires a session, so you need to give the session a secret key.
## Optional Configuration
### Optional
- `audience`: The intended recipients of the token
- `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
### Resolvers
This provider includes several resolvers out of the box that you can use:
- `emailMatchingUserEntityProfileEmail`: Matches the email address from the auth provider with the User entity that has a matching `spec.profile.email`. If no match is found it will throw a `NotFoundError`.
- `emailLocalPartMatchingUserEntityName`: Matches the [local part](https://en.wikipedia.org/wiki/Email_address#Local-part) of the email address from the auth provider with the User entity that has a matching `name`. If no match is found it will throw a `NotFoundError`.
:::note Note
The resolvers will be tried in order, but will only be skipped if they throw a `NotFoundError`.
:::
If these resolvers do not fit your needs you can build a custom resolver, this is covered in the [Building Custom Resolvers](../identity-resolver.md#building-custom-resolvers) section of the Sign-in Identities and Resolvers documentation.
## Backend Installation
To add the provider to the backend we will first need to install the package by running this command:
```bash title="from your Backstage root directory"
yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-auth0-provider
```
Then we will need to add this line:
```ts title="packages/backend/src/index.ts"
import { createBackend } from '@backstage/backend-defaults';
//...
backend.add(import('@backstage/plugin-auth-backend'));
// highlight-add-next-line
backend.add(import('@backstage/plugin-auth-backend-module-auth0-provider'));
//...
```
## Adding the provider to the Backstage frontend
To add the provider to the frontend, add the `auth0AuthApi` reference and
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
@@ -0,0 +1,8 @@
# Auth Module: Auth0 Provider
This module provides an Auth0 auth provider implementation for `@backstage/plugin-auth-backend`.
## Links
- [Repository](https://gitlab.com/backstage/backstage/tree/master/plugins/auth-backend-module-auth0-provider)
- [Backstage Project Homepage](https://backstage.io)
@@ -0,0 +1,51 @@
## API Report File for "@backstage/plugin-auth-backend-module-auth0-provider"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import Auth0InternalStrategy from 'passport-auth0';
import { BackendFeature } from '@backstage/backend-plugin-api';
import { OAuthAuthenticator } from '@backstage/plugin-auth-node';
import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node';
import { PassportProfile } from '@backstage/plugin-auth-node';
import { StateStore } from 'passport-oauth2';
// @public (undocumented)
export const auth0Authenticator: OAuthAuthenticator<
{
helper: PassportOAuthAuthenticatorHelper;
audience: string | undefined;
connection: string | undefined;
connectionScope: string | undefined;
},
PassportProfile
>;
// @public (undocumented)
export class Auth0Strategy extends Auth0InternalStrategy {
constructor(
options: Auth0StrategyOptionsWithRequest,
verify: Auth0InternalStrategy.VerifyFunction,
);
}
// @public (undocumented)
export interface Auth0StrategyOptionsWithRequest {
// (undocumented)
callbackURL: string;
// (undocumented)
clientID: string;
// (undocumented)
clientSecret: string;
// (undocumented)
domain: string;
// (undocumented)
passReqToCallback: true;
// (undocumented)
store: StateStore;
}
// @public (undocumented)
const authModuleAuth0Provider: BackendFeature;
export default authModuleAuth0Provider;
```
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-plugin-auth-backend-module-auth0-provider
title: '@backstage/plugin-auth-backend-module-auth0-provider'
description: The auth0-provider backend module for the auth plugin.
spec:
lifecycle: experimental
type: backstage-backend-plugin-module
owner: maintainers
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright 2024 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.
*/
export interface Config {
auth?: {
providers?: {
/** @visibility frontend */
auth0?: {
[authEnv: string]: {
clientId: string;
/**
* @visibility secret
*/
clientSecret: string;
domain: string;
callbackUrl?: string;
audience?: string;
connection?: string;
connectionScope?: string;
};
};
};
};
}
@@ -0,0 +1,26 @@
/*
* Copyright 2024 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.
*/
import { createBackend } from '@backstage/backend-defaults';
import authPlugin from '@backstage/plugin-auth-backend';
import authModuleAuth0Provider from '../src';
const backend = createBackend();
backend.add(authPlugin);
backend.add(authModuleAuth0Provider);
backend.start();
@@ -0,0 +1,53 @@
{
"name": "@backstage/plugin-auth-backend-module-auth0-provider",
"version": "0.0.0",
"description": "The auth0-provider backend module for the auth plugin.",
"backstage": {
"role": "backend-plugin-module",
"pluginId": "auth",
"pluginPackage": "@backstage/plugin-auth-backend"
},
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "plugins/auth-backend-module-auth0-provider"
},
"license": "Apache-2.0",
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist",
"config.d.ts"
],
"scripts": {
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"lint": "backstage-cli package lint",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack",
"start": "backstage-cli package start",
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/plugin-auth-node": "workspace:^",
"@types/passport-auth0": "^1.0.5",
"@types/passport-oauth2": "^1.4.15",
"express": "^4.17.1",
"passport-auth0": "^1.4.3",
"passport-oauth2": "^1.6.1"
},
"devDependencies": {
"@backstage/backend-defaults": "workspace:^",
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"@backstage/plugin-auth-backend": "workspace:^",
"supertest": "^6.3.3"
},
"configSchema": "config.d.ts"
}
@@ -0,0 +1,116 @@
/*
* Copyright 2024 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.
*/
import express from 'express';
import {
createOAuthAuthenticator,
PassportOAuthAuthenticatorHelper,
PassportOAuthDoneCallback,
PassportProfile,
} from '@backstage/plugin-auth-node';
import { Auth0Strategy } from './strategy';
/** @public */
export const auth0Authenticator = createOAuthAuthenticator({
defaultProfileTransform:
PassportOAuthAuthenticatorHelper.defaultProfileTransform,
initialize({ callbackUrl, config }) {
const clientID = config.getString('clientId');
const clientSecret = config.getString('clientSecret');
const domain = config.getString('domain');
const audience = config.getOptionalString('audience');
const connection = config.getOptionalString('connection');
const connectionScope = config.getOptionalString('connectionScope');
const callbackURL = config.getOptionalString('callbackUrl') ?? callbackUrl;
// 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.
// This implementation of StateStore matches the NullStore found within
// passport-oauth2, which is the StateStore implementation used when options.state = false,
// allowing us to avoid using express-session in order to integrate with auth0.
const store = {
store(_req: express.Request, cb: any) {
cb(null, null);
},
verify(_req: express.Request, _state: string, cb: any) {
cb(null, true);
},
};
const helper = PassportOAuthAuthenticatorHelper.from(
new Auth0Strategy(
{
clientID,
clientSecret,
callbackURL,
domain,
store,
// 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,
},
(
accessToken: string,
refreshToken: string,
params: any,
fullProfile: PassportProfile,
done: PassportOAuthDoneCallback,
) => {
done(
undefined,
{
fullProfile,
accessToken,
params,
},
{
refreshToken,
},
);
},
),
);
return { helper, audience, connection, connectionScope };
},
async start(
input,
{ helper, audience, connection, connectionScope: connection_scope },
) {
return helper.start(input, {
accessType: 'offline',
prompt: 'consent',
...(audience ? { audience } : {}),
...(connection ? { connection } : {}),
...(connection_scope ? { connection_scope } : {}),
});
},
async authenticate(
input,
{ helper, audience, connection, connectionScope: connection_scope },
) {
return helper.authenticate(input, {
...(audience ? { audience } : {}),
...(connection ? { connection } : {}),
...(connection_scope ? { connection_scope } : {}),
});
},
async refresh(input, { helper }) {
return helper.refresh(input);
},
});
@@ -0,0 +1,28 @@
/*
* Copyright 2024 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.
*/
/**
* The auth0-provider backend module for the auth plugin.
*
* @packageDocumentation
*/
export {
Auth0Strategy,
type Auth0StrategyOptionsWithRequest,
} from './strategy';
export { auth0Authenticator } from './authenticator';
export { authModuleAuth0Provider as default } from './module';
@@ -0,0 +1,90 @@
/*
* Copyright 2024 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.
*/
import { mockServices, startTestBackend } from '@backstage/backend-test-utils';
import authPlugin from '@backstage/plugin-auth-backend';
import { authModuleAuth0Provider } from './module';
import request from 'supertest';
import { decodeOAuthState } from '@backstage/plugin-auth-node';
describe('authModuleAuth0Provider', () => {
it('should start', 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',
},
},
},
session: {
secret: 'secret',
},
},
},
}),
],
});
const agent = request.agent(server);
const res = await agent.get('/api/auth/auth0/start?env=development');
expect(res.status).toEqual(302);
const nonceCookie = agent.jar.getCookie('auth0-nonce', {
domain: 'localhost',
path: '/api/auth/auth0/handler',
script: false,
secure: false,
});
expect(nonceCookie).toBeDefined();
const startUrl = new URL(res.get('location'));
expect(startUrl.origin).toBe('https://domain');
expect(startUrl.pathname).toBe('/authorize');
expect(Object.fromEntries(startUrl.searchParams)).toEqual({
response_type: 'code',
scope: '',
client_id: 'clientId',
redirect_uri: `http://localhost:${server.port()}/api/auth/auth0/handler/frame`,
prompt: 'consent',
accessType: 'offline',
connection: 'connection',
connection_scope: 'connectionScope',
nonce: expect.any(String),
state: expect.any(String),
});
expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({
env: 'development',
nonce: decodeURIComponent(nonceCookie.value),
});
});
});
@@ -0,0 +1,47 @@
/*
* Copyright 2024 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.
*/
import { createBackendModule } from '@backstage/backend-plugin-api';
import {
authProvidersExtensionPoint,
commonSignInResolvers,
createOAuthProviderFactory,
} from '@backstage/plugin-auth-node';
import { auth0Authenticator } from './authenticator';
/** @public */
export const authModuleAuth0Provider = createBackendModule({
pluginId: 'auth',
moduleId: 'auth0-provider',
register(reg) {
reg.registerInit({
deps: {
providers: authProvidersExtensionPoint,
},
async init({ providers }) {
providers.registerProvider({
providerId: 'auth0',
factory: createOAuthProviderFactory({
authenticator: auth0Authenticator,
signInResolverFactories: {
...commonSignInResolvers,
},
}),
});
},
});
},
});
@@ -0,0 +1,45 @@
/*
* Copyright 2024 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.
*/
import Auth0InternalStrategy from 'passport-auth0';
import { StateStore } from 'passport-oauth2';
/** @public */
export interface Auth0StrategyOptionsWithRequest {
clientID: string;
clientSecret: string;
callbackURL: string;
domain: string;
passReqToCallback: true;
store: StateStore;
}
/** @public */
export class Auth0Strategy extends Auth0InternalStrategy {
constructor(
options: Auth0StrategyOptionsWithRequest,
verify: Auth0InternalStrategy.VerifyFunction,
) {
const optionsWithURLs = {
...options,
authorizationURL: `https://${options.domain}/authorize`,
tokenURL: `https://${options.domain}/oauth/token`,
userInfoURL: `https://${options.domain}/userinfo`,
apiUrl: `https://${options.domain}/api`,
};
super(optionsWithURLs, verify);
}
}
+1
View File
@@ -50,6 +50,7 @@
"@backstage/config": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/plugin-auth-backend-module-atlassian-provider": "workspace:^",
"@backstage/plugin-auth-backend-module-auth0-provider": "workspace:^",
"@backstage/plugin-auth-backend-module-aws-alb-provider": "workspace:^",
"@backstage/plugin-auth-backend-module-azure-easyauth-provider": "workspace:^",
"@backstage/plugin-auth-backend-module-bitbucket-provider": "workspace:^",
@@ -14,40 +14,25 @@
* limitations under the License.
*/
import express from 'express';
import passport from 'passport';
import Auth0Strategy from './strategy';
import {
OAuthAdapter,
OAuthProviderOptions,
OAuthHandlers,
OAuthResponse,
OAuthEnvironmentHandler,
OAuthStartRequest,
encodeState,
OAuthRefreshRequest,
OAuthResult,
} from '../../lib/oauth';
import {
executeFetchUserProfileStrategy,
executeFrameHandlerStrategy,
executeRedirectStrategy,
executeRefreshTokenStrategy,
makeProfileInfo,
PassportDoneCallback,
} from '../../lib/passport';
import { OAuthStartResponse, AuthHandler } from '../types';
import { OAuthProviderOptions, OAuthResult } from '../../lib/oauth';
import { AuthHandler } from '../types';
import { createAuthProviderIntegration } from '../createAuthProviderIntegration';
import { StateStore } from 'passport-oauth2';
import {
AuthResolverContext,
createOAuthProviderFactory,
SignInResolver,
} from '@backstage/plugin-auth-node';
import {
adaptLegacyOAuthHandler,
adaptLegacyOAuthSignInResolver,
} from '../../lib/legacy';
import { auth0Authenticator } from '@backstage/plugin-auth-backend-module-auth0-provider';
type PrivateInfo = {
refreshToken: string;
};
/**
* @public
* @deprecated The Auth0 auth provider was extracted to `@backstage/plugin-auth-backend-module-auth0-provider`.
*/
export type Auth0AuthProviderOptions = OAuthProviderOptions & {
domain: string;
signInResolver?: SignInResolver<OAuthResult>;
@@ -58,155 +43,6 @@ export type Auth0AuthProviderOptions = OAuthProviderOptions & {
connectionScope?: string;
};
export class Auth0AuthProvider implements OAuthHandlers {
private readonly _strategy: Auth0Strategy;
private readonly signInResolver?: SignInResolver<OAuthResult>;
private readonly authHandler: AuthHandler<OAuthResult>;
private readonly resolverContext: AuthResolverContext;
private readonly audience?: string;
private readonly connection?: string;
private readonly connectionScope?: string;
/**
* 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.
* This implementation of StateStore matches the NullStore found within
* passport-oauth2, which is the StateStore implementation used when options.state = false,
* allowing us to avoid using express-session in order to integrate with auth0.
*/
private store: StateStore = {
store(_req: express.Request, cb: any) {
cb(null, null);
},
verify(_req: express.Request, _state: string, cb: any) {
cb(null, true);
},
};
constructor(options: Auth0AuthProviderOptions) {
this.signInResolver = options.signInResolver;
this.authHandler = options.authHandler;
this.resolverContext = options.resolverContext;
this.audience = options.audience;
this.connection = options.connection;
this.connectionScope = options.connectionScope;
this._strategy = new Auth0Strategy(
{
clientID: options.clientId,
clientSecret: options.clientSecret,
callbackURL: options.callbackUrl,
domain: options.domain,
// 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,
store: this.store,
},
(
accessToken: any,
refreshToken: any,
params: any,
fullProfile: passport.Profile,
done: PassportDoneCallback<OAuthResult, PrivateInfo>,
) => {
done(
undefined,
{
fullProfile,
accessToken,
refreshToken,
params,
},
{
refreshToken,
},
);
},
);
}
async start(req: OAuthStartRequest): Promise<OAuthStartResponse> {
return await executeRedirectStrategy(req, this._strategy, {
accessType: 'offline',
prompt: 'consent',
scope: req.scope,
state: encodeState(req.state),
...(this.audience ? { audience: this.audience } : {}),
...(this.connection ? { connection: this.connection } : {}),
...(this.connectionScope
? { connection_scope: this.connectionScope }
: {}),
});
}
async handler(req: express.Request) {
const { result, privateInfo } = await executeFrameHandlerStrategy<
OAuthResult,
PrivateInfo
>(req, this._strategy, {
...(this.audience ? { audience: this.audience } : {}),
...(this.connection ? { connection: this.connection } : {}),
...(this.connectionScope
? { connection_scope: this.connectionScope }
: {}),
});
return {
response: await this.handleResult(result),
refreshToken: privateInfo.refreshToken,
};
}
async refresh(req: OAuthRefreshRequest) {
const { accessToken, refreshToken, params } =
await executeRefreshTokenStrategy(
this._strategy,
req.refreshToken,
req.scope,
);
const fullProfile = await executeFetchUserProfileStrategy(
this._strategy,
accessToken,
);
return {
response: await this.handleResult({
fullProfile,
params,
accessToken,
}),
refreshToken,
};
}
private async handleResult(result: OAuthResult) {
const { profile } = await this.authHandler(result, this.resolverContext);
const response: OAuthResponse = {
providerInfo: {
idToken: result.params.id_token,
accessToken: result.accessToken,
scope: result.params.scope,
expiresInSeconds: result.params.expires_in,
},
profile,
};
if (this.signInResolver) {
response.backstageIdentity = await this.signInResolver(
{
result,
profile,
},
this.resolverContext,
);
}
return response;
}
}
/**
* Auth provider integration for auth0 auth
*
@@ -230,44 +66,10 @@ export const auth0 = createAuthProviderIntegration({
resolver: SignInResolver<OAuthResult>;
};
}) {
return ({ providerId, globalConfig, config, resolverContext }) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
const domain = envConfig.getString('domain');
const customCallbackUrl = envConfig.getOptionalString('callbackUrl');
const audience = envConfig.getOptionalString('audience');
const connection = envConfig.getOptionalString('connection');
const connectionScope = envConfig.getOptionalString('connectionScope');
const callbackUrl =
customCallbackUrl ||
`${globalConfig.baseUrl}/${providerId}/handler/frame`;
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
? options.authHandler
: async ({ fullProfile, params }) => ({
profile: makeProfileInfo(fullProfile, params.id_token),
});
const signInResolver = options?.signIn?.resolver;
const provider = new Auth0AuthProvider({
clientId,
clientSecret,
callbackUrl,
domain,
authHandler,
signInResolver,
resolverContext,
audience,
connection,
connectionScope,
});
return OAuthAdapter.fromConfig(globalConfig, provider, {
providerId,
callbackUrl,
});
});
return createOAuthProviderFactory({
authenticator: auth0Authenticator,
profileTransform: adaptLegacyOAuthHandler(options?.authHandler),
signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver),
});
},
});
+1
View File
@@ -503,6 +503,7 @@ export class PassportOAuthAuthenticatorHelper {
// (undocumented)
authenticate(
input: OAuthAuthenticatorAuthenticateInput,
options?: Record<string, string>,
): Promise<OAuthAuthenticatorResult<PassportProfile>>;
// (undocumented)
static defaultProfileTransform: ProfileTransform<
@@ -85,12 +85,13 @@ export class PassportOAuthAuthenticatorHelper {
async authenticate(
input: OAuthAuthenticatorAuthenticateInput,
options?: Record<string, string>,
): Promise<OAuthAuthenticatorResult<PassportProfile>> {
const { result, privateInfo } =
await PassportHelpers.executeFrameHandlerStrategy<
PassportOAuthResult,
PassportOAuthPrivateInfo
>(input.req, this.#strategy);
>(input.req, this.#strategy, options);
return {
fullProfile: result.fullProfile as PassportProfile,
+20
View File
@@ -4795,6 +4795,25 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/plugin-auth-backend-module-auth0-provider@workspace:^, @backstage/plugin-auth-backend-module-auth0-provider@workspace:plugins/auth-backend-module-auth0-provider":
version: 0.0.0-use.local
resolution: "@backstage/plugin-auth-backend-module-auth0-provider@workspace:plugins/auth-backend-module-auth0-provider"
dependencies:
"@backstage/backend-defaults": "workspace:^"
"@backstage/backend-plugin-api": "workspace:^"
"@backstage/backend-test-utils": "workspace:^"
"@backstage/cli": "workspace:^"
"@backstage/plugin-auth-backend": "workspace:^"
"@backstage/plugin-auth-node": "workspace:^"
"@types/passport-auth0": ^1.0.5
"@types/passport-oauth2": ^1.4.15
express: ^4.17.1
passport-auth0: ^1.4.3
passport-oauth2: ^1.6.1
supertest: ^6.3.3
languageName: unknown
linkType: soft
"@backstage/plugin-auth-backend-module-aws-alb-provider@workspace:^, @backstage/plugin-auth-backend-module-aws-alb-provider@workspace:plugins/auth-backend-module-aws-alb-provider":
version: 0.0.0-use.local
resolution: "@backstage/plugin-auth-backend-module-aws-alb-provider@workspace:plugins/auth-backend-module-aws-alb-provider"
@@ -5118,6 +5137,7 @@ __metadata:
"@backstage/config": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/plugin-auth-backend-module-atlassian-provider": "workspace:^"
"@backstage/plugin-auth-backend-module-auth0-provider": "workspace:^"
"@backstage/plugin-auth-backend-module-aws-alb-provider": "workspace:^"
"@backstage/plugin-auth-backend-module-azure-easyauth-provider": "workspace:^"
"@backstage/plugin-auth-backend-module-bitbucket-provider": "workspace:^"