From dab4cf2814ea23aaf1d408a6c69c85d2bec1ec28 Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Thu, 18 Apr 2024 11:22:14 +0100 Subject: [PATCH 1/8] drafting onelogin auth backend module Signed-off-by: Matteo Silvestri --- .../.eslintrc.js | 1 + .../README.md | 8 ++ .../catalog-info.yaml | 10 +++ .../config.d.ts | 34 ++++++++ .../dev/index.ts | 24 ++++++ .../knip-report.md | 19 +++++ .../package.json | 49 ++++++++++++ .../src/authenticator.ts | 74 ++++++++++++++++++ .../src/index.ts | 25 ++++++ .../src/module.test.ts | 78 +++++++++++++++++++ .../src/module.ts | 48 ++++++++++++ .../src/resolvers.ts | 50 ++++++++++++ .../src}/types.d.ts | 5 ++ yarn.lock | 17 ++++ 14 files changed, 442 insertions(+) create mode 100644 plugins/auth-backend-module-onelogin-provider/.eslintrc.js create mode 100644 plugins/auth-backend-module-onelogin-provider/README.md create mode 100644 plugins/auth-backend-module-onelogin-provider/catalog-info.yaml create mode 100644 plugins/auth-backend-module-onelogin-provider/config.d.ts create mode 100644 plugins/auth-backend-module-onelogin-provider/dev/index.ts create mode 100644 plugins/auth-backend-module-onelogin-provider/knip-report.md create mode 100644 plugins/auth-backend-module-onelogin-provider/package.json create mode 100644 plugins/auth-backend-module-onelogin-provider/src/authenticator.ts create mode 100644 plugins/auth-backend-module-onelogin-provider/src/index.ts create mode 100644 plugins/auth-backend-module-onelogin-provider/src/module.test.ts create mode 100644 plugins/auth-backend-module-onelogin-provider/src/module.ts create mode 100644 plugins/auth-backend-module-onelogin-provider/src/resolvers.ts rename plugins/{auth-backend/src/providers/onelogin => auth-backend-module-onelogin-provider/src}/types.d.ts (81%) diff --git a/plugins/auth-backend-module-onelogin-provider/.eslintrc.js b/plugins/auth-backend-module-onelogin-provider/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/auth-backend-module-onelogin-provider/README.md b/plugins/auth-backend-module-onelogin-provider/README.md new file mode 100644 index 0000000000..2af119d25a --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/README.md @@ -0,0 +1,8 @@ +# Auth Module: OneLogin Provider + +This module provides an OneLogin auth provider implementation for `@backstage/plugin-auth-backend`. + +## Links + +- [Repository](https://github.com/backstage/backstage/tree/master/plugins/auth-backend-module-onelogin-provider) +- [Backstage Project Homepage](https://backstage.io) diff --git a/plugins/auth-backend-module-onelogin-provider/catalog-info.yaml b/plugins/auth-backend-module-onelogin-provider/catalog-info.yaml new file mode 100644 index 0000000000..6988215b59 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-auth-backend-module-onelogin-provider + title: '@backstage/plugin-auth-backend-module-onelogin-provider' + description: The onelogin-provider backend module for the auth plugin. +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers diff --git a/plugins/auth-backend-module-onelogin-provider/config.d.ts b/plugins/auth-backend-module-onelogin-provider/config.d.ts new file mode 100644 index 0000000000..d4f1457461 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/config.d.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2020 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 */ + onelogin?: { + [authEnv: string]: { + clientId: string; + /** + * @visibility secret + */ + clientSecret: string; + issuer: string; + callbackUrl?: string; + }; + }; + }; + }; +} diff --git a/plugins/auth-backend-module-onelogin-provider/dev/index.ts b/plugins/auth-backend-module-onelogin-provider/dev/index.ts new file mode 100644 index 0000000000..d3c18c1d48 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/dev/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2023 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'; + +const backend = createBackend(); + +backend.add(import('@backstage/plugin-auth-backend')); +backend.add(import('../src')); + +backend.start(); diff --git a/plugins/auth-backend-module-onelogin-provider/knip-report.md b/plugins/auth-backend-module-onelogin-provider/knip-report.md new file mode 100644 index 0000000000..a9ad7e156a --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/knip-report.md @@ -0,0 +1,19 @@ +# Knip report + +## Unused dependencies (4) + +| Name | Location | Severity | +| :---------------------------- | :----------- | :------- | +| @backstage/backend-plugin-api | package.json | error | +| @backstage/plugin-auth-node | package.json | error | +| passport | package.json | error | +| express | package.json | error | + +## Unused devDependencies (3) + +| Name | Location | Severity | +| :----------------------------- | :----------- | :------- | +| @backstage/plugin-auth-backend | package.json | error | +| @backstage/backend-test-utils | package.json | error | +| @backstage/backend-defaults | package.json | error | + diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json new file mode 100644 index 0000000000..d9185be128 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/package.json @@ -0,0 +1,49 @@ +{ + "name": "@backstage/plugin-auth-backend-module-onelogin-provider", + "version": "0.0.1", + "description": "The onelogin-provider backend module for the auth plugin.", + "backstage": { + "role": "backend-plugin-module" + }, + "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-onelogin-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:^", + "express": "^4.18.2", + "passport": "^0.7.0", + "passport-onelogin-oauth": "^0.0.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" +} diff --git a/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts b/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts new file mode 100644 index 0000000000..ab1d4c3238 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2023 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 { Strategy as OneLoginStrategy } from 'passport-onelogin-oauth'; +import { + createOAuthAuthenticator, + PassportOAuthAuthenticatorHelper, + PassportOAuthDoneCallback, + PassportProfile, +} from '@backstage/plugin-auth-node'; + +/** @public */ +export const oneLoginAuthenticator = createOAuthAuthenticator({ + defaultProfileTransform: + PassportOAuthAuthenticatorHelper.defaultProfileTransform, + initialize({ callbackUrl, config }) { + const clientId = config.getString('clientId'); + const clientSecret = config.getString('clientSecret'); + const issuer = config.getString('issuer'); + + return PassportOAuthAuthenticatorHelper.from( + new OneLoginStrategy( + { + clientID: clientId, + clientSecret: clientSecret, + callbackURL: callbackUrl, + issuer, + passReqToCallback: false, + }, + ( + accessToken: string, + refreshToken: string, + params: any, + fullProfile: PassportProfile, + done: PassportOAuthDoneCallback, + ) => { + done( + undefined, + { fullProfile, params, accessToken }, + { refreshToken }, + ); + }, + ), + ); + }, + + async start(input, helper) { + return helper.start(input, { + accessType: 'offline', + prompt: 'consent', + }); + }, + + async authenticate(input, helper) { + return helper.authenticate(input); + }, + + async refresh(input, helper) { + return helper.refresh(input); + }, +}); diff --git a/plugins/auth-backend-module-onelogin-provider/src/index.ts b/plugins/auth-backend-module-onelogin-provider/src/index.ts new file mode 100644 index 0000000000..82855d11a0 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/src/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2023 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 onelogin-provider backend module for the auth plugin. + * + * @packageDocumentation + */ + +export { oneLoginAuthenticator } from './authenticator'; +export { authModuleOneLoginProvider as default } from './module'; +export { oneLoginSignInResolvers } from './resolvers'; diff --git a/plugins/auth-backend-module-onelogin-provider/src/module.test.ts b/plugins/auth-backend-module-onelogin-provider/src/module.test.ts new file mode 100644 index 0000000000..56465d33f4 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/src/module.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2023 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 { authModuleOneLoginProvider } from './module'; +import request from 'supertest'; +import { decodeOAuthState } from '@backstage/plugin-auth-node'; + +describe('authModuleOneLoginProvider', () => { + it('should start', async () => { + const { server } = await startTestBackend({ + features: [ + import('@backstage/plugin-auth-backend'), + authModuleOneLoginProvider, + mockServices.rootConfig.factory({ + data: { + app: { + baseUrl: 'http://localhost:3000', + }, + auth: { + providers: { + onelogin: { + development: { + clientId: 'my-client-id', + clientSecret: 'my-client-secret', + issuer: 'https://my-company.onelogin.com/oidc/2', + }, + }, + }, + }, + }, + }), + ], + }); + + const agent = request.agent(server); + + const res = await agent.get('/api/auth/onelogin/start?env=development'); + + expect(res.status).toEqual(302); + + const nonceCookie = agent.jar.getCookie('onelogin-nonce', { + domain: 'localhost', + path: '/api/auth/onelogin/handler', + script: false, + secure: false, + }); + expect(nonceCookie).toBeDefined(); + + const startUrl = new URL(res.get('location')); + expect(startUrl.origin).toBe('https://my-company.onelogin.com'); + expect(startUrl.pathname).toBe('/oidc/2/auth'); + expect(Object.fromEntries(startUrl.searchParams)).toEqual({ + response_type: 'code', + client_id: 'my-client-id', + redirect_uri: `http://localhost:${server.port()}/api/auth/onelogin/handler/frame`, + state: expect.any(String), + }); + + expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({ + env: 'development', + nonce: decodeURIComponent(nonceCookie.value), + }); + }); +}); diff --git a/plugins/auth-backend-module-onelogin-provider/src/module.ts b/plugins/auth-backend-module-onelogin-provider/src/module.ts new file mode 100644 index 0000000000..7d67424152 --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/src/module.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2023 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 { oneLoginAuthenticator } from './authenticator'; +import { oneLoginSignInResolvers } from './resolvers'; + +/** @public */ +export const authModuleOneLoginProvider = createBackendModule({ + pluginId: 'auth', + moduleId: 'onelogin-provider', + register(reg) { + reg.registerInit({ + deps: { + providers: authProvidersExtensionPoint, + }, + async init({ providers }) { + providers.registerProvider({ + providerId: 'onelogin', + factory: createOAuthProviderFactory({ + authenticator: oneLoginAuthenticator, + signInResolverFactories: { + ...oneLoginSignInResolvers, + ...commonSignInResolvers, + }, + }), + }); + }, + }); + }, +}); diff --git a/plugins/auth-backend-module-onelogin-provider/src/resolvers.ts b/plugins/auth-backend-module-onelogin-provider/src/resolvers.ts new file mode 100644 index 0000000000..93bfd758cb --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/src/resolvers.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2023 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 { + createSignInResolverFactory, + OAuthAuthenticatorResult, + PassportProfile, + SignInInfo, +} from '@backstage/plugin-auth-node'; + +/** + * Available sign-in resolvers for the OneLogin auth provider. + * + * @public + */ +export namespace oneLoginSignInResolvers { + /** + * Looks up the user by matching their OneLogin username to the entity name. + */ + export const usernameMatchingUserEntityName = createSignInResolverFactory({ + create() { + return async ( + info: SignInInfo>, + ctx, + ) => { + const { result } = info; + + const id = result.fullProfile.username; + if (!id) { + throw new Error(`OneLogin user profile does not contain a username`); + } + + return ctx.signInWithCatalogUser({ entityRef: { name: id } }); + }; + }, + }); +} diff --git a/plugins/auth-backend/src/providers/onelogin/types.d.ts b/plugins/auth-backend-module-onelogin-provider/src/types.d.ts similarity index 81% rename from plugins/auth-backend/src/providers/onelogin/types.d.ts rename to plugins/auth-backend-module-onelogin-provider/src/types.d.ts index 6df10388af..9a05c4f2ec 100644 --- a/plugins/auth-backend/src/providers/onelogin/types.d.ts +++ b/plugins/auth-backend-module-onelogin-provider/src/types.d.ts @@ -13,8 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + declare module 'passport-onelogin-oauth' { + import { Request } from 'express'; + import { StrategyCreated } from 'passport'; + export class Strategy { constructor(options: any, verify: any); + authenticate(this: StrategyCreated, req: Request, options?: any): any; } } diff --git a/yarn.lock b/yarn.lock index 7610418fa2..452796c641 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4688,6 +4688,23 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-auth-backend-module-onelogin-provider@workspace:plugins/auth-backend-module-onelogin-provider": + version: 0.0.0-use.local + resolution: "@backstage/plugin-auth-backend-module-onelogin-provider@workspace:plugins/auth-backend-module-onelogin-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:^" + express: ^4.18.2 + passport: ^0.7.0 + passport-onelogin-oauth: ^0.0.1 + supertest: ^6.3.3 + languageName: unknown + linkType: soft + "@backstage/plugin-auth-backend-module-pinniped-provider@workspace:plugins/auth-backend-module-pinniped-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-pinniped-provider@workspace:plugins/auth-backend-module-pinniped-provider" From b78caea0c6aa4747c3a1cfad0871508ba51d446b Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Fri, 19 Apr 2024 09:51:18 +0100 Subject: [PATCH 2/8] add api-report.md Signed-off-by: Matteo Silvestri --- .../api-report.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 plugins/auth-backend-module-onelogin-provider/api-report.md diff --git a/plugins/auth-backend-module-onelogin-provider/api-report.md b/plugins/auth-backend-module-onelogin-provider/api-report.md new file mode 100644 index 0000000000..8f94b27cab --- /dev/null +++ b/plugins/auth-backend-module-onelogin-provider/api-report.md @@ -0,0 +1,30 @@ +## API Report File for "@backstage/plugin-auth-backend-module-onelogin-provider" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; +import { OAuthAuthenticatorResult } from '@backstage/plugin-auth-node'; +import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; +import { PassportProfile } from '@backstage/plugin-auth-node'; +import { SignInResolverFactory } from '@backstage/plugin-auth-node'; + +// @public (undocumented) +const authModuleOneLoginProvider: () => BackendFeature; +export default authModuleOneLoginProvider; + +// @public (undocumented) +export const oneLoginAuthenticator: OAuthAuthenticator< + PassportOAuthAuthenticatorHelper, + PassportProfile +>; + +// @public +export namespace oneLoginSignInResolvers { + const usernameMatchingUserEntityName: SignInResolverFactory< + OAuthAuthenticatorResult, + unknown + >; +} +``` From 844f5a21bb6abb8e38296856128d067e9a6a4f0b Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Fri, 19 Apr 2024 13:05:14 +0100 Subject: [PATCH 3/8] fix onelogin auth Signed-off-by: Matteo Silvestri --- .../src/apis/implementations/auth/onelogin/OneLoginAuth.ts | 2 +- .../auth-backend-module-onelogin-provider/src/authenticator.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts b/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts index d8f54e7e50..582b5a8282 100644 --- a/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts @@ -76,7 +76,7 @@ export default class OneLoginAuth { oauthRequestApi, provider, environment, - defaultScopes: ['openid', 'email', 'profile', 'offline_access'], + defaultScopes: ['openid', 'email', 'profile'], scopeTransform(scopes) { return scopes.map(scope => { if (OIDC_SCOPES.has(scope)) { diff --git a/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts b/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts index ab1d4c3238..f6ac3f55d5 100644 --- a/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts @@ -58,6 +58,7 @@ export const oneLoginAuthenticator = createOAuthAuthenticator({ }, async start(input, helper) { + input.scope = 'openid email profile'; return helper.start(input, { accessType: 'offline', prompt: 'consent', From 0631fe2d283e807fad2d77d5e974ea3025caa2a5 Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Fri, 19 Apr 2024 13:28:33 +0100 Subject: [PATCH 4/8] fix tests Signed-off-by: Matteo Silvestri --- plugins/auth-backend-module-onelogin-provider/package.json | 2 +- .../auth-backend-module-onelogin-provider/src/module.test.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index d9185be128..7012742eb2 100644 --- a/plugins/auth-backend-module-onelogin-provider/package.json +++ b/plugins/auth-backend-module-onelogin-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-onelogin-provider", - "version": "0.0.1", + "version": "0.0.0", "description": "The onelogin-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module" diff --git a/plugins/auth-backend-module-onelogin-provider/src/module.test.ts b/plugins/auth-backend-module-onelogin-provider/src/module.test.ts index 56465d33f4..ed3fd4c477 100644 --- a/plugins/auth-backend-module-onelogin-provider/src/module.test.ts +++ b/plugins/auth-backend-module-onelogin-provider/src/module.test.ts @@ -65,6 +65,7 @@ describe('authModuleOneLoginProvider', () => { expect(startUrl.pathname).toBe('/oidc/2/auth'); expect(Object.fromEntries(startUrl.searchParams)).toEqual({ response_type: 'code', + scope: 'openid email profile', client_id: 'my-client-id', redirect_uri: `http://localhost:${server.port()}/api/auth/onelogin/handler/frame`, state: expect.any(String), From 566d7cb3cdf4accc2647bf53d229ef2035caf822 Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Mon, 13 May 2024 17:12:49 +0200 Subject: [PATCH 5/8] add changeset for onelogin-provider Signed-off-by: Matteo Silvestri --- .changeset/breezy-planets-sparkle.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/breezy-planets-sparkle.md diff --git a/.changeset/breezy-planets-sparkle.md b/.changeset/breezy-planets-sparkle.md new file mode 100644 index 0000000000..025becb2d6 --- /dev/null +++ b/.changeset/breezy-planets-sparkle.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-onelogin-provider': patch +--- + +Initial changeset to introduce onelogin provider From 3e1bb1567437fbcc11ed20ebdc1b514cc7c7dc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 21 May 2024 10:09:30 +0200 Subject: [PATCH 6/8] fixup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/breezy-planets-sparkle.md | 4 +- .changeset/wild-coats-doubt.md | 5 + .../config/vocabularies/Backstage/accept.txt | 1 + .../auth/onelogin/OneLoginAuth.ts | 2 +- .../src/authenticator.ts | 1 - plugins/auth-backend/package.json | 1 + .../src/providers/onelogin/provider.ts | 192 ++---------------- yarn.lock | 3 +- 8 files changed, 26 insertions(+), 183 deletions(-) create mode 100644 .changeset/wild-coats-doubt.md diff --git a/.changeset/breezy-planets-sparkle.md b/.changeset/breezy-planets-sparkle.md index 025becb2d6..eea9e5c6bf 100644 --- a/.changeset/breezy-planets-sparkle.md +++ b/.changeset/breezy-planets-sparkle.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-auth-backend-module-onelogin-provider': patch +'@backstage/plugin-auth-backend-module-onelogin-provider': minor --- -Initial changeset to introduce onelogin provider +Separate out the OneLogin provider into its own module diff --git a/.changeset/wild-coats-doubt.md b/.changeset/wild-coats-doubt.md new file mode 100644 index 0000000000..4e86911127 --- /dev/null +++ b/.changeset/wild-coats-doubt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Updated to use the new `@backstage/plugin-auth-backend-module-onelogin-provider` implementation diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index 77dd930a73..7322385e9b 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -268,6 +268,7 @@ Olausson Oldsberg onboarding Onboarding +onelogin OpenSearch OpenShift openssl diff --git a/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts b/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts index 582b5a8282..d8f54e7e50 100644 --- a/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts @@ -76,7 +76,7 @@ export default class OneLoginAuth { oauthRequestApi, provider, environment, - defaultScopes: ['openid', 'email', 'profile'], + defaultScopes: ['openid', 'email', 'profile', 'offline_access'], scopeTransform(scopes) { return scopes.map(scope => { if (OIDC_SCOPES.has(scope)) { diff --git a/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts b/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts index f6ac3f55d5..ab1d4c3238 100644 --- a/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts @@ -58,7 +58,6 @@ export const oneLoginAuthenticator = createOAuthAuthenticator({ }, async start(input, helper) { - input.scope = 'openid email profile'; return helper.start(input, { accessType: 'offline', prompt: 'consent', diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 0797145855..ffdaf53fb3 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -57,6 +57,7 @@ "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "workspace:^", "@backstage/plugin-auth-backend-module-oidc-provider": "workspace:^", "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^", + "@backstage/plugin-auth-backend-module-onelogin-provider": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", "@backstage/types": "workspace:^", diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts index 0360a91a1c..ab5995da4c 100644 --- a/plugins/auth-backend/src/providers/onelogin/provider.ts +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -14,156 +14,18 @@ * limitations under the License. */ -import { Strategy as OneLoginStrategy } from 'passport-onelogin-oauth'; -import express from 'express'; +import { oneLoginAuthenticator } from '@backstage/plugin-auth-backend-module-onelogin-provider'; import { - OAuthAdapter, - OAuthProviderOptions, - OAuthHandlers, - OAuthResponse, - OAuthEnvironmentHandler, - OAuthStartRequest, - encodeState, - OAuthRefreshRequest, - OAuthResult, -} from '../../lib/oauth'; -import passport from 'passport'; -import { - executeFrameHandlerStrategy, - executeRedirectStrategy, - executeRefreshTokenStrategy, - makeProfileInfo, - executeFetchUserProfileStrategy, - PassportDoneCallback, -} from '../../lib/passport'; -import { OAuthStartResponse, AuthHandler } from '../types'; -import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; -import { - AuthResolverContext, SignInResolver, + createOAuthProviderFactory, } from '@backstage/plugin-auth-node'; - -type PrivateInfo = { - refreshToken: string; -}; - -export type Options = OAuthProviderOptions & { - issuer: string; - signInResolver?: SignInResolver; - authHandler: AuthHandler; - resolverContext: AuthResolverContext; -}; - -export class OneLoginProvider implements OAuthHandlers { - private readonly _strategy: any; - private readonly signInResolver?: SignInResolver; - private readonly authHandler: AuthHandler; - private readonly resolverContext: AuthResolverContext; - - constructor(options: Options) { - this.signInResolver = options.signInResolver; - this.authHandler = options.authHandler; - this.resolverContext = options.resolverContext; - this._strategy = new OneLoginStrategy( - { - issuer: options.issuer, - clientID: options.clientId, - clientSecret: options.clientSecret, - callbackURL: options.callbackUrl, - passReqToCallback: false, - }, - ( - accessToken: any, - refreshToken: any, - params: any, - fullProfile: passport.Profile, - done: PassportDoneCallback, - ) => { - done( - undefined, - { - accessToken, - refreshToken, - params, - fullProfile, - }, - { - refreshToken, - }, - ); - }, - ); - } - async start(req: OAuthStartRequest): Promise { - return await executeRedirectStrategy(req, this._strategy, { - accessType: 'offline', - prompt: 'consent', - scope: 'openid', - state: encodeState(req.state), - }); - } - - async handler(req: express.Request) { - const { result, privateInfo } = await executeFrameHandlerStrategy< - OAuthResult, - PrivateInfo - >(req, this._strategy); - - return { - response: await this.handleResult(result), - refreshToken: privateInfo.refreshToken, - }; - } - - async refresh(req: OAuthRefreshRequest) { - const { accessToken, refreshToken, params } = - await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - 'openid', - ); - - 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; - } -} +import { + adaptLegacyOAuthHandler, + adaptLegacyOAuthSignInResolver, +} from '../../lib/legacy'; +import { OAuthResult } from '../../lib/oauth'; +import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; +import { AuthHandler } from '../types'; /** * Auth provider integration for OneLogin auth @@ -188,36 +50,10 @@ export const onelogin = createAuthProviderIntegration({ resolver: SignInResolver; }; }) { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const issuer = envConfig.getString('issuer'); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; - - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile, params.id_token), - }); - - const provider = new OneLoginProvider({ - clientId, - clientSecret, - callbackUrl, - issuer, - authHandler, - signInResolver: options?.signIn?.resolver, - resolverContext, - }); - - return OAuthAdapter.fromConfig(globalConfig, provider, { - providerId, - callbackUrl, - }); - }); + return createOAuthProviderFactory({ + authenticator: oneLoginAuthenticator, + profileTransform: adaptLegacyOAuthHandler(options?.authHandler), + signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver), + }); }, }); diff --git a/yarn.lock b/yarn.lock index 452796c641..33dbd33246 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4688,7 +4688,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-auth-backend-module-onelogin-provider@workspace:plugins/auth-backend-module-onelogin-provider": +"@backstage/plugin-auth-backend-module-onelogin-provider@workspace:^, @backstage/plugin-auth-backend-module-onelogin-provider@workspace:plugins/auth-backend-module-onelogin-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-onelogin-provider@workspace:plugins/auth-backend-module-onelogin-provider" dependencies: @@ -4776,6 +4776,7 @@ __metadata: "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "workspace:^" "@backstage/plugin-auth-backend-module-oidc-provider": "workspace:^" "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^" + "@backstage/plugin-auth-backend-module-onelogin-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" "@backstage/types": "workspace:^" From 3606cce5088194d5619d67a0f0fa592e7da830ec Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Tue, 4 Jun 2024 12:07:26 +0200 Subject: [PATCH 7/8] override onelogin start and refresh scope Signed-off-by: Matteo Silvestri --- .../auth-backend-module-onelogin-provider/src/authenticator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts b/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts index ab1d4c3238..7b40ad7473 100644 --- a/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-onelogin-provider/src/authenticator.ts @@ -58,6 +58,7 @@ export const oneLoginAuthenticator = createOAuthAuthenticator({ }, async start(input, helper) { + input.scope = 'openid'; return helper.start(input, { accessType: 'offline', prompt: 'consent', @@ -69,6 +70,7 @@ export const oneLoginAuthenticator = createOAuthAuthenticator({ }, async refresh(input, helper) { + input.scope = 'openid'; return helper.refresh(input); }, }); From 6e903fbf3f22a98a39c048499790edf5226d69cb Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Tue, 4 Jun 2024 12:21:02 +0200 Subject: [PATCH 8/8] fix tests Signed-off-by: Matteo Silvestri --- .../auth-backend-module-onelogin-provider/src/module.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auth-backend-module-onelogin-provider/src/module.test.ts b/plugins/auth-backend-module-onelogin-provider/src/module.test.ts index ed3fd4c477..c98dd7f71a 100644 --- a/plugins/auth-backend-module-onelogin-provider/src/module.test.ts +++ b/plugins/auth-backend-module-onelogin-provider/src/module.test.ts @@ -65,7 +65,7 @@ describe('authModuleOneLoginProvider', () => { expect(startUrl.pathname).toBe('/oidc/2/auth'); expect(Object.fromEntries(startUrl.searchParams)).toEqual({ response_type: 'code', - scope: 'openid email profile', + scope: 'openid', client_id: 'my-client-id', redirect_uri: `http://localhost:${server.port()}/api/auth/onelogin/handler/frame`, state: expect.any(String),