From 2f81cce5f9a44269af9d0af93dd7b896870cb5fe Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Wed, 11 Oct 2023 15:32:24 -0500 Subject: [PATCH 1/7] feat(showcase): add conditional rendering for login page Signed-off-by: Paul Schultz --- packages/app/src/App.tsx | 46 +----- packages/app/src/api/AuthApiRefs.ts | 29 ++++ .../api/JanusBackstageCustomizeApiClient.ts | 69 ++++++++ packages/app/src/api/index.ts | 71 +------- packages/app/src/apis.ts | 48 +++++- .../src/components/SignInPage/SignInPage.tsx | 155 ++++++++++++++++++ packages/app/src/lib/CustomTechRadar.ts | 2 +- 7 files changed, 310 insertions(+), 110 deletions(-) create mode 100644 packages/app/src/api/AuthApiRefs.ts create mode 100644 packages/app/src/api/JanusBackstageCustomizeApiClient.ts create mode 100644 packages/app/src/components/SignInPage/SignInPage.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 8bc175862..6ef5ee898 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -1,11 +1,6 @@ import { createApp } from '@backstage/app-defaults'; import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; -import { - AlertDisplay, - OAuthRequestDialog, - ProxiedSignInPage, - SignInPage, -} from '@backstage/core-components'; +import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; import { ApiExplorerPage, apiDocsPlugin } from '@backstage/plugin-api-docs'; import { CatalogEntityPage, @@ -19,6 +14,7 @@ import { catalogImportPlugin, } from '@backstage/plugin-catalog-import'; import { HomepageCompositionRoot } from '@backstage/plugin-home'; +import { LighthousePage } from '@backstage/plugin-lighthouse'; import { orgPlugin } from '@backstage/plugin-org'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder'; @@ -33,26 +29,21 @@ import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; import { UserSettingsPage } from '@backstage/plugin-user-settings'; import { UnifiedThemeProvider } from '@backstage/theme'; -import LightIcon from '@mui/icons-material/WbSunny'; -import DarkIcon from '@mui/icons-material/Brightness2'; import { OcmPage } from '@janus-idp/backstage-plugin-ocm'; +import DarkIcon from '@mui/icons-material/Brightness2'; +import LightIcon from '@mui/icons-material/WbSunny'; import React from 'react'; import { Route } from 'react-router-dom'; import { apis } from './apis'; import { Root } from './components/Root'; +import { SignInPage } from './components/SignInPage/SignInPage'; import { entityPage } from './components/catalog/EntityPage'; import { HomePage } from './components/home/HomePage'; import { LearningPaths } from './components/learningPaths/LearningPathsPage'; import { SearchPage } from './components/search/SearchPage'; -import { LighthousePage } from '@backstage/plugin-lighthouse'; -import { - configApiRef, - githubAuthApiRef, - useApi, -} from '@backstage/core-plugin-api'; -import { customLightTheme } from './themes/lightTheme'; -import { customDarkTheme } from './themes/darkTheme'; import { useUpdateTheme } from './hooks/useUpdateTheme'; +import { customDarkTheme } from './themes/darkTheme'; +import { customLightTheme } from './themes/lightTheme'; const app = createApp({ apis, @@ -106,28 +97,7 @@ const app = createApp({ }, ], components: { - SignInPage: props => { - const configApi = useApi(configApiRef); - if (configApi.getString('auth.environment') === 'development') { - return ( - - ); - } - return ; - }, + SignInPage: props => , }, }); diff --git a/packages/app/src/api/AuthApiRefs.ts b/packages/app/src/api/AuthApiRefs.ts new file mode 100644 index 000000000..14f348efb --- /dev/null +++ b/packages/app/src/api/AuthApiRefs.ts @@ -0,0 +1,29 @@ +import { + createApiRef, + type ApiRef, + type BackstageIdentityApi, + type OAuthApi, + type OpenIdConnectApi, + type ProfileInfoApi, + type SessionApi, +} from '@backstage/core-plugin-api'; + +export const oidcAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +> = createApiRef({ + id: 'internal.auth.oidc', +}); + +export const auth0AuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +> = createApiRef({ + id: 'internal.auth.auth0', +}); diff --git a/packages/app/src/api/JanusBackstageCustomizeApiClient.ts b/packages/app/src/api/JanusBackstageCustomizeApiClient.ts new file mode 100644 index 000000000..7188e70f7 --- /dev/null +++ b/packages/app/src/api/JanusBackstageCustomizeApiClient.ts @@ -0,0 +1,69 @@ +import { + ConfigApi, + createApiRef, + DiscoveryApi, +} from '@backstage/core-plugin-api'; +import { TechRadarLoaderResponse } from '@backstage/plugin-tech-radar'; +import { QuickAccessLinks } from '../types/types'; + +const DEFAULT_PROXY_PATH = '/developer-hub'; + +export interface JanusBackstageCustomizeApi { + getHomeDataJson(): Promise; + getTechRadarDataJson(): Promise; +} + +export const janusBackstageCustomizeApiRef = + createApiRef({ + id: 'app.developer-hub.service', + }); + +export type Options = { + discoveryApi: DiscoveryApi; + configApi: ConfigApi; +}; + +export class JanusBackstageCustomizeApiClient + implements JanusBackstageCustomizeApi +{ + // @ts-ignore + private readonly discoveryApi: DiscoveryApi; + + private readonly configApi: ConfigApi; + + constructor(options: Options) { + this.discoveryApi = options.discoveryApi; + this.configApi = options.configApi; + } + + private async getBaseUrl() { + const proxyPath = + this.configApi.getOptionalString('developerHub.proxyPath') || + DEFAULT_PROXY_PATH; + return `${await this.discoveryApi.getBaseUrl('proxy')}${proxyPath}`; + } + + private async fetcher(url: string) { + const response = await fetch(url, { + headers: { 'Content-Type': 'application/json' }, + }); + if (!response.ok) { + throw new Error( + `failed to fetch data, status ${response.status}: ${response.statusText}`, + ); + } + return await response.json(); + } + + async getHomeDataJson() { + const proxyUrl = await this.getBaseUrl(); + const data = await this.fetcher(`${proxyUrl}`); + return data; + } + + async getTechRadarDataJson() { + const proxyUrl = await this.getBaseUrl(); + const data = await this.fetcher(`${proxyUrl}/tech-radar`); + return data; + } +} diff --git a/packages/app/src/api/index.ts b/packages/app/src/api/index.ts index 7188e70f7..5aac7be26 100644 --- a/packages/app/src/api/index.ts +++ b/packages/app/src/api/index.ts @@ -1,69 +1,2 @@ -import { - ConfigApi, - createApiRef, - DiscoveryApi, -} from '@backstage/core-plugin-api'; -import { TechRadarLoaderResponse } from '@backstage/plugin-tech-radar'; -import { QuickAccessLinks } from '../types/types'; - -const DEFAULT_PROXY_PATH = '/developer-hub'; - -export interface JanusBackstageCustomizeApi { - getHomeDataJson(): Promise; - getTechRadarDataJson(): Promise; -} - -export const janusBackstageCustomizeApiRef = - createApiRef({ - id: 'app.developer-hub.service', - }); - -export type Options = { - discoveryApi: DiscoveryApi; - configApi: ConfigApi; -}; - -export class JanusBackstageCustomizeApiClient - implements JanusBackstageCustomizeApi -{ - // @ts-ignore - private readonly discoveryApi: DiscoveryApi; - - private readonly configApi: ConfigApi; - - constructor(options: Options) { - this.discoveryApi = options.discoveryApi; - this.configApi = options.configApi; - } - - private async getBaseUrl() { - const proxyPath = - this.configApi.getOptionalString('developerHub.proxyPath') || - DEFAULT_PROXY_PATH; - return `${await this.discoveryApi.getBaseUrl('proxy')}${proxyPath}`; - } - - private async fetcher(url: string) { - const response = await fetch(url, { - headers: { 'Content-Type': 'application/json' }, - }); - if (!response.ok) { - throw new Error( - `failed to fetch data, status ${response.status}: ${response.statusText}`, - ); - } - return await response.json(); - } - - async getHomeDataJson() { - const proxyUrl = await this.getBaseUrl(); - const data = await this.fetcher(`${proxyUrl}`); - return data; - } - - async getTechRadarDataJson() { - const proxyUrl = await this.getBaseUrl(); - const data = await this.fetcher(`${proxyUrl}/tech-radar`); - return data; - } -} +export * from './AuthApiRefs'; +export * from './JanusBackstageCustomizeApiClient'; diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 8e0fbfef4..2ff502ad0 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -1,3 +1,4 @@ +import { OAuth2 } from '@backstage/core-app-api'; import { AnyApiFactory, analyticsApiRef, @@ -5,6 +6,7 @@ import { createApiFactory, discoveryApiRef, identityApiRef, + oauthRequestApiRef, } from '@backstage/core-plugin-api'; import { ScmAuth, @@ -13,11 +15,12 @@ import { } from '@backstage/integration-react'; import { techRadarApiRef } from '@backstage/plugin-tech-radar'; import { SegmentAnalytics } from '@janus-idp/backstage-plugin-analytics-provider-segment'; -import { CustomTechRadar } from './lib/CustomTechRadar'; +import { auth0AuthApiRef, oidcAuthApiRef } from './api/AuthApiRefs'; import { JanusBackstageCustomizeApiClient, janusBackstageCustomizeApiRef, -} from './api'; +} from './api/JanusBackstageCustomizeApiClient'; +import { CustomTechRadar } from './lib/CustomTechRadar'; export const apis: AnyApiFactory[] = [ createApiFactory({ @@ -49,4 +52,45 @@ export const apis: AnyApiFactory[] = [ factory: ({ janusBackstageCustomizeApi }) => new CustomTechRadar({ janusBackstageCustomizeApi }), }), + // OIDC + createApiFactory({ + api: oidcAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'oidc', + title: 'OIDC', + icon: () => null, + }, + environment: configApi.getOptionalString('auth.environment'), + }), + }), + // Auth0 + createApiFactory({ + api: auth0AuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'auth0', + title: 'Auth0', + icon: () => null, + }, + defaultScopes: ['openid', 'email', 'profile'], + environment: configApi.getOptionalString('auth.environment'), + }), + }), ]; diff --git a/packages/app/src/components/SignInPage/SignInPage.tsx b/packages/app/src/components/SignInPage/SignInPage.tsx new file mode 100644 index 000000000..22346e099 --- /dev/null +++ b/packages/app/src/components/SignInPage/SignInPage.tsx @@ -0,0 +1,155 @@ +import React from 'react'; + +import { + atlassianAuthApiRef, + bitbucketAuthApiRef, + bitbucketServerAuthApiRef, + configApiRef, + githubAuthApiRef, + gitlabAuthApiRef, + googleAuthApiRef, + microsoftAuthApiRef, + oktaAuthApiRef, + oneloginAuthApiRef, + useApi, + type SignInPageProps, +} from '@backstage/core-plugin-api'; + +import { + SignInPage as CCSignInPage, + ProxiedSignInPage, + type SignInProviderConfig, +} from '@backstage/core-components'; +import { auth0AuthApiRef, oidcAuthApiRef } from '../../api'; + +const PROVIDERS = new Map([ + [ + 'auth0', + { + id: 'auth0-auth-provider', + title: 'Auth0', + message: 'Sign in using Auth0', + apiRef: auth0AuthApiRef, + }, + ], + [ + 'atlassian', + { + id: 'atlassian-auth-provider', + title: 'Atlassian', + message: 'Sign in using Atlassian', + apiRef: atlassianAuthApiRef, + }, + ], + [ + 'microsoft', + { + id: 'microsoft-auth-provider', + title: 'Microsoft', + message: 'Sign in using Microsoft', + apiRef: microsoftAuthApiRef, + }, + ], + ['azure-easyauth', 'azure-easyauth'], + [ + 'bitbucket', + { + id: 'bitbucket-auth-provider', + title: 'Bitbucket', + message: 'Sign in using Bitbucket', + apiRef: bitbucketAuthApiRef, + }, + ], + [ + 'bitbucketServer', + { + id: 'bitbucket-server-auth-provider', + title: 'Bitbucket Server', + message: 'Sign in using Bitbucket Server', + apiRef: bitbucketServerAuthApiRef, + }, + ], + ['cfaccess', 'cfaccess'], + [ + 'github', + { + id: 'github-auth-provider', + title: 'GitHub', + message: 'Sign in using GitHub', + apiRef: githubAuthApiRef, + }, + ], + [ + 'gitlab', + { + id: 'gitlab-auth-provider', + title: 'GitLab', + message: 'Sign in using GitLab', + apiRef: gitlabAuthApiRef, + }, + ], + [ + 'google', + { + id: 'google-auth-provider', + title: 'Google', + message: 'Sign in using Google', + apiRef: googleAuthApiRef, + }, + ], + ['gcp-iap', 'gcp-iap'], + [ + 'oidc', + { + id: 'oidc-auth-provider', + title: 'Auth0', + message: 'Sign in using OIDC', + apiRef: oidcAuthApiRef, + }, + ], + [ + 'okta', + { + id: 'okta-auth-provider', + title: 'Okta', + message: 'Sign in using Okta', + apiRef: oktaAuthApiRef, + }, + ], + ['oauth2Proxy', 'oauth2Proxy'], + [ + 'onelogin', + { + id: 'onelogin-auth-provider', + title: 'OneLogin', + message: 'Sign in using OneLogin', + apiRef: oneloginAuthApiRef, + }, + ], +]); + +export function SignInPage(props: SignInPageProps): React.JSX.Element { + const configApi = useApi(configApiRef); + const isDevEnv = configApi.getString('auth.environment') === 'development'; + const provider = + [...PROVIDERS.keys()].find(key => configApi.has(`auth.providers.${key}`)) ?? + 'github'; + const providerConfig = PROVIDERS.get(provider)!; + + if (typeof providerConfig === 'object') { + const providers = isDevEnv + ? (['guest', providerConfig] satisfies ['guest', SignInProviderConfig]) + : [providerConfig]; + + return ( + + ); + } + + return ; +} diff --git a/packages/app/src/lib/CustomTechRadar.ts b/packages/app/src/lib/CustomTechRadar.ts index a6ee7af69..c2f1795bd 100644 --- a/packages/app/src/lib/CustomTechRadar.ts +++ b/packages/app/src/lib/CustomTechRadar.ts @@ -2,7 +2,7 @@ import { TechRadarApi, type TechRadarLoaderResponse, } from '@backstage/plugin-tech-radar'; -import { JanusBackstageCustomizeApi } from '../api'; +import { JanusBackstageCustomizeApi } from '../api/JanusBackstageCustomizeApiClient'; export class CustomTechRadar implements TechRadarApi { private readonly janusBackstageCustomizeApi: JanusBackstageCustomizeApi; From 8d0806c5da305f6f973c6df5e1f470944ad48d2f Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Mon, 16 Oct 2023 15:22:33 -0500 Subject: [PATCH 2/7] update custom data api class name Signed-off-by: Paul Schultz --- ...stomizeApiClient.ts => CustomDataApiClient.ts} | 15 +++++---------- packages/app/src/api/index.ts | 2 +- packages/app/src/apis.ts | 15 +++++++-------- packages/app/src/hooks/useQuickAccess.ts | 6 +++--- packages/app/src/lib/CustomTechRadar.ts | 12 +++++------- 5 files changed, 21 insertions(+), 29 deletions(-) rename packages/app/src/api/{JanusBackstageCustomizeApiClient.ts => CustomDataApiClient.ts} (84%) diff --git a/packages/app/src/api/JanusBackstageCustomizeApiClient.ts b/packages/app/src/api/CustomDataApiClient.ts similarity index 84% rename from packages/app/src/api/JanusBackstageCustomizeApiClient.ts rename to packages/app/src/api/CustomDataApiClient.ts index 7188e70f7..2bcb68bfe 100644 --- a/packages/app/src/api/JanusBackstageCustomizeApiClient.ts +++ b/packages/app/src/api/CustomDataApiClient.ts @@ -8,27 +8,22 @@ import { QuickAccessLinks } from '../types/types'; const DEFAULT_PROXY_PATH = '/developer-hub'; -export interface JanusBackstageCustomizeApi { +export interface CustomDataApi { getHomeDataJson(): Promise; getTechRadarDataJson(): Promise; } -export const janusBackstageCustomizeApiRef = - createApiRef({ - id: 'app.developer-hub.service', - }); +export const customDataApiRef = createApiRef({ + id: 'app.developer-hub.service', +}); export type Options = { discoveryApi: DiscoveryApi; configApi: ConfigApi; }; -export class JanusBackstageCustomizeApiClient - implements JanusBackstageCustomizeApi -{ - // @ts-ignore +export class CustomDataApiClient implements CustomDataApi { private readonly discoveryApi: DiscoveryApi; - private readonly configApi: ConfigApi; constructor(options: Options) { diff --git a/packages/app/src/api/index.ts b/packages/app/src/api/index.ts index 5aac7be26..0d15aceb9 100644 --- a/packages/app/src/api/index.ts +++ b/packages/app/src/api/index.ts @@ -1,2 +1,2 @@ export * from './AuthApiRefs'; -export * from './JanusBackstageCustomizeApiClient'; +export * from './CustomDataApiClient'; diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 2ff502ad0..0d96f121f 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -17,9 +17,9 @@ import { techRadarApiRef } from '@backstage/plugin-tech-radar'; import { SegmentAnalytics } from '@janus-idp/backstage-plugin-analytics-provider-segment'; import { auth0AuthApiRef, oidcAuthApiRef } from './api/AuthApiRefs'; import { - JanusBackstageCustomizeApiClient, - janusBackstageCustomizeApiRef, -} from './api/JanusBackstageCustomizeApiClient'; + CustomDataApiClient, + customDataApiRef, +} from './api/CustomDataApiClient'; import { CustomTechRadar } from './lib/CustomTechRadar'; export const apis: AnyApiFactory[] = [ @@ -36,21 +36,20 @@ export const apis: AnyApiFactory[] = [ SegmentAnalytics.fromConfig(configApi, identityApi), }), createApiFactory({ - api: janusBackstageCustomizeApiRef, + api: customDataApiRef, deps: { discoveryApi: discoveryApiRef, configApi: configApiRef, }, factory: ({ discoveryApi, configApi }) => - new JanusBackstageCustomizeApiClient({ discoveryApi, configApi }), + new CustomDataApiClient({ discoveryApi, configApi }), }), createApiFactory({ api: techRadarApiRef, deps: { - janusBackstageCustomizeApi: janusBackstageCustomizeApiRef, + customDataApi: customDataApiRef, }, - factory: ({ janusBackstageCustomizeApi }) => - new CustomTechRadar({ janusBackstageCustomizeApi }), + factory: ({ customDataApi }) => new CustomTechRadar({ customDataApi }), }), // OIDC createApiFactory({ diff --git a/packages/app/src/hooks/useQuickAccess.ts b/packages/app/src/hooks/useQuickAccess.ts index d905afd56..61a6cd5eb 100644 --- a/packages/app/src/hooks/useQuickAccess.ts +++ b/packages/app/src/hooks/useQuickAccess.ts @@ -1,14 +1,14 @@ -import { useState, useCallback, useEffect } from 'react'; import { useApi } from '@backstage/core-plugin-api'; -import { janusBackstageCustomizeApiRef } from '../api'; +import { useCallback, useEffect, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; +import { customDataApiRef } from '../api'; import { QuickAccessLinks } from '../types/types'; export const useQuickAccess = () => { const [isLoading, setIsLoading] = useState(true); const [data, setData] = useState(); const [error, setError] = useState(); - const client = useApi(janusBackstageCustomizeApiRef); + const client = useApi(customDataApiRef); const { value, error: apiError, diff --git a/packages/app/src/lib/CustomTechRadar.ts b/packages/app/src/lib/CustomTechRadar.ts index c2f1795bd..19421de2a 100644 --- a/packages/app/src/lib/CustomTechRadar.ts +++ b/packages/app/src/lib/CustomTechRadar.ts @@ -2,19 +2,17 @@ import { TechRadarApi, type TechRadarLoaderResponse, } from '@backstage/plugin-tech-radar'; -import { JanusBackstageCustomizeApi } from '../api/JanusBackstageCustomizeApiClient'; +import { CustomDataApi } from '../api/CustomDataApiClient'; export class CustomTechRadar implements TechRadarApi { - private readonly janusBackstageCustomizeApi: JanusBackstageCustomizeApi; - constructor(options: { - janusBackstageCustomizeApi: JanusBackstageCustomizeApi; - }) { - this.janusBackstageCustomizeApi = options.janusBackstageCustomizeApi; + private readonly customDataApi: CustomDataApi; + constructor(options: { customDataApi: CustomDataApi }) { + this.customDataApi = options.customDataApi; } async load(id: string | undefined): Promise { let data; try { - data = await this.janusBackstageCustomizeApi.getTechRadarDataJson(); + data = await this.customDataApi.getTechRadarDataJson(); } catch (e) { const res = await fetch(`/tech-radar/data-${id}.json`); data = (await res.json()) as TechRadarLoaderResponse; From faaca32e0d1abf6da5b2a348a2f12c25e32f4a46 Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Mon, 16 Oct 2023 15:27:35 -0500 Subject: [PATCH 3/7] add changeset Signed-off-by: Paul Schultz --- .changeset/tiny-cougars-unite.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tiny-cougars-unite.md diff --git a/.changeset/tiny-cougars-unite.md b/.changeset/tiny-cougars-unite.md new file mode 100644 index 000000000..c556c3fc5 --- /dev/null +++ b/.changeset/tiny-cougars-unite.md @@ -0,0 +1,5 @@ +--- +'app': minor +--- + +Add authentication provider conditional rendering for login page From bea39fed567e065b7efd2daf852e359fb73aeb72 Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Mon, 23 Oct 2023 15:52:59 -0500 Subject: [PATCH 4/7] add saml support Signed-off-by: Paul Schultz --- packages/app/src/api/AuthApiRefs.ts | 26 +++++++++---------- packages/app/src/apis.ts | 26 ++++++++++++++++++- .../src/components/SignInPage/SignInPage.tsx | 19 +++++++++++++- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/packages/app/src/api/AuthApiRefs.ts b/packages/app/src/api/AuthApiRefs.ts index 14f348efb..e42a8da97 100644 --- a/packages/app/src/api/AuthApiRefs.ts +++ b/packages/app/src/api/AuthApiRefs.ts @@ -8,22 +8,20 @@ import { type SessionApi, } from '@backstage/core-plugin-api'; -export const oidcAuthApiRef: ApiRef< - OAuthApi & - OpenIdConnectApi & - ProfileInfoApi & - BackstageIdentityApi & - SessionApi -> = createApiRef({ +type CustomAuthApiRefType = OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi; + +export const oidcAuthApiRef: ApiRef = createApiRef({ id: 'internal.auth.oidc', }); -export const auth0AuthApiRef: ApiRef< - OAuthApi & - OpenIdConnectApi & - ProfileInfoApi & - BackstageIdentityApi & - SessionApi -> = createApiRef({ +export const auth0AuthApiRef: ApiRef = createApiRef({ id: 'internal.auth.auth0', }); + +export const samlAuthApiRef: ApiRef = createApiRef({ + id: 'internal.auth.saml', +}); diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 0d96f121f..340672cae 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -15,7 +15,11 @@ import { } from '@backstage/integration-react'; import { techRadarApiRef } from '@backstage/plugin-tech-radar'; import { SegmentAnalytics } from '@janus-idp/backstage-plugin-analytics-provider-segment'; -import { auth0AuthApiRef, oidcAuthApiRef } from './api/AuthApiRefs'; +import { + auth0AuthApiRef, + oidcAuthApiRef, + samlAuthApiRef, +} from './api/AuthApiRefs'; import { CustomDataApiClient, customDataApiRef, @@ -92,4 +96,24 @@ export const apis: AnyApiFactory[] = [ environment: configApi.getOptionalString('auth.environment'), }), }), + // SAML + createApiFactory({ + api: samlAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'saml', + title: 'SAML', + icon: () => null, + }, + environment: configApi.getOptionalString('auth.environment'), + }), + }), ]; diff --git a/packages/app/src/components/SignInPage/SignInPage.tsx b/packages/app/src/components/SignInPage/SignInPage.tsx index 22346e099..c1207e8da 100644 --- a/packages/app/src/components/SignInPage/SignInPage.tsx +++ b/packages/app/src/components/SignInPage/SignInPage.tsx @@ -20,8 +20,16 @@ import { ProxiedSignInPage, type SignInProviderConfig, } from '@backstage/core-components'; -import { auth0AuthApiRef, oidcAuthApiRef } from '../../api'; +import { auth0AuthApiRef, oidcAuthApiRef, samlAuthApiRef } from '../../api'; +/** + * Key: + * string - Provider name. + * + * Value: + * SignInProviderConfig - Local sign-in provider configuration. + * string - Proxy sign-in provider configuration. + */ const PROVIDERS = new Map([ [ 'auth0', @@ -126,6 +134,15 @@ const PROVIDERS = new Map([ apiRef: oneloginAuthApiRef, }, ], + [ + 'saml', + { + id: 'saml-auth-provider', + title: 'SAML', + message: 'Sign in using SAML', + apiRef: samlAuthApiRef, + }, + ], ]); export function SignInPage(props: SignInPageProps): React.JSX.Element { From 1d48cedf9804ff80f27092039c5cef486364b52f Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Mon, 6 Nov 2023 14:21:14 -0600 Subject: [PATCH 5/7] fix auth issue Signed-off-by: Paul Schultz --- .../src/components/SignInPage/SignInPage.tsx | 8 ++-- showcase-docs/getting-started.md | 38 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/packages/app/src/components/SignInPage/SignInPage.tsx b/packages/app/src/components/SignInPage/SignInPage.tsx index c1207e8da..c28a3a7d7 100644 --- a/packages/app/src/components/SignInPage/SignInPage.tsx +++ b/packages/app/src/components/SignInPage/SignInPage.tsx @@ -22,6 +22,8 @@ import { } from '@backstage/core-components'; import { auth0AuthApiRef, oidcAuthApiRef, samlAuthApiRef } from '../../api'; +const DEFAULT_PROVIDER = 'github'; + /** * Key: * string - Provider name. @@ -149,9 +151,9 @@ export function SignInPage(props: SignInPageProps): React.JSX.Element { const configApi = useApi(configApiRef); const isDevEnv = configApi.getString('auth.environment') === 'development'; const provider = - [...PROVIDERS.keys()].find(key => configApi.has(`auth.providers.${key}`)) ?? - 'github'; - const providerConfig = PROVIDERS.get(provider)!; + configApi.getOptionalString('signInPage') ?? DEFAULT_PROVIDER; + const providerConfig = + PROVIDERS.get(provider) ?? PROVIDERS.get(DEFAULT_PROVIDER)!; if (typeof providerConfig === 'object') { const providers = isDevEnv diff --git a/showcase-docs/getting-started.md b/showcase-docs/getting-started.md index aba515e8b..7cdb78035 100644 --- a/showcase-docs/getting-started.md +++ b/showcase-docs/getting-started.md @@ -132,20 +132,30 @@ The easiest and fastest method for getting started: Backstage Showcase app, runn - To enable authentication in the Showcase, add the [respective config](https://backstage.io/docs/auth/) in your `app-config`. The Showcase supports the following providers: - - Auth0 - - Atlassian - - Azure - - Azure Easy Auth - - Bitbucket - - Bitbucket Server - - Cloudflare Access - - GitHub - - GitLab - - Google - - Google IAP - - Okta - - OAuth 2 Custom Proxy - - OneLogin + - Auth0 - `auth0` + - Atlassian - `atlassian` + - Azure - `microsoft` + - Azure Easy Auth - `azure-easyauth` + - Bitbucket - `bitbucket` + - Bitbucket Server - `bitbucketServer` + - Cloudflare Access - `cfaccess` + - GitHub - `github` + - GitLab - `gitlab` + - Google - `google` + - Google IAP - `gcp-iap` + - OIDC - `oidc` + - Okta - `okta` + - OAuth 2 Custom Proxy - `oauth2Proxy` + - OneLogin - `onelogin` + - SAML - `saml` + + - Add the corresponding authentication provider key as the value to `signInPage` in your `app-config`. + + e.g. + + ```yaml + signInPage: oidc + ``` - Setup the Nexus Repository Manager plugin From 0ef97268ca75890483b2983a789d64014fb71f66 Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Tue, 7 Nov 2023 08:20:50 -0600 Subject: [PATCH 6/7] fix config Signed-off-by: Paul Schultz --- packages/app/config.d.ts | 5 +++++ showcase-docs/dynamic-plugins.md | 2 +- yarn.lock | 5 ----- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app/config.d.ts b/packages/app/config.d.ts index 5eb2bff9b..26f491a73 100644 --- a/packages/app/config.d.ts +++ b/packages/app/config.d.ts @@ -107,4 +107,9 @@ export interface Config { }; }; }; + /** + * The signInPage provider + * @visibility frontend + */ + signInPage?: string; } diff --git a/showcase-docs/dynamic-plugins.md b/showcase-docs/dynamic-plugins.md index d05d25c50..0c38c9833 100644 --- a/showcase-docs/dynamic-plugins.md +++ b/showcase-docs/dynamic-plugins.md @@ -520,7 +520,7 @@ Mount points are defined identifiers available across the applications. These po The following mount points are available: -| Mount point | Description | Visible even when no plugins are enabled | +| Mount point | Description | Visible even when no plugins are enabled | | ---------------------------- | ----------------------------------- | -------------------------------------------------------------- | | `entity.page.overview` | Catalog entity overview page | YES for all entities | | `entity.page.topology` | Catalog entity "Topology" tab | NO | diff --git a/yarn.lock b/yarn.lock index 38c76f88f..51054a237 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9708,11 +9708,6 @@ dependencies: "@types/node" "*" -"@types/geojson@*": - version "7946.0.12" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.12.tgz#0307536218d32e6b970bccd1d148b9c4e5b6f10d" - integrity sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA== - "@types/glob@*": version "8.1.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" From 1c6f9f7647b13bcf707cee427ea408c01338c100 Mon Sep 17 00:00:00 2001 From: Paul Schultz Date: Tue, 7 Nov 2023 15:08:56 -0600 Subject: [PATCH 7/7] update docs Signed-off-by: Paul Schultz --- showcase-docs/getting-started.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/showcase-docs/getting-started.md b/showcase-docs/getting-started.md index a8d536f58..36eea4268 100644 --- a/showcase-docs/getting-started.md +++ b/showcase-docs/getting-started.md @@ -157,6 +157,8 @@ The easiest and fastest method for getting started: Backstage Showcase app, runn signInPage: oidc ``` + - To disable the guest login set `auth.environment` to `production`. + - Setup the RBAC plugin - This [URL](https://github.com/janus-idp/backstage-plugins/tree/main/plugins/rbac-backend) explains how to use the RBAC Backend Plugin.