Skip to content
This repository has been archived by the owner on Sep 10, 2024. It is now read-only.

Document the admin API #3038

Merged
merged 6 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This documentation has four main sections:

- The [installation guide](./setup/README.md) will guide you through the process of setting up the `matrix-authentication-service` on your own infrastructure.
- The topics sections goes into more details about how the service works, like the [policy engine](./topics/policy.md) and how [authorization sessions](./topics/authorization.md) are managed.
- The reference documentation covers [configuration options](./reference/configuration.md), the [GraphQL API](./reference/graphql.md), the [scopes](./reference/scopes.md) supported by the service, and the [command line interface](./reference/cli/).
- The reference documentation covers [configuration options](./reference/configuration.md), the [Admin API](./api/index.html), the [scopes](./reference/scopes.md) supported by the service, and the [command line interface](./reference/cli/).
- The developer documentation is intended for people who want to [contribute to the project](./development/contributing.md). Developers may also be interested in:
- Technical documentation for individual crates: [`rustdoc`](./rustdoc/mas_handlers/)
- UI components: [`storybook`](./storybook/)
6 changes: 4 additions & 2 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Summary
# Summary

# Introduction

Expand All @@ -21,11 +21,12 @@

- [Policy engine](./topics/policy.md)
- [Authorization and sessions](./topics/authorization.md)
- [Use the Admin API](./topics/admin-api.md)

# Reference

- [Configuration file reference](./reference/configuration.md)
- [GraphQL API](./reference/graphql.md)
- [Admin API](./api/index.html)
- [OAuth 2.0 scopes](./reference/scopes.md)
- [Command line tool](./reference/cli/README.md)
- [`config`](./reference/cli/config.md)
Expand All @@ -40,6 +41,7 @@
- [Contributing](./development/contributing.md)
- [Architecture](./development/architecture.md)
- [Database](./development/database.md)
- [Internal GraphQL API](./development/graphql.md)

---

Expand Down
13 changes: 6 additions & 7 deletions docs/reference/graphql.md → docs/development/graphql.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# GraphQL API
# Internal GraphQL API

MAS provides a GraphQL API which serves two purposes:
> **Note:** This API used to be the way for external tools to interact with MAS. However, **external usage is now deprecated** in favour of the REST based [Admin API](../topics/admin-api.md). External access to this API will be removed in a future release.

- it is used by the self-service user interface (usually accessible on `/account/`), for users to manage their own account.
- it can be used with external tools to manage the service.
MAS uses an internal GraphQL API which is used by the self-service user interface (usually accessible on `/account/`), for users to manage their own account.

The endpoint for this API can be discovered through the OpenID Connect discovery document, under the `"org.matrix.matrix-authentication-service.graphql_endpoint` key.
The endpoint for this API can be discovered through the OpenID Connect discovery document, under the `org.matrix.matrix-authentication-service.graphql_endpoint` key.
Though it is usually hosted at `https://<mas-host>/graphql`.

GraphQL uses [a self-describing schema](https://github.com/matrix-org/matrix-authentication-service/blob/main/frontend/schema.graphql), which means that the API can be explored in tools like the GraphQL Playground.
Expand All @@ -23,5 +22,5 @@ With only this scope, the session will be authorized as the user who owns the ac

To get full access to the GraphQL API, the access token must have the [`urn:mas:admin`] scope in addition to the [`urn:mas:graphql:*`] scope.

[`urn:mas:graphql:*`]: ./scopes.md#urnmasgraphql
[`urn:mas:admin`]: ./scopes.md#urnmasadmin
[`urn:mas:graphql:*`]: ../reference/scopes.md#urnmasgraphql
[`urn:mas:admin`]: ../reference/scopes.md#urnmasadmin
2 changes: 2 additions & 0 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ http:
# Serve the given folder on the /assets/ path
- name: assets
path: ./share/assets/
# Serve the admin API on the /api/admin/v1/ path. Disabled by default
#- name: adminapi

# List of addresses and ports to listen to
binds:
Expand Down
27 changes: 14 additions & 13 deletions docs/reference/scopes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ The [default policy](../topics/policy.md#authorization-requests) shipped with MA
- [`urn:matrix:org.matrix.msc2967.client:device:[device id]`](#urnmatrixorgmatrixmsc2967clientdevicedevice-id)
- [`urn:matrix:org.matrix.msc2967.client:guest`](#urnmatrixorgmatrixmsc2967clientguest)
- [`urn:synapse:admin:*`](#urnsynapseadmin)
- [`urn:mas:graphql:*`](#urnmasgraphql)
- [`urn:mas:admin`](#urnmasadmin)
- [`urn:mas:graphql:*`](#urnmasgraphql)

## OpenID Connect scopes

Expand Down Expand Up @@ -79,19 +79,9 @@ It allows:

MAS also has a few scopes that are specific to the MAS implementation.

### `urn:mas:graphql:*`

This scope grants access to the whole MAS [GraphQL API].
What permission the session has on the API is determined by the entity that the session is authorized as.
When [authorized as a user](../topics/authorization.md#authorized-as-a-user-or-authorized-as-a-client) (and without the `mas:urn:admin` scope), this will usually allow querying and mutating the user's own data.

The default policy allows any client and any user to request this scope.

### `urn:mas:admin`

This scope allows full access to the MAS [GraphQL API].
It requires the `urn:mas:graphql:*` scope to be present in the request.
This allows the authenticated entity to perform any operation on the API, regardless of whether the entity owns the data or not.
This scope grants full access to the MAS [Admin API].

The default policy doesn't allow everyone to request this scope.
It allows:
Expand All @@ -102,9 +92,20 @@ It allows:
- for the "client credentials" grant:
- clients that are listed in the [`policy.data.admin_clients`](../reference/configuration.md#policy) configuration option

### `urn:mas:graphql:*`

This scope grants access to the whole MAS [Internal GraphQL API].
What permission the session has on the API is determined by the entity that the session is authorized as.
When [authorized as a user](../topics/authorization.md#authorized-as-a-user-or-authorized-as-a-client) (and without the `mas:urn:admin` scope), this will usually allow querying and mutating the user's own data.

The default policy allows any client and any user to request this scope.

However, as noted in the [Internal GraphQL API] documentation, access to the Internal GraphQL API from outside of MAS itself is deprecated in favour of the [Admin API].

[authorization code]: ../topics/authorization.md#authorization-code-grant
[device authorization]: ../topics/authorization.md#device-authorization-grant
[GraphQL API]: ./graphql.md
[Internal GraphQL API]: ../development/graphql.md
[Admin API]: ../topics/admin-api.md
[Synapse admin API]: https://element-hq.github.io/synapse/latest/usage/administration/admin_api/index.html
[OpenID Connect Core 1.0]: https://openid.net/specs/openid-connect-core-1_0.html
[MSC2967]: https://github.com/matrix-org/matrix-spec-proposals/pull/2967
270 changes: 270 additions & 0 deletions docs/topics/admin-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
# Admin API

MAS provides a REST-like API for administrators to manage the service.
This API is intended to build tools on top of MAS, and is only available to administrators.

> **Note:** This Admin API is now the correct way for external tools to interact with MAS. External access to the [Internal GraphQL API](../development/graphql.md) is deprecated and will be removed in a future release.

## Enabling the API

The API isn't exposed by default, and must be added to either a public or a private HTTP listener.
It is considered safe to expose the API to the public, as access to it is gated by the `urn:mas:admin` scope.

To enable the API, tweak the [`http.listeners`](../reference/configuration.md#httplisteners) configuration section to add the `adminapi` resource:

```yaml
http:
listeners:
- name: web
resources:
# Other public resources
- name: discovery
# …
- name: adminapi
binds:
- address: "[::]:8080"
# or to a separate, internal listener:
- name: internal
resources:
# Other internal resources
- name: health
- name: prometheus
# …
- name: adminapi
binds:
- host: localhost
port: 8081
```

## Reference documentation

The API is documented using the [OpenAPI specification](https://spec.openapis.org/oas/v3.1.0).
The API schema is available [here](../api/spec.json).
This schema can be viewed in tools like Swagger UI, available [here](../api/).

If admin API is enabled, MAS will also serve the specification at `/api/spec.json`, with a Swagger UI available at `/api/doc/`.

## Authentication

All requests to the admin API are gated using access tokens obtained using OAuth 2.0 grants.
They must have the [`urn:mas:admin`](../reference/scopes.md#urnmasadmin) scope.

### User-interactive tools

If the intent is to build admin tools where the administrator logs in themselves, interactive grants like the [authorization code] grant or the [device authorization] grant should be used.

In this case, whether the user can request admin access or not is defined by the `can_request_admin` attribute of the user.

To try it out in Swagger UI, a client can be defined statically in the configuration file like this:

```yaml
clients:
- client_id: 01J44Q10GR4AMTFZEEF936DTCM
# For the authorization_code grant, Swagger UI uses the client_secret_post authentication method
client_auth_method: client_secret_post
client_secret: wie9oh2EekeeDeithei9Eipaeh2sohte
redirect_uris:
# The Swagger UI callback in the hosted documentation
- https://matrix-org.github.io/matrix-authentication-service/api/oauth2-redirect.html
# The Swagger UI callback hosted by the service
- https://mas.example.com/api/doc/oauth2-redirect
```

Then, in Swagger UI, click on the "Authorize" button.
In the modal, enter the client ID and client secret **in the `authorizationCode` section**, select the `urn:mas:admin` scope and click on the "Authorize" button.

### Automated tools

If the intent is to build tools that are not meant to be used by humans, the client credentials grant should be used.

In this case, the client must be listed in the [`policy.data.admin_clients`](../reference/configuration.md#policy) configuration option.

```yaml
policy:
data:
admin_clients:
- 01J44QC8BCY7FCFM7WGHQGKMTJ
```

To try it out in Swagger UI, a client can be defined statically in the configuration file like this:

```yaml
clients:
- client_id: 01J44QC8BCY7FCFM7WGHQGKMTJ
# For the client_credentials grant, Swagger UI uses the client_secret_basic authentication method
client_auth_method: client_secret_basic
client_secret: eequie6Oth4Ip2InahT5zuQu8OuPohLi
```

Then, in Swagger UI, click on the "Authorize" button.
In the modal, enter the client ID and client secret **in the `clientCredentials` section**, select the `urn:mas:admin` scope and click on the "Authorize" button.


## General API shape

The API takes inspiration from the [JSON API](https://jsonapi.org/) specification for its request and response shapes.

### Single resource

When querying a single resource, the response is generally shaped like this:

```json
{
"data": {
"type": "type-of-the-resource",
"id": "unique-id-for-the-resource",
"attributes": {
"some-attribute": "some-value"
},
"links": {
"self": "/api/admin/v1/type-of-the-resource/unique-id-for-the-resource"
}
},
"links": {
"self": "/api/admin/v1/type-of-the-resource/unique-id-for-the-resource"
}
}
```

### List of resources

When querying a list of resources, the response is generally shaped like this:

```json
{
"meta": {
"count": 42
},
"data": [
{
"type": "type-of-the-resource",
"id": "unique-id-for-the-resource",
"attributes": {
"some-attribute": "some-value"
},
"links": {
"self": "/api/admin/v1/type-of-the-resource/unique-id-for-the-resource"
}
},
{ "...": "..." },
{ "...": "..." }
],
"links": {
"self": "/api/admin/v1/type-of-the-resource?page[first]=10&page[after]=some-id",
"first": "/api/admin/v1/type-of-the-resource?page[first]=10",
"last": "/api/admin/v1/type-of-the-resource?page[last]=10",
"next": "/api/admin/v1/type-of-the-resource?page[first]=10&page[after]=some-id",
"prev": "/api/admin/v1/type-of-the-resource?page[last]=10&page[before]=some-id"
}
}
```

The `meta` will have the total number of items in it, and the `links` object contains the links to the next and previous pages, if any.

Pagination is cursor-based, where the ID of items is used as the cursor.
Resources can be paginated forwards using the `page[after]` and `page[first]` parameters, and backwards using the `page[before]` and `page[last]` parameters.

### Error responses

Error responses will use a 4xx or 5xx status code, with the following shape:

```json
{
"errors": [
{
"title": "Error title"
}
]
}
```

Well-known error codes are not yet specified.

## Example

With the following configuration:

```yaml
clients:
- client_id: 01J44RKQYM4G3TNVANTMTDYTX6
client_auth_method: client_secret_basic
client_secret: phoo8ahneir3ohY2eigh4xuu6Oodaewi

policy:
data:
admin_clients:
- 01J44RKQYM4G3TNVANTMTDYTX6
```

`curl` example to list the users that are not locked and have the `can_request_admin` flag set to `true`:

```bash
CLIENT_ID=01J44RKQYM4G3TNVANTMTDYTX6
CLIENT_SECRET=phoo8ahneir3ohY2eigh4xuu6Oodaewi

# Get an access token
curl \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=client_credentials&scope=urn:mas:admin" \
https://mas.example.com/oauth2/token \
| jq -r '.access_token' \
| read -r ACCESS_TOKEN

# List users (The -g flag prevents curl from interpreting the brackets in the URL)
curl \
-g \
-H "Authorization: Bearer $ACCESS_TOKEN" \
'https://mas.example.com/api/admin/v1/users?filter[can_request_admin]=true&filter[status]=active&page[first]=100' \
| jq
```

<details>
<summary>
Sample output
</summary>

```json
{
"meta": {
"count": 2
},
"data": [
{
"type": "user",
"id": "01J2KDPHTZYW3TAT1SKVAD63SQ",
"attributes": {
"username": "kilgore-trout",
"created_at": "2024-07-12T12:11:46.911578Z",
"locked_at": null,
"can_request_admin": true
},
"links": {
"self": "/api/admin/v1/users/01J2KDPHTZYW3TAT1SKVAD63SQ"
}
},
{
"type": "user",
"id": "01J3G5W8MRMBJ93ZYEGX2BN6NK",
"attributes": {
"username": "quentin",
"created_at": "2024-07-23T16:13:04.024378Z",
"locked_at": null,
"can_request_admin": true
},
"links": {
"self": "/api/admin/v1/users/01J3G5W8MRMBJ93ZYEGX2BN6NK"
}
}
],
"links": {
"self": "/api/admin/v1/users?filter[can_request_admin]=true&filter[status]=active&page[first]=100",
"first": "/api/admin/v1/users?filter[can_request_admin]=true&filter[status]=active&page[first]=100",
"last": "/api/admin/v1/users?filter[can_request_admin]=true&filter[status]=active&page[last]=100"
}
}
```

</details>

[authorization code]: ../topics/authorization.md#authorization-code-grant
[device authorization]: ../topics/authorization.md#device-authorization-grant
Loading
Loading