diff --git a/docs/README.md b/docs/README.md index 06f458c44..5cd4553e2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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/) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 41b1be973..749c40b1f 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -1,4 +1,4 @@ -# Summary +# Summary # Introduction @@ -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) @@ -40,6 +41,7 @@ - [Contributing](./development/contributing.md) - [Architecture](./development/architecture.md) - [Database](./development/database.md) +- [Internal GraphQL API](./development/graphql.md) --- diff --git a/docs/reference/graphql.md b/docs/development/graphql.md similarity index 66% rename from docs/reference/graphql.md rename to docs/development/graphql.md index ed700df15..1b703ef1b 100644 --- a/docs/reference/graphql.md +++ b/docs/development/graphql.md @@ -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:///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. @@ -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 diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 466807997..c84b09c2e 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -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: diff --git a/docs/reference/scopes.md b/docs/reference/scopes.md index c7a230835..78a261c91 100644 --- a/docs/reference/scopes.md +++ b/docs/reference/scopes.md @@ -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 @@ -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: @@ -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 diff --git a/docs/topics/admin-api.md b/docs/topics/admin-api.md new file mode 100644 index 000000000..7e6b703a8 --- /dev/null +++ b/docs/topics/admin-api.md @@ -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 +``` + +
+ +Sample output + + +```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" + } +} +``` + +
+ +[authorization code]: ../topics/authorization.md#authorization-code-grant +[device authorization]: ../topics/authorization.md#device-authorization-grant diff --git a/docs/topics/authorization.md b/docs/topics/authorization.md index b5527412d..d2d7ab29c 100644 --- a/docs/topics/authorization.md +++ b/docs/topics/authorization.md @@ -86,11 +86,11 @@ the API can be requested by a session which has the [`urn:mas:graphql:*`] and th MAS supports a few different authorization grants for OAuth 2.0 sessions. Whilst this section won't go into the technical details of how those grants work, it's important to understand what they are and what they are used for. -| Grant type | Entity | User interaction | Matrix C-S API | Synapse admin API |  MAS GraphQL API | -| --------------------------------------------------- | ------ | ---------------- | -------------- | ----------------- | ---------------- | -| [Authorization code](#authorization-code-grant) | User | Same device | Yes | Yes | Yes | -| [Device authorization](#device-authorization-grant) | User | Other device | Yes | Yes | Yes | -| [Client credentials](#client-credentials-grant) | Client | None | No | No[^admin] | No | +| Grant type | Entity | User interaction | Matrix C-S API | Synapse Admin API | MAS Admin API | MAS Internal GraphQL API | +| --------------------------------------------------- | ------ | ---------------- | -------------- | ----------------- | ------------- | ------------------------ | +| [Authorization code](#authorization-code-grant) | User | Same device | Yes | Yes | Yes | Yes | +| [Device authorization](#device-authorization-grant) | User | Other device | Yes | Yes | Yes | Yes | +| [Client credentials](#client-credentials-grant) | Client | None | No | No[^admin] | Yes | Yes | [^admin]: The Synapse admin API doesn't strictly require a user, but Synapse doesn't support client-only sessions yet. In the future, it will be possible to leverage the client credentials grant to access the Synapse admin API.