document the auth service triplet

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2024-06-05 13:24:13 +02:00
parent c8c0bb95ca
commit 2d5fa73716
4 changed files with 324 additions and 4 deletions
+133 -1
View File
@@ -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<BackstageUserPrincipal>` 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.
+99 -1
View File
@@ -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.
@@ -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.
+60 -1
View File
@@ -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.