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 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
configuration section to add the adminapi
resource:
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. The API schema is available here. This schema can be viewed in tools like Swagger UI, available here.
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
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:
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
configuration option.
policy:
data:
admin_clients:
- 01J44QC8BCY7FCFM7WGHQGKMTJ
To try it out in Swagger UI, a client can be defined statically in the configuration file like this:
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 specification for its request and response shapes.
Single resource
When querying a single resource, the response is generally shaped like this:
{
"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:
{
"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:
{
"errors": [
{
"title": "Error title"
}
]
}
Well-known error codes are not yet specified.
Example
With the following configuration:
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
:
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
{
"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"
}
}