From 335151a281f76f9462c8cb5761e94c0b16b0681b Mon Sep 17 00:00:00 2001 From: web-next-automation Date: Sun, 7 Apr 2024 19:26:17 -0400 Subject: [PATCH 01/10] fix: adjust the backend plugin docs for new backend Signed-off-by: web-next-automation Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 145 ++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 67 deletions(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 497a6be9b9..827d82c4cf 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -44,7 +44,7 @@ cd plugins/carmen-backend yarn start ``` -> Note: this documentation assumes you are using the latest version of Backstage and the new backend system. If you are not please upgrade and migrate your backend using the [Migration Guide](../backend-system/building-backends/08-migrating.md) +> Note: this documentation assumes you are using the latest version of Backstage and the new backend system. If you are not, please upgrade and migrate your backend using the [Migration Guide](../backend-system/building-backends/08-migrating.md) This will think for a bit, and then say `Listening on :7007`. In a different terminal window, now run @@ -72,38 +72,19 @@ to your backend. yarn --cwd packages/backend add @internal/plugin-carmen-backend@^0.1.0 # Change this to match the plugin's package.json ``` -Create a new file named `packages/backend/src/plugins/carmen.ts`, and add the -following to it +Update `packages/backend/src/index` with the following, ```ts -import { createRouter } from '@internal/plugin-carmen-backend'; -import { Router } from 'express'; -import { PluginEnvironment } from '../types'; +const backend = createBackend(); -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - // Here is where you will add all of the required initialization code that - // your backend plugin needs to be able to start! - - // The env contains a lot of goodies, but our router currently only - // needs a logger - return await createRouter({ - logger: env.logger, - }); -} -``` - -And finally, wire this into the overall backend router. Edit -`packages/backend/src/index.ts`: - -```ts -import carmen from './plugins/carmen'; // ... -async function main() { - // ... - const carmenEnv = useHotMemoize(module, () => createEnv('carmen')); - apiRouter.use('/carmen', await carmen(carmenEnv)); + +// highlight-add-next-line +backend.add(import('@internal/plugin-carmen-backend')); + +// ... + +backend.start(); ``` After you start the backend (e.g. using `yarn start-backend` from the repo @@ -122,30 +103,48 @@ The Backstage backend comes with a builtin facility for SQL database access. Most plugins that have persistence needs will choose to make use of this facility, so that Backstage operators can manage database needs uniformly. -As part of the environment object that is passed to your `createPlugin` -function, there is a `database` field. You can use that to get a -[Knex](http://knexjs.org/) connection object. +You can access this by adding a dependency on the `coreServices.database` service. +That will give you a [Knex](http://knexjs.org/) connection object. -```ts -// in packages/backend/src/plugins/carmen.ts -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const db: Knex = await env.database.getClient(); - - // You will then pass this client into your actual plugin implementation - // code, maybe similar to the following: - const model = new CarmenDatabaseModel(db); - return await createRouter({ - model: model, - logger: env.logger, - }); -} +```ts title="plugins/carmen-backend/src/plugin.ts" +export const carmenPlugin = createBackendPlugin({ + pluginId: 'carmenPlugin', + register(env) { + env.registerInit({ + deps: { + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, + // highlight-next-line + database: coreServices.database, + }, + async init({ + httpRouter, + logger, + // highlight-next-line + database, + }) { + // You will then pass this client into your actual plugin implementation + // code, maybe similar to the following: + const model = new CarmenDatabaseModel(db); + httpRouter.use( + await createRouter({ + // highlight-next-line + model, + logger, + }), + ); + httpRouter.addAuthPolicy({ + path: '/health', + allow: 'unauthenticated', + }); + }, + }); + }, +}); ``` -You may note that the `getClient` call has no parameters. This is because all -plugin database needs are configured under the `backend.database` config key of -your `app-config.yaml`. The framework may even make sure behind the scenes that +All plugin database needs are configured under the `backend.database` config key +of your `app-config.yaml`. The framework may even make sure behind the scenes that the logical database is created automatically if it doesn't exist, based on rules that the Backstage operator decides on. @@ -159,24 +158,36 @@ database.. ## Making Use of the User's Identity -The Backstage backend comes with a facility for retrieving the identity of the -logged in user. +The Backstage backend also offers a core service to access the user's identity. You can access it through the `coreServices.identity` dependency. -As part of the environment object that is passed to your `createPlugin` -function, there is a `identity` field. You can use that to get an identity -from the request. - -```ts -// in packages/backend/src/plugins/carmen.ts -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - return await createRouter({ - model: model, - logger: env.logger, - identity: env.identity, - }); -} +```ts title="plugins/carmen-backend/src/plugin.ts" +export const carmenPlugin = createBackendPlugin({ + pluginId: 'carmenPlugin', + register(env) { + env.registerInit({ + deps: { + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, + // highlight-next-line + identity: coreServices.identity, + }, + async init({ + httpRouter, + logger, + // highlight-next-line + identity, + }) { + httpRouter.use( + await createRouter({ + // highlight-next-line + identity, + logger, + }), + ); + }, + }); + }, +}); ``` The plugin can then extract the identity from the request. From cf7f8d7836aa84faeb85137350c78c5b8b6c92a1 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Date: Mon, 8 Apr 2024 21:10:20 -0400 Subject: [PATCH 02/10] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 827d82c4cf..a47df2a106 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -108,7 +108,7 @@ That will give you a [Knex](http://knexjs.org/) connection object. ```ts title="plugins/carmen-backend/src/plugin.ts" export const carmenPlugin = createBackendPlugin({ - pluginId: 'carmenPlugin', + pluginId: 'carmen', register(env) { env.registerInit({ deps: { @@ -125,7 +125,7 @@ export const carmenPlugin = createBackendPlugin({ }) { // You will then pass this client into your actual plugin implementation // code, maybe similar to the following: - const model = new CarmenDatabaseModel(db); + const model = new CarmenDatabaseModel(database); httpRouter.use( await createRouter({ // highlight-next-line From 7c6d8600c9a9a21e5d9bc7bb72a97f7ca3d579c9 Mon Sep 17 00:00:00 2001 From: web-next-automation Date: Mon, 8 Apr 2024 21:29:12 -0400 Subject: [PATCH 03/10] doc updates Signed-off-by: web-next-automation Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 107 +++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index a47df2a106..24b6a76fbc 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -97,6 +97,109 @@ curl localhost:7007/api/carmen/health This should return `{"status":"ok"}` like before. Success! +## Secure by Default + +In 1.25, Backstage started moving to a secure by default model for plugins. This means that network requests to plugins will by default not allow unauthenticated users. Let's take a deeper look at the above curl request which should allow unauthenticated access. + +The actual endpoint that is being called is defined in + +```ts title="plugins/carmen-backend/src/service/router.ts" +export async function createRouter( + options: RouterOptions, +): Promise { + const { logger } = options; + + const router = Router(); + router.use(express.json()); + + // highlight-start + router.get('/health', (_, response) => { + logger.info('PONG!'); + response.json({ status: 'ok' }); + }); + // highlight-end + + router.use(errorHandler()); + return router; +} +``` + +You'll notice that there is no authentication mechanism defined here, just the route name and response data. That's because the authentication is handled in your plugin definition, + +```ts title="plugins/carmen-backend/src/plugin.ts" +export const carmenPlugin = createBackendPlugin({ + pluginId: 'carmenPlugin', + register(env) { + env.registerInit({ + deps: { + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, + }, + async init({ httpRouter, logger }) { + httpRouter.use( + await createRouter({ + logger, + }), + ); + // highlight-start + httpRouter.addAuthPolicy({ + path: '/health', + allow: 'unauthenticated', + }); + // highlight-end + }, + }); + }, +}); +``` + +This allows requests to this plugin's `/health` endpoint to go through unauthenticated! + +## Using Dependencies + +In the new backend, dependencies are defined statically during registration and then "injected" during initialization. Here's an example of what this looks like, + +```ts title="plugins/carmen-backend/src/plugin.ts" + register(env) { + env.registerInit({ + // highlight-start + deps: { + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, + }, + // highlight-end + // And then you can use them through the options property! + // highlight-next-line + async init({ httpRouter, logger }) { + httpRouter.use( + await createRouter({ + logger, + }), + ); + }, + }); + }, +``` + +Backstage provides a bunch of `coreServices` out of box: + +- **Database**: A database connection layer using knex. +- **Root Config**: Access to the config that Backstage was started with. +- **Logger**: A plugin scoped logger ready for logging! +- **HTTP Router**: An `express`-compatible router for adding HTTP routes to your plugin. +- **User Info**: Personalize your plugin experience for users by getting the currently logged in user. +- **Auth**: Perform low level authentication. +- **HTTP Auth**: HTTP authentication mechanisms, for example, allowing unauthenticated access. See [here](https://backstage.io/docs/backend-system/core-services/http-auth) for more info. +- **Cache**: A plugin-scoped cache. +- **Discovery**: Service discovery mechanism for your plugins. +- **Plugin Metadata**: Introspect your plugin. +- **Lifecycle**: Add hooks to lifecycle events. +- **Permissions**: Authorize requests to specific resources in your plugins. +- **URL Reader**: Authenticated URL calling mechanism. +- **Scheduler**: Schedule jobs to run on a certain cadence. + +If you want to override or create new ones, you can also define your own services much like you create your own plugins. + ## Making Use of a Database The Backstage backend comes with a builtin facility for SQL database access. @@ -133,10 +236,6 @@ export const carmenPlugin = createBackendPlugin({ logger, }), ); - httpRouter.addAuthPolicy({ - path: '/health', - allow: 'unauthenticated', - }); }, }); }, From 40f557c2e497855dcaf1ddfecb854a3d08927896 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Mon, 8 Apr 2024 21:36:19 -0400 Subject: [PATCH 04/10] small update Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 24b6a76fbc..9951b67b18 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -181,6 +181,25 @@ In the new backend, dependencies are defined statically during registration and }, ``` +You can add your own dependencies by adding a named item to the `deps` parameter: + +```ts +deps: { + // highlight-next-line + myDependency: coreServices.rootConfig, +}, +``` + +And then you can access it by referencing it in the `init` block of your plugin definition, + +```ts +async init({ myDependency }) { + // .. +} +``` + +And then you're free to call it and pass it into your router as needed. + Backstage provides a bunch of `coreServices` out of box: - **Database**: A database connection layer using knex. From 55b47216c3bb4d58bcd4d6cd977879320af32f0a Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Tue, 9 Apr 2024 10:41:44 -0400 Subject: [PATCH 05/10] cut down the docs Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 186 +++++++++++---------------------- 1 file changed, 63 insertions(+), 123 deletions(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 9951b67b18..a0b0b878cd 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -107,10 +107,7 @@ The actual endpoint that is being called is defined in export async function createRouter( options: RouterOptions, ): Promise { - const { logger } = options; - - const router = Router(); - router.use(express.json()); + // ... // highlight-start router.get('/health', (_, response) => { @@ -119,7 +116,7 @@ export async function createRouter( }); // highlight-end - router.use(errorHandler()); + // ... return router; } ``` @@ -127,30 +124,17 @@ export async function createRouter( You'll notice that there is no authentication mechanism defined here, just the route name and response data. That's because the authentication is handled in your plugin definition, ```ts title="plugins/carmen-backend/src/plugin.ts" -export const carmenPlugin = createBackendPlugin({ - pluginId: 'carmenPlugin', - register(env) { - env.registerInit({ - deps: { - httpRouter: coreServices.httpRouter, - logger: coreServices.logger, - }, - async init({ httpRouter, logger }) { - httpRouter.use( - await createRouter({ - logger, - }), - ); - // highlight-start - httpRouter.addAuthPolicy({ - path: '/health', - allow: 'unauthenticated', - }); - // highlight-end - }, - }); - }, +httpRouter.use( + await createRouter({ + logger, + }), +); +// highlight-start +httpRouter.addAuthPolicy({ + path: '/health', + allow: 'unauthenticated', }); +// highlight-end ``` This allows requests to this plugin's `/health` endpoint to go through unauthenticated! @@ -160,25 +144,19 @@ This allows requests to this plugin's `/health` endpoint to go through unauthent In the new backend, dependencies are defined statically during registration and then "injected" during initialization. Here's an example of what this looks like, ```ts title="plugins/carmen-backend/src/plugin.ts" - register(env) { - env.registerInit({ - // highlight-start - deps: { - httpRouter: coreServices.httpRouter, - logger: coreServices.logger, - }, - // highlight-end - // And then you can use them through the options property! - // highlight-next-line - async init({ httpRouter, logger }) { - httpRouter.use( - await createRouter({ - logger, - }), - ); - }, - }); - }, + +// highlight-start +deps: { + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, +}, +// highlight-end +// And then you can use them through the options property! +// highlight-next-line +async init({ httpRouter, logger }) { + // ... +}, + ``` You can add your own dependencies by adding a named item to the `deps` parameter: @@ -200,24 +178,7 @@ async init({ myDependency }) { And then you're free to call it and pass it into your router as needed. -Backstage provides a bunch of `coreServices` out of box: - -- **Database**: A database connection layer using knex. -- **Root Config**: Access to the config that Backstage was started with. -- **Logger**: A plugin scoped logger ready for logging! -- **HTTP Router**: An `express`-compatible router for adding HTTP routes to your plugin. -- **User Info**: Personalize your plugin experience for users by getting the currently logged in user. -- **Auth**: Perform low level authentication. -- **HTTP Auth**: HTTP authentication mechanisms, for example, allowing unauthenticated access. See [here](https://backstage.io/docs/backend-system/core-services/http-auth) for more info. -- **Cache**: A plugin-scoped cache. -- **Discovery**: Service discovery mechanism for your plugins. -- **Plugin Metadata**: Introspect your plugin. -- **Lifecycle**: Add hooks to lifecycle events. -- **Permissions**: Authorize requests to specific resources in your plugins. -- **URL Reader**: Authenticated URL calling mechanism. -- **Scheduler**: Schedule jobs to run on a certain cadence. - -If you want to override or create new ones, you can also define your own services much like you create your own plugins. +Backstage provides a bunch of `coreServices` out of box, see the more in depth docs [here](https://backstage.io/docs/backend-system/core-services/index). ## Making Use of a Database @@ -229,36 +190,26 @@ You can access this by adding a dependency on the `coreServices.database` servic That will give you a [Knex](http://knexjs.org/) connection object. ```ts title="plugins/carmen-backend/src/plugin.ts" -export const carmenPlugin = createBackendPlugin({ - pluginId: 'carmen', - register(env) { - env.registerInit({ - deps: { - httpRouter: coreServices.httpRouter, - logger: coreServices.logger, - // highlight-next-line - database: coreServices.database, - }, - async init({ - httpRouter, - logger, - // highlight-next-line - database, - }) { - // You will then pass this client into your actual plugin implementation - // code, maybe similar to the following: - const model = new CarmenDatabaseModel(database); - httpRouter.use( - await createRouter({ - // highlight-next-line - model, - logger, - }), - ); - }, - }); - }, -}); +deps: { + // ... + // highlight-next-line + database: coreServices.database, +}, +async init({ + // highlight-next-line + database, +}) { + // You will then pass this client into your actual plugin implementation + // code, maybe similar to the following: + const model = new CarmenDatabaseModel(database); + httpRouter.use( + await createRouter({ + // highlight-next-line + model, + logger, + }), + ); +} ``` All plugin database needs are configured under the `backend.database` config key @@ -279,33 +230,22 @@ database.. The Backstage backend also offers a core service to access the user's identity. You can access it through the `coreServices.identity` dependency. ```ts title="plugins/carmen-backend/src/plugin.ts" -export const carmenPlugin = createBackendPlugin({ - pluginId: 'carmenPlugin', - register(env) { - env.registerInit({ - deps: { - httpRouter: coreServices.httpRouter, - logger: coreServices.logger, - // highlight-next-line - identity: coreServices.identity, - }, - async init({ - httpRouter, - logger, - // highlight-next-line - identity, - }) { - httpRouter.use( - await createRouter({ - // highlight-next-line - identity, - logger, - }), - ); - }, - }); - }, -}); +deps: { + // highlight-next-line + identity: coreServices.identity, +}, +async init({ + // highlight-next-line + identity, +}) { + httpRouter.use( + await createRouter({ + // highlight-next-line + identity, + logger, + }), + ); +} ``` The plugin can then extract the identity from the request. @@ -314,7 +254,7 @@ The plugin can then extract the identity from the request. export async function createRouter( options: RouterOptions, ): Promise { - const router = Router(); + // ... const { identity } = options; router.post('/example', async (req, res) => { From f3b9148f8840ce8fb4041cd3b8ca32cc62a3a1b5 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Mon, 22 Apr 2024 09:36:44 -0400 Subject: [PATCH 06/10] update docs for new auth Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 44 ++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index a0b0b878cd..9984112f96 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -178,7 +178,7 @@ async init({ myDependency }) { And then you're free to call it and pass it into your router as needed. -Backstage provides a bunch of `coreServices` out of box, see the more in depth docs [here](https://backstage.io/docs/backend-system/core-services/index). +Backstage provides a bunch of `coreServices` out of box, see the more in depth docs [here](../backend-system/core-services/01-index.md). ## Making Use of a Database @@ -231,17 +231,23 @@ The Backstage backend also offers a core service to access the user's identity. ```ts title="plugins/carmen-backend/src/plugin.ts" deps: { - // highlight-next-line - identity: coreServices.identity, + // highlight-start + httpAuth: coreServices.httpAuth, + userInfo: coreServices.userInfo + // highlight-end }, async init({ - // highlight-next-line - identity, + // highlight-start + httpAuth, + userInfo, + // highlight-end }) { httpRouter.use( await createRouter({ - // highlight-next-line - identity, + // highlight-start + httpAuth, + userInfo, + // highlight-end logger, }), ); @@ -251,14 +257,32 @@ async init({ The plugin can then extract the identity from the request. ```ts +export interface RouterOptions { + logger: LoggerService; + // highlight-start + userInfo: UserInfoService; + httpAuth: HttpAuthService; + // highlight-end +} + export async function createRouter( options: RouterOptions, ): Promise { // ... - const { identity } = options; + const { userInfo, httpAuth } = options; - router.post('/example', async (req, res) => { - const userIdentity = await identity.getIdentity({ request: req }); + router.post('/me', async (request, response) => { + const credentials = await httpAuth.credentials(request) + const userInfo = await userInfo.getUserInfo(credentials); + response.json( + { + // The catalog entity ref of the user. + userEntityRef: userInfo.userEntityRef, + + // The list of entities that this user or any teams this user is a part of owns. + ownershipEntityRefs: userInfo.ownershipEntityRefs + }, + ); ... }); ``` From 61d4dc593e73d4fa10ed09c34a222bd180affd4f Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Mon, 22 Apr 2024 10:40:03 -0400 Subject: [PATCH 07/10] whoops Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 9984112f96..46997f0101 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -227,7 +227,7 @@ database.. ## Making Use of the User's Identity -The Backstage backend also offers a core service to access the user's identity. You can access it through the `coreServices.identity` dependency. +The Backstage backend also offers a core service to access the user's identity. You can access it through the `coreServices.httpAuth` and `coreServices.userInfo` dependencies. ```ts title="plugins/carmen-backend/src/plugin.ts" deps: { @@ -272,6 +272,10 @@ export async function createRouter( const { userInfo, httpAuth } = options; router.post('/me', async (request, response) => { + if (!auth.isPrincipal(credentials, 'user')) { + // Block requests that aren't from the user, this can include services or external callers. + response.status(401); + } const credentials = await httpAuth.credentials(request) const userInfo = await userInfo.getUserInfo(credentials); response.json( From db7c4847f2a2d3397e3ccfb62e54d728541870ac Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Mon, 22 Apr 2024 10:40:32 -0400 Subject: [PATCH 08/10] return early Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 46997f0101..384c6ca861 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -274,7 +274,7 @@ export async function createRouter( router.post('/me', async (request, response) => { if (!auth.isPrincipal(credentials, 'user')) { // Block requests that aren't from the user, this can include services or external callers. - response.status(401); + return response.status(401); } const credentials = await httpAuth.credentials(request) const userInfo = await userInfo.getUserInfo(credentials); From f50d970af685f490c3367f74d817269159f29268 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Mon, 22 Apr 2024 10:46:19 -0400 Subject: [PATCH 09/10] inject auth Signed-off-by: aramissennyeydd --- docs/plugins/backend-plugin.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 384c6ca861..fa27dcf8bb 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -233,13 +233,15 @@ The Backstage backend also offers a core service to access the user's identity. deps: { // highlight-start httpAuth: coreServices.httpAuth, - userInfo: coreServices.userInfo + userInfo: coreServices.userInfo, + auth: coreServices.auth, // highlight-end }, async init({ // highlight-start httpAuth, userInfo, + auth, // highlight-end }) { httpRouter.use( @@ -247,6 +249,7 @@ async init({ // highlight-start httpAuth, userInfo, + auth, // highlight-end logger, }), @@ -262,6 +265,7 @@ export interface RouterOptions { // highlight-start userInfo: UserInfoService; httpAuth: HttpAuthService; + auth: AuthService; // highlight-end } @@ -269,7 +273,7 @@ export async function createRouter( options: RouterOptions, ): Promise { // ... - const { userInfo, httpAuth } = options; + const { userInfo, httpAuth, auth } = options; router.post('/me', async (request, response) => { if (!auth.isPrincipal(credentials, 'user')) { From b649797bf42999d7035d2651938a1d39a25c76ac Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 22 Apr 2024 16:57:31 +0200 Subject: [PATCH 10/10] docs/plugins/backend-plugin: switch to httpAuth Signed-off-by: Patrik Oldsberg --- docs/plugins/backend-plugin.md | 39 ++++++++++++++++------------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index fa27dcf8bb..7a51cb635c 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -234,14 +234,12 @@ deps: { // highlight-start httpAuth: coreServices.httpAuth, userInfo: coreServices.userInfo, - auth: coreServices.auth, // highlight-end }, async init({ // highlight-start httpAuth, userInfo, - auth, // highlight-end }) { httpRouter.use( @@ -249,7 +247,6 @@ async init({ // highlight-start httpAuth, userInfo, - auth, // highlight-end logger, }), @@ -265,32 +262,32 @@ export interface RouterOptions { // highlight-start userInfo: UserInfoService; httpAuth: HttpAuthService; - auth: AuthService; // highlight-end } export async function createRouter( options: RouterOptions, ): Promise { - // ... - const { userInfo, httpAuth, auth } = options; + const { userInfo, httpAuth } = options; + + router.post('/me', async (req, res) => { + const credentials = await httpAuth.credentials(req, { + // This rejects request from non-users. Only use this if your plugin needs to access the + // user identity, most of the time it's enough to just call `httpAuth.credentials(req)` + allow: ['user'], + }); - router.post('/me', async (request, response) => { - if (!auth.isPrincipal(credentials, 'user')) { - // Block requests that aren't from the user, this can include services or external callers. - return response.status(401); - } - const credentials = await httpAuth.credentials(request) const userInfo = await userInfo.getUserInfo(credentials); - response.json( - { - // The catalog entity ref of the user. - userEntityRef: userInfo.userEntityRef, - // The list of entities that this user or any teams this user is a part of owns. - ownershipEntityRefs: userInfo.ownershipEntityRefs - }, - ); - ... + res.json({ + // The catalog entity ref of the user. + userEntityRef: userInfo.userEntityRef, + + // The list of entities that this user or any teams this user is a part of owns. + ownershipEntityRefs: userInfo.ownershipEntityRefs, + }); }); + + // ... +} ```