diff --git a/docs/backend-system/core-services/auth.md b/docs/backend-system/core-services/auth.md index 4f1229a9d7..f1243f11fe 100644 --- a/docs/backend-system/core-services/auth.md +++ b/docs/backend-system/core-services/auth.md @@ -5,4 +5,136 @@ sidebar_label: Auth description: Documentation for the Auth service --- -TODO +This service deals with the generation and verification of tokens and their +associated representations as credentials objects. You can use it for validating +incoming tokens, and generating tokens for calling other services. + +If you want to deal with credentials specifically in the HTTP request/response +flow, see also [the `httpAuth` service](./http-auth.md). If you want to extract +more details about authenticated users such as their ownership entity refs, use +[the `userInfo` service](./user-info.md). + +## Using the Service + +In the following code examples, the `auth` and `httpAuth` variables are assumed +to be dependency-injected instances of the `coreServices.auth` and +`coreServices.httpAuth` service, respectively. For a backend plugin, it might +look like this: + +```ts +export default createBackendPlugin({ + pluginId: 'my-plugin', + register(env) { + env.registerInit({ + deps: { + auth: coreServices.auth, + httpAuth: coreServices.httpAuth, + httpRouter: coreServices.httpRouter, + }, + async init({ auth, httpAuth, httpRouter }) { + // Your code goes here + }, + }); + }, +}); +``` + +### Creating Request Tokens + +If you need to create a token that can be used for making a request to another +backend plugin: + +```ts +const { token } = await auth.getPluginRequestToken({ + onBehalfOf: await auth.getOwnServiceCredentials(), + targetPluginId: 'catalog', +}); +``` + +:::note Note + +Never store and reuse tokens. Always call `getPluginRequestToken` immediately +before making a request. Otherwise you run the risk of running into permission +problems when expired tokens are being used for requests. + +::: + +This example is suitable when you need to make the request "as your own plugin", +i.e. when your code is the original initiator of the call. An example of this +could be periodic batch processes that index content in another service. + +In situations where you are making a call on-behalf-of someone else, for example +when making upstream requests inside a request handler, please always instead +use the extracted credentials from the request. + +```ts +router.get('/makes-calls', async (req, res) => { + const { token } = await auth.getPluginRequestToken({ + onBehalfOf: await httpAuth.credentials(req), + targetPluginId: 'catalog', + }); + // make a call using the token +``` + +This ensures that the original caller and their associated permissions are +properly carried along with the request chain. See [the `httpAuth` service docs](./http-auth.md) +for more details. + +The [service to service auth docs](../../auth/service-to-service-auth.md) +contain more details about how to properly use tokens in your HTTP request +paths. + +### Authorizing Tokens + +Most plugins should not deal with incoming request tokens directly at all, but +rather use [`httpAuth.credentials`](./http-auth.md) instead as part of their +request handlers. But in the rare cases where you are holding an incoming token +and want to validate it and turn it into a credentials object, you can do so: + +```ts +const credentials = await auth.authenticate(token); +``` + +There is an optional second parameter that you can set to `{ allowLimitedAccess: +true }` if you specifically built a plugin that deals with cookie based access, +which is rare. + +### Inspecting Credentials + +The `auth` service also contains facilities for working with credentials +objects. For example checking what type of principal (caller type - e.g. user or +service) they represent. For example: + +```ts +if (auth.isPrincipal(credentials, 'user)) { + // In here, the TypeScript type of the credentials object has been properly + // narrowed to `BackstageCredentials` so you can + // access its specific properties such as `credentials.principal.userEntityRef`. +} +``` + +## Configuring the service + +:::note Note + +The `auth` service is not suitable for having its implementation replaced +entirely in your private repo. If you desire additional service auth related +features, don't hesitate to [file an issue](https://github.com/backstage/backstage/issues/new/choose) +or [contribute](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) to the open source features. + +::: + +For configuring service-to-service access methods, see [the auth docs](../../auth/service-to-service-auth.md). + +The default auth policy requires all requests to be authenticated with either +user or service credentials. This can be disabled by setting the +`backend.auth.dangerouslyDisableDefaultAuthPolicy` app-config flag to `true`. +Disabling this check means that the backend will no longer block unauthenticated +requests, but instead allow them to pass through to plugins. Do not do this in +production unless absolutely necessary. + +If permissions are enabled, unauthenticated requests will be treated exactly as +such, leaving it to the permission policy to determine what permissions should +be allowed for an unauthenticated identity. Note that this will also apply to +service-to-service calls between plugins unless you configure credentials for +service calls. diff --git a/docs/backend-system/core-services/http-auth.md b/docs/backend-system/core-services/http-auth.md index 7af30c3251..05a26fc075 100644 --- a/docs/backend-system/core-services/http-auth.md +++ b/docs/backend-system/core-services/http-auth.md @@ -5,4 +5,102 @@ sidebar_label: Http Auth description: Documentation for the Http Auth service --- -TODO +The `httpAuth` service deals with submitting and receiving credentials on Express +HTTP request/response objects. This service is frequently used in plugins that +have REST interfaces. + +If you want to deal with raw tokens and do low level credentials handling, see +also [the `auth` service](./auth.md). If you want to extract more details about +authenticated users such as their ownership entity refs, use [the `userInfo` service](./user-info.md). + +## Using the Service + +In the following code examples, the `auth` and `httpAuth` variables are assumed +to be dependency-injected instances of the `coreServices.auth` and +`coreServices.httpAuth` service, respectively. For a backend plugin, it might +look like this: + +```ts +export default createBackendPlugin({ + pluginId: 'my-plugin', + register(env) { + env.registerInit({ + deps: { + auth: coreServices.auth, + httpAuth: coreServices.httpAuth, + httpRouter: coreServices.httpRouter, + }, + async init({ auth, httpAuth, httpRouter }) { + // Your code goes here + }, + }); + }, +}); +``` + +### Getting Request Credentials + +If you need to extract the validated credentials out of an incoming request, you +can do so like this: + +```ts +router.get('/some-request', async (req, res) => { + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); + // Do something with the credentials here +``` + +The second argument is optional, but in this example we specified that we only +want to allow user based requests. The credentials returned will then have a +narrowed TypeScript type that reflects that the principal is known to be of the +user type. This second argument can also specify `allowLimitedAccess: true` if +you specifically built a plugin that deals with cookie based access, which is +rare. + +The default is to accept both service and user credentials (excluding limited +access), but in the example above, any attempt to call this endpoint with +service credentials will result in an Unauthorized error being thrown. + +:::note Note + +You don't need to call `httpAuth.credentials` _just_ to ensure that incoming +credentials are valid in the first place; only use this method if you actually +need to act upon the credentials somehow. The Backstage backend framework will have +ensured the actual validity of any incoming token before your backend code is +reached. The policy for these upfront framework level rules is controlled using +[the `httpRouter` service](./http-router.md) when you register your routes. + +::: + +If you want to further work with the credentials object, [the `auth` service](./auth.md) +has helper methods for that. + +### Issuing Cookies + +For some rare use cases, plugins may want to issue cookies with _limited access_ +user credentials. This is mostly relevant when browsers need to be able to +request static resources, such as in the TechDocs plugin. + +Plugins should almost never interact with the cookie functionality of the +`httpAuth` service directly. The framework has builtin handling of cookie +creation/deletion requests on a dedicated well-known endpoint. All you normally +have to do to accept limited user access is to inform [the `httpRouter` service](./http-router.md) +when creating your route that you want to permit +cookie based access for a specific route, and then setting `allowLimitedAccess` +to `true` when extracting credentials. + +Due to the above, we do not document the `httpAuth.issueUserCookie` method here. + +## Configuring the service + +:::note Note + +The `httpAuth` service is not suitable for having its implementation replaced +entirely in your private repo. If you desire additional service auth related +features, don't hesitate to [file an issue](https://github.com/backstage/backstage/issues/new/choose) +or [contribute](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) to the open source features. + +::: + +This service has no configuration options, but it abides by the policies you +have set up using [the `httpRouter` service](./http-router.md) for your routes, +if any. diff --git a/docs/backend-system/core-services/http-router.md b/docs/backend-system/core-services/http-router.md index 4dfa7d80e1..6ab8218c83 100644 --- a/docs/backend-system/core-services/http-router.md +++ b/docs/backend-system/core-services/http-router.md @@ -5,7 +5,8 @@ sidebar_label: Http Router description: Documentation for the Http Router service --- -One of the most common services is the HTTP router service which is used to expose HTTP endpoints for other plugins to consume. +One of the most common services is the HTTP router service which is used to +expose HTTP endpoints for other plugins to consume. ## Using the service @@ -37,6 +38,36 @@ createBackendPlugin({ }); ``` +This service is also responsible for keeping track of the auth policies that +apply to your routes. The default policy is to require that auth is present with +every incoming request, and to accept both service and user credentials +(excluding limited access tokens). You can override this while registering your +routes. This dangerously allows unauthenticated access on a specific route: + +```ts +http.addAuthPolicy({ + path: '/static/:id', + allow: 'unauthenticated', +}); +``` + +Note that the path is exactly the same format as what you used in your routes, +including placeholders. + +If your plugin uses cookie based access (which is rare), you need to allow that +as follows: + +```ts +http.addAuthPolicy({ + path: '/static/:id', + allow: 'user-cookie', +}); +``` + +For those routes you will also have to specify `allowLimitedAccess: true` when +using the [`auth`](./auth.md) and [`httpAuth`](./http-auth.md) services to +access the incoming credentials. + ## Configuring the service This service does not have any configuration options. diff --git a/docs/backend-system/core-services/user-info.md b/docs/backend-system/core-services/user-info.md index 5ca717ddd5..e9d17f1dd7 100644 --- a/docs/backend-system/core-services/user-info.md +++ b/docs/backend-system/core-services/user-info.md @@ -5,4 +5,63 @@ sidebar_label: User Info description: Documentation for the User Info service --- -TODO +This service lets you extract more information about a set of user credentials. +Specifically, it can be used to extract the ownership entity refs for a user +principal. + +See also the [`auth`](./auth.md) and [`httpAuth`](./http-auth.md) services for +general credentials handling. + +## Using the Service + +In the following code examples, the `userInfo`, `auth`, and `httpAuth` variables are assumed +to be dependency-injected instances of the `coreServices.userInfo` and +`coreServices.httpAuth` service, respectively. For a backend plugin, it might +look like this: + +```ts +export default createBackendPlugin({ + pluginId: 'my-plugin', + register(env) { + env.registerInit({ + deps: { + auth: coreServices.auth, + httpAuth: coreServices.httpAuth, + httpRouter: coreServices.httpRouter, + userInfo: coreServices.userInfo, + }, + async init({ auth, httpAuth, httpRouter, userInfo }) { + // Your code goes here + }, + }); + }, +}); +``` + +### Getting User Info + +This example extracts some user credentials out of a request and fetches +additional information about that principal. + +```ts +router.get('/some-request', async (req, res) => { + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); + const info = await userInfo.getUserInfo(credentials); +``` + +The `userInfo` service only deals with credentials that contain user principals, +it won't accept requests for service principals. In our example code the initial +credentials extraction limits it to user credentials upfront. If you have an +endpoint that allows both user and service credentials, you may want to wrap +your user info extraction in a principal type check: + +```ts +router.get('/some-request', async (req, res) => { + const credentials = await httpAuth.credentials(req); + if (auth.isPrincipal(credentials, 'user')) { + const info = await userInfo.getUserInfo(credentials); + // ... +``` + +The user info contains data that was extracted during sign-in for the given +user.