From 3d9de0164c2c93ddef1576dca0c4e59500902783 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Sun, 4 Apr 2021 16:35:05 +0100 Subject: [PATCH 01/12] tutorial: Using AWS Application Load Balancer with Azure Active Directory to authenticate requests Signed-off-by: Marco Crivellaro --- .../docs/tutorials/aws-alb-aad-oidc-auth.md | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 contrib/docs/tutorials/aws-alb-aad-oidc-auth.md diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md new file mode 100644 index 0000000000..9dab72fe66 --- /dev/null +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -0,0 +1,179 @@ +# Using AWS Application Load Balancer with Azure Active Directory to authenticate requests + +Backstage allows to offload the responsibility of authenticating users to AWS Application Loadbalancer (**ALB**), leveraging the authentication support on ALB. +This tutorial shows how to use authentication on an ALB sitting in front of Backstage. +Azure Active Directory (**AAD**) is used as identity provider but any identity provider supporting OpenID Connect (OIDC) can be used. + +It is assumed an ALB is already serving traffic in front of a Backstage instance configured to serve the frontend app from the backend. + +## Infrastructure setup + +### AAD App + +The AAD App is used to execute the authentication flow, serve and refresh the identity token. + +Create the AAD App following the steps outlined in `Create a Microsoft App Registration in Microsoft Portal` section from the tutorial [Monorepo App Setup With Authentication][monorepo-app-setup-with-auth]. + +Instead of `localhost` addresses, use the following values. + +- Identifier URI: `https://backstage.yourdomain.com` +- Redirect URI: `https://backstage.yourdomain.com/oauth2/idpresponse` + +`Application (client) Id`, `Directory (tenant) ID` and `client secret`values will be used while configuring the ALB. + +### ALB + +In AWS console configure ALB Authentication as described below: + +- Edit the ALB rule used to forward the traffic to Backstage and add a new `Authenticate` action. The action will have higher priority compared to the existing `Forward to`. +- Select `OIDC` under `Authenticate` +- Set `Issuer` to `https://login.microsoftonline.com/{TENANT_ID}/v2.0`, replacing `{TENANT_ID}` with the value of `Directory (tenant) ID` of the AAD App. +- Set `Authorization endpoint` to `https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/authorize`, replacing `{TENANT_ID}` with the value of `Directory (tenant) ID` of the AAD App. +- Set `Token endpoint` to `https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/authorize`, replacing `{TENANT_ID}` with the value of `Directory (tenant) ID` of the AAD App. +- Set `User info endpoint` to `https://graph.microsoft.com/oidc/userinfo` +- Set `Client ID` to the AAD App `Application (client) Id` +- Set `Client secret` to the AAD APP `client secret` + +Use the following advanced settings: + +- `Session cookie name` = `AWSELBAuthSessionCookie` +- `Session timeout` = `604800` seconds +- `Scope` = `openid profile offline_access` +- `Action on unauthenticated request` = `Autenticate (client reattempt)` + +Once you've saved the action, you should see an authentication flow be triggered against AAD when visiting Backstage address at `https://backstage.yourdomain.com`. The flow will not complete successfully as Backstage app isn't yet configured properly. + +## Backstage changes + +### Frontend + +The Backstage App needs a SingInPage when authentication is required. +When using ALB authentication Backstage will only be loaded once the user has successfully authenticated; we won't need to display a SignIn page, however we will need to create a dummy SignIn component that can refresh the token. + +- edit `packages/app/src/App.tsx` +- import the following two additional definitions from `@backstage/core`: `useApi`, `configApiRef`; these will be used to check wether backstage is running locally or behind an ALB +- add the following definition just before the app is created (`const app = createApp`): + +```ts +const DummySignInComponent: any = (props: any) => { + const config = useApi(configApiRef); + const shouldAuth = !!config.getOptionalConfig('auth.providers.awsalb'); + if (shouldAuth) { + fetch(`${window.location.origin}/api/auth/awsalb/refresh`) + .then(data => data.json()) + .then(data => { + props.onResult({ + userId: data.backstageIdentity.id, + profile: data.profile, + }); + }); + } else { + // when running locally we default user identity to `Local User` + props.onResult({ + userId: 'local user', + profile: { + email: 'local.user@yourdomain.com', + displayName: 'Local User', + picture: '', + }, + }); + } + + return
; +}; +``` + +- use `DummySingInComponent` as `SignInPage` by changing `createApp` statement as follow: + +```ts +const app = createApp({ + apis, + plugins: Object.values(plugins), + components: { + SignInPage: DummySignInComponent, + }, + bindRoutes({ bind }) { + bind(catalogPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + bind(apiDocsPlugin.externalRoutes, { + createComponent: scaffolderPlugin.routes.root, + }); + }, +}); +``` + +### Backend + +When using ALB auth it is not possible to leverage the built-in auth config discovery mechanism implemented in the app created by default: a bespoke logic needs to be implemented. + +- replace the content of `packages/backend/plugin/auth.ts` with the below + +```ts +import { + createRouter, + AuthResponse, + AuthProviderFactoryOptions, + defaultAuthProviderFactories, +} from '@backstage/plugin-auth-backend'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin({ + logger, + database, + config, + discovery, +}: PluginEnvironment) { + const identityResolver = (payload: any): Promise> => { + return Promise.resolve({ + providerInfo: {}, + profile: { + email: payload.email, + displayName: payload.name, + picture: payload.picture, + }, + backstageIdentity: { + id: payload.email, + }, + }); + }; + const providerFactories = { + awsalb: (options: AuthProviderFactoryOptions) => + defaultAuthProviderFactories.awsalb({ ...options, identityResolver }), + }; + return await createRouter({ + logger, + config, + database, + discovery, + providerFactories, + }); +} +``` + +### Configuration + +Use the following `auth` configuration when running Backstage on AWS: + +```yaml +auth: + providers: + awsalb: + issuer: + issuer: https://login.microsoftonline.com/{TENANT_ID}/v2.0 + region: { AWS_REGION } +``` + +Replace `{TENANT_ID}` with the value of `Directory (tenant) ID` of the AAD App and `{AWS_REGION}` with the AWS region identifier where the ALB is deployed (for example: `eu-central-1`). + +## Conclusion + +Once it's deployed, after going through the AAD authentication flow, Backstage should display the AAD user details. + +Special thanks to [@backjo][gh-backjo] for implementing the `auth-backend` provider for AWS ALB on [#4047][pr-4047] + + + +[monorepo-app-setup-with-auth-ms]: https://backstage.io/docs/tutorials/quickstart-app-auth#the-auth-configuration +[gh-backjo]: https://github.com/backjo +[pr-4047]: https://github.com/backstage/backstage/pull/4047 From 3074cfc0403d27fceb6904cc2e3610ff3c2e2e73 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Mon, 5 Apr 2021 09:05:44 +0100 Subject: [PATCH 02/12] fix syntax, remove conclusion thanking (everybody should be thanked) Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 9dab72fe66..5041e60149 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -1,6 +1,6 @@ # Using AWS Application Load Balancer with Azure Active Directory to authenticate requests -Backstage allows to offload the responsibility of authenticating users to AWS Application Loadbalancer (**ALB**), leveraging the authentication support on ALB. +Backstage allows to offload the responsibility of authenticating users to AWS Application Load Balancer (**ALB**), leveraging the authentication support on ALB. This tutorial shows how to use authentication on an ALB sitting in front of Backstage. Azure Active Directory (**AAD**) is used as identity provider but any identity provider supporting OpenID Connect (OIDC) can be used. @@ -170,10 +170,6 @@ Replace `{TENANT_ID}` with the value of `Directory (tenant) ID` of the AAD App a Once it's deployed, after going through the AAD authentication flow, Backstage should display the AAD user details. -Special thanks to [@backjo][gh-backjo] for implementing the `auth-backend` provider for AWS ALB on [#4047][pr-4047] - [monorepo-app-setup-with-auth-ms]: https://backstage.io/docs/tutorials/quickstart-app-auth#the-auth-configuration -[gh-backjo]: https://github.com/backjo -[pr-4047]: https://github.com/backstage/backstage/pull/4047 From ffc6b81ffa50cff0034b155d1c584237c8b5797e Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 23:44:57 +0200 Subject: [PATCH 03/12] Update contrib/docs/tutorials/aws-alb-aad-oidc-auth.md Co-authored-by: Tim Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 5041e60149..4a29ce6758 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -47,7 +47,7 @@ Once you've saved the action, you should see an authentication flow be triggered ### Frontend -The Backstage App needs a SingInPage when authentication is required. +The Backstage App needs a SignInPage when authentication is required. When using ALB authentication Backstage will only be loaded once the user has successfully authenticated; we won't need to display a SignIn page, however we will need to create a dummy SignIn component that can refresh the token. - edit `packages/app/src/App.tsx` From ee9ecc99b4eb7096de97e1a693f626984852b2f6 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 23:45:09 +0200 Subject: [PATCH 04/12] Update contrib/docs/tutorials/aws-alb-aad-oidc-auth.md Co-authored-by: Tim Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 4a29ce6758..7af2a12f5f 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -1,6 +1,6 @@ # Using AWS Application Load Balancer with Azure Active Directory to authenticate requests -Backstage allows to offload the responsibility of authenticating users to AWS Application Load Balancer (**ALB**), leveraging the authentication support on ALB. +Backstage allows offloading the responsibility of authenticating users to an AWS Application Load Balancer (**ALB**), leveraging the authentication support on ALB. This tutorial shows how to use authentication on an ALB sitting in front of Backstage. Azure Active Directory (**AAD**) is used as identity provider but any identity provider supporting OpenID Connect (OIDC) can be used. From 529c968413a16528b97571a4a580e2726c7cb4ed Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 23:45:23 +0200 Subject: [PATCH 05/12] Update contrib/docs/tutorials/aws-alb-aad-oidc-auth.md Co-authored-by: Tim Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 7af2a12f5f..6c1436cf09 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -23,7 +23,7 @@ Instead of `localhost` addresses, use the following values. ### ALB -In AWS console configure ALB Authentication as described below: +In the AWS console, configure ALB Authentication: - Edit the ALB rule used to forward the traffic to Backstage and add a new `Authenticate` action. The action will have higher priority compared to the existing `Forward to`. - Select `OIDC` under `Authenticate` From c3a659599917f9b7b6812e850ff865fa424d7e0e Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 23:46:06 +0200 Subject: [PATCH 06/12] Update contrib/docs/tutorials/aws-alb-aad-oidc-auth.md Co-authored-by: Tim Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 6c1436cf09..158368f72a 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -41,7 +41,7 @@ Use the following advanced settings: - `Scope` = `openid profile offline_access` - `Action on unauthenticated request` = `Autenticate (client reattempt)` -Once you've saved the action, you should see an authentication flow be triggered against AAD when visiting Backstage address at `https://backstage.yourdomain.com`. The flow will not complete successfully as Backstage app isn't yet configured properly. +Once you've saved the action, you should see an authentication flow be triggered against AAD when visiting Backstage address at `https://backstage.yourdomain.com`. The flow will not complete successfully as the Backstage app isn't yet configured properly. ## Backstage changes From 68717560ba2ce103c76340e6235e8d6f99d35298 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 23:46:19 +0200 Subject: [PATCH 07/12] Update contrib/docs/tutorials/aws-alb-aad-oidc-auth.md Co-authored-by: Tim Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 158368f72a..ebaf8cb3ba 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -51,7 +51,7 @@ The Backstage App needs a SignInPage when authentication is required. When using ALB authentication Backstage will only be loaded once the user has successfully authenticated; we won't need to display a SignIn page, however we will need to create a dummy SignIn component that can refresh the token. - edit `packages/app/src/App.tsx` -- import the following two additional definitions from `@backstage/core`: `useApi`, `configApiRef`; these will be used to check wether backstage is running locally or behind an ALB +- import the following two additional definitions from `@backstage/core`: `useApi`, `configApiRef`; these will be used to check whether Backstage is running locally or behind an ALB - add the following definition just before the app is created (`const app = createApp`): ```ts From c59c13190561385326971962b41a878ee171430b Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 23:46:28 +0200 Subject: [PATCH 08/12] Update contrib/docs/tutorials/aws-alb-aad-oidc-auth.md Co-authored-by: Tim Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index ebaf8cb3ba..a36eb18833 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -83,7 +83,7 @@ const DummySignInComponent: any = (props: any) => { }; ``` -- use `DummySingInComponent` as `SignInPage` by changing `createApp` statement as follow: +- use `DummySignInComponent` as `SignInPage` by changing the `createApp` call: ```ts const app = createApp({ From 3eb5825045472f5e98a8825620a192e37800e035 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 23:46:44 +0200 Subject: [PATCH 09/12] Update contrib/docs/tutorials/aws-alb-aad-oidc-auth.md Co-authored-by: Tim Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index a36eb18833..4eb52aa53c 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -105,7 +105,7 @@ const app = createApp({ ### Backend -When using ALB auth it is not possible to leverage the built-in auth config discovery mechanism implemented in the app created by default: a bespoke logic needs to be implemented. +When using ALB auth it is not possible to leverage the built-in auth config discovery mechanism implemented in the app created by default; bespoke logic needs to be implemented. - replace the content of `packages/backend/plugin/auth.ts` with the below From 0a0e1481500b66dd1f69a1266a37fdeb900c3f2d Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 22:51:36 +0100 Subject: [PATCH 10/12] using guest user convention Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 4eb52aa53c..4d90079046 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -70,10 +70,10 @@ const DummySignInComponent: any = (props: any) => { } else { // when running locally we default user identity to `Local User` props.onResult({ - userId: 'local user', + userId: 'guest', profile: { - email: 'local.user@yourdomain.com', - displayName: 'Local User', + email: 'guest@example.com', + displayName: 'Guest', picture: '', }, }); From b76b5ef9873cad5c2dfefdbcd3beeafc7a3f57be Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 22:55:44 +0100 Subject: [PATCH 11/12] remove details of createApp call Signed-off-by: Marco Crivellaro --- contrib/docs/tutorials/aws-alb-aad-oidc-auth.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 4d90079046..2503c31756 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -68,7 +68,6 @@ const DummySignInComponent: any = (props: any) => { }); }); } else { - // when running locally we default user identity to `Local User` props.onResult({ userId: 'guest', profile: { @@ -83,23 +82,16 @@ const DummySignInComponent: any = (props: any) => { }; ``` -- use `DummySignInComponent` as `SignInPage` by changing the `createApp` call: +- add `DummySignInComponent` as `SignInPage`: ```ts const app = createApp({ - apis, - plugins: Object.values(plugins), + ... components: { SignInPage: DummySignInComponent, + ... }, - bindRoutes({ bind }) { - bind(catalogPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - }); - bind(apiDocsPlugin.externalRoutes, { - createComponent: scaffolderPlugin.routes.root, - }); - }, + ... }); ``` From 703f47042a6bd24f7dc44796e4b30da4f06246ae Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 8 Apr 2021 23:15:05 +0100 Subject: [PATCH 12/12] handling error by outputing the message to the div Signed-off-by: Marco Crivellaro --- .../docs/tutorials/aws-alb-aad-oidc-auth.md | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index 2503c31756..ba0aef89ea 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -56,29 +56,32 @@ When using ALB authentication Backstage will only be loaded once the user has su ```ts const DummySignInComponent: any = (props: any) => { - const config = useApi(configApiRef); - const shouldAuth = !!config.getOptionalConfig('auth.providers.awsalb'); - if (shouldAuth) { - fetch(`${window.location.origin}/api/auth/awsalb/refresh`) - .then(data => data.json()) - .then(data => { - props.onResult({ - userId: data.backstageIdentity.id, - profile: data.profile, + try { + const config = useApi(configApiRef); + const shouldAuth = !!config.getOptionalConfig('auth.providers.awsalb'); + if (shouldAuth) { + fetch(`${window.location.origin}/api/auth/awsalb/refresh`) + .then(data => data.json()) + .then(data => { + props.onResult({ + userId: data.backstageIdentity.id, + profile: data.profile, + }); }); + } else { + props.onResult({ + userId: 'guest', + profile: { + email: 'guest@example.com', + displayName: 'Guest', + picture: '', + }, }); - } else { - props.onResult({ - userId: 'guest', - profile: { - email: 'guest@example.com', - displayName: 'Guest', - picture: '', - }, - }); + } + return
; + } catch (err) { + return
{err.message}
; } - - return
; }; ```