mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
264 lines
11 KiB
Plaintext
264 lines
11 KiB
Plaintext
---
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
title: Admin API keys
|
|
description: Admin API key objects and the operations that manage them.
|
|
---
|
|
|
|
import RouteHeader from '@/components/RouteHeader.astro';
|
|
|
|
An Admin API key is a long-lived credential for the [Admin API](/admin-api/). It authenticates as the account that created it, has its own [ACL](/admin-api/#acl-registry) set, and is honoured only on paths below `/v1/admin`.
|
|
|
|
Every operation on this page requires `admin_api_key:manage`. None records an Admin audit entry or reads the `X-Audit-Log-Reason` header. An operation that addresses one key returns 404 `ADMIN_API_KEY_NOT_FOUND` when any of these holds:
|
|
|
|
- No key has that identifier.
|
|
- Another account created the key.
|
|
- The key has expired.
|
|
|
|
:::caution[Key management is scoped to the creating account]
|
|
The wildcard ACL reaches no key created by another account, and such a key reports `ADMIN_API_KEY_NOT_FOUND`.
|
|
:::
|
|
|
|
## Admin API key object
|
|
|
|
A key is owned by the account that created it, and it stores its own ACL set. Narrowing the owner and narrowing the key are separate actions.
|
|
|
|
Fluxer checks both sets when a request presents a key. A request passes when all of these hold:
|
|
|
|
- The owning account holds `admin:authenticate` or `*`.
|
|
- The owning account holds the ACL the operation requires, or `*`.
|
|
- The key itself has that ACL, or `*`.
|
|
|
|
A key therefore never has wider permission than the account behind it.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| key_id | snowflake | The ID of the key |
|
|
| name | string | The name given to the key (1-100 characters) |
|
|
| acls<sup>1</sup> | array[string] | The [ACLs](/admin-api/#acl-registry) stored on the key |
|
|
| created_by_user_id<sup>2</sup> | snowflake | The ID of the account that created the key, and whose identity the key assumes |
|
|
| created_at | ISO8601 timestamp | Time the key was created |
|
|
| last_used_at<sup>3</sup> | ?ISO8601 timestamp | Time the key last authenticated a request, or null when it never has |
|
|
| expires_at<sup>4</sup> | ?ISO8601 timestamp | Time the key expires, or null when the key does not expire |
|
|
|
|
<sup>1</sup> Bounded at the size of the [ACL registry](/admin-api/#acl-registry). Every value written through this API is a registry member
|
|
|
|
<sup>2</sup> Never changes, so a key cannot be transferred to another account
|
|
|
|
<sup>3</sup> Written on every successful authentication, including when the authenticated operation is a read
|
|
|
|
<sup>4</sup> An expired key is absent from every listing and is reported as not found
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"key_id": "1501314428688998182",
|
|
"name": "moderation-tooling",
|
|
"acls": ["user:lookup", "report:view", "report:resolve"],
|
|
"created_by_user_id": "1489200013322551296",
|
|
"created_at": "2026-05-14T09:12:44.183000+00:00",
|
|
"last_used_at": "2026-06-02T18:40:11.902000+00:00",
|
|
"expires_at": null
|
|
}
|
|
```
|
|
|
|
## Admin API key creation object
|
|
|
|
Only [Create Admin API key](#create-admin-api-key) returns this object. It has the raw credential and omits `last_used_at` and `created_by_user_id`.
|
|
|
|
### Structure
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| key_id | snowflake | The ID of the key |
|
|
| key<sup>1</sup> | string | The raw credential the key authenticates with |
|
|
| name | string | The name given to the key (1-100 characters) |
|
|
| created_at | ISO8601 timestamp | Time the key was created |
|
|
| expires_at<sup>2</sup> | ?ISO8601 timestamp | Time the key expires, or null when the key does not expire |
|
|
| acls<sup>3</sup> | array[string] | The [ACLs](/admin-api/#acl-registry) stored on the key |
|
|
|
|
<sup>1</sup> Has the form `fa_<key_id>_<32 characters>` described by [token formats](/authentication/#token-formats), and is presented as `Admin <token>` in the `Authorization` header
|
|
|
|
<sup>2</sup> Derived from `expires_in_days` at the instant the key is created, and null when that field is omitted
|
|
|
|
<sup>3</sup> Reflects the stored set, so a value repeated in the request appears once
|
|
|
|
:::caution[The secret is returned once]
|
|
Fluxer stores the value as a password hash, so no later operation returns it and none rotates it. A key whose raw value is lost must be revoked and replaced.
|
|
:::
|
|
|
|
### Example
|
|
|
|
```json
|
|
{
|
|
"key_id": "1501314428688998182",
|
|
"key": "fa_1501314428688998182_7Qk2ZbW9xLmR4TnP0vAeJdCyHs6UgF1B",
|
|
"name": "moderation-tooling",
|
|
"created_at": "2026-05-14T09:12:44.183000+00:00",
|
|
"expires_at": "2026-08-12T09:12:44.183000+00:00",
|
|
"acls": ["user:lookup", "report:view"]
|
|
}
|
|
```
|
|
|
|
## List Admin API keys
|
|
|
|
<RouteHeader method="GET" path="/v1/admin/api-keys" />
|
|
|
|
Returns every [Admin API key](#admin-api-key-object) object the acting account created, as a bare JSON array with no ordering guarantee. Requires `admin_api_key:manage`.
|
|
|
|
A key past its expiry is never returned.
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200<sup>1</sup> | array[[Admin API key](#admin-api-key-object) object] | Keys were returned |
|
|
|
|
<sup>1</sup> An account that created no key receives an empty array
|
|
|
|
### Rate limit
|
|
|
|
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
|
|
|
|
## Create Admin API key
|
|
|
|
<RouteHeader method="POST" path="/v1/admin/api-keys" />
|
|
|
|
Creates a key and returns an [Admin API key creation](#admin-api-key-creation-object) object that has the raw credential, with HTTP 200, not 201. Requires `admin_api_key:manage`.
|
|
|
|
The acting credential must already have every value in `acls`, unless it has `*`. The key authenticates immediately, and its effective permission is the intersection described under [Admin API key object](#admin-api-key-object).
|
|
|
|
### JSON body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| name<sup>1</sup> | string | The name given to the key (1-100 characters) |
|
|
| expires_in_days?<sup>2</sup> | integer | The number of days until the key expires (1-365) |
|
|
| acls<sup>3</sup> | array[string] | The [ACLs](/admin-api/#acl-registry) stored on the key, each a registry value (at most 111) |
|
|
|
|
<sup>1</sup> A value that is empty after trimming is rejected, so whitespace alone is not a name
|
|
|
|
<sup>2</sup> The stored expiry is the request instant plus this many days. Omitting the field creates a key that does not expire
|
|
|
|
<sup>3</sup> An empty array produces a key that satisfies no operation. Fluxer compares `acls` against the presenting key's own ACLs, so a key cannot issue a broader key
|
|
|
|
Fluxer rejects the request with 403 `MISSING_ACL` on the first ungrantable value. A request that names several ungrantable ACLs reports only that one.
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [Admin API key creation](#admin-api-key-creation-object) object | Key was created |
|
|
| 400 | [error response](/admin-api/#error-response) | Body validation fails, including an `acls` value outside the [ACL registry](/admin-api/#acl-registry), returned as `INVALID_FORM_BODY` |
|
|
| 403 | [error response](/admin-api/#error-response) | Credential type is refused, `admin_api_key:manage` is absent, or `acls` names a value the acting credential does not have |
|
|
|
|
### Rate limit
|
|
|
|
30 requests per minute for each authenticated user, on the `admin:code:generation` bucket.
|
|
|
|
## Get Admin API key
|
|
|
|
<RouteHeader method="GET" path="/v1/admin/api-keys/{key_id}" />
|
|
|
|
Returns one [Admin API key](#admin-api-key-object) object. Requires `admin_api_key:manage`.
|
|
|
|
This operation never returns the raw credential.
|
|
|
|
### Path parameters
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| key_id | snowflake | The ID of the key |
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [Admin API key](#admin-api-key-object) object | Key was returned |
|
|
| 404 | [error response](/admin-api/#error-response) | `ADMIN_API_KEY_NOT_FOUND`, which also covers an expired key and a key created by another account |
|
|
|
|
### Rate limit
|
|
|
|
200 requests per minute for each authenticated user, on the `admin:lookup` bucket.
|
|
|
|
## Update Admin API key
|
|
|
|
<RouteHeader method="PATCH" path="/v1/admin/api-keys/{key_id}" />
|
|
|
|
Renames a key or replaces its ACL set, and returns the updated [Admin API key](#admin-api-key-object) object. Requires `admin_api_key:manage`.
|
|
|
|
Fluxer leaves an omitted field unchanged, and the supplied fields take effect on the key's next authenticated request. The acting credential must already have every value in a supplied `acls`, unless it has `*`. A key with an expiry keeps it.
|
|
|
|
:::caution[Supplying acls replaces the whole stored set]
|
|
An update never rotates the credential, and no field on this route changes the expiry, so revoking the key is the only way to disable it.
|
|
:::
|
|
|
|
### Path parameters
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| key_id | snowflake | The ID of the key |
|
|
|
|
### JSON body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| name?<sup>1</sup> | string | The replacement name for the key (1-100 characters) |
|
|
| acls?<sup>2</sup> | array[string] | The complete replacement set of [ACLs](/admin-api/#acl-registry), each a registry value (at most 111) |
|
|
|
|
<sup>1</sup> A value that is empty after trimming is rejected
|
|
|
|
<sup>2</sup> An empty array leaves the key with no ACLs, the narrowest state short of revocation. An empty request body is accepted and changes nothing
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | [Admin API key](#admin-api-key-object) object | Key was updated |
|
|
| 400 | [error response](/admin-api/#error-response) | Body validation fails, including an `acls` value outside the [ACL registry](/admin-api/#acl-registry), returned as `INVALID_FORM_BODY` |
|
|
| 403<sup>1</sup> | [error response](/admin-api/#error-response) | Credential type is refused, `admin_api_key:manage` is absent, or `acls` names a value the acting credential does not have |
|
|
| 404 | [error response](/admin-api/#error-response) | `ADMIN_API_KEY_NOT_FOUND`, which also covers an expired key and a key created by another account |
|
|
|
|
<sup>1</sup> The ownership check runs before the ACL grant check, so a key belonging to another account returns 404, not 403
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|
|
|
|
## Revoke Admin API key
|
|
|
|
<RouteHeader method="DELETE" path="/v1/admin/api-keys/{key_id}" />
|
|
|
|
Revokes an Admin API key and returns HTTP 200 with a response body. Requires `admin_api_key:manage`.
|
|
|
|
The credential stops authenticating on its next use. A request already in flight runs to completion.
|
|
|
|
:::danger[Revocation is permanent]
|
|
Every later request presenting that credential fails authentication, and no operation recovers the key ID or reissues the raw value.
|
|
:::
|
|
|
|
### Path parameters
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| key_id | snowflake | The ID of the key |
|
|
|
|
### Response body
|
|
|
|
| Field | Type | Description |
|
|
| --- | --- | --- |
|
|
| success | boolean | Always true |
|
|
|
|
### Response
|
|
|
|
| Status | Body | Condition |
|
|
| --- | --- | --- |
|
|
| 200 | response body | Key was revoked |
|
|
| 404 | [error response](/admin-api/#error-response) | `ADMIN_API_KEY_NOT_FOUND`, which also covers an expired key and a key created by another account |
|
|
|
|
### Rate limit
|
|
|
|
100 requests per minute for each authenticated user, on the `admin:user:modify` bucket.
|