mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-08 10:27:08 +02:00
API v1 no longer mints short-lived JWT access tokens. Clients send the grant secret from POST /auth/login or /auth/setup as `Authorization: Bearer` on every request. Every request already looked the grant up in the database, so the JWT gave no speed or revocation benefit and only added a refresh loop, which early client authors pushed back on. The grant already is an API key: one per client sign-in, scoped and revocable. Revocation is now immediate on every node; the contract promises "within one minute". Removed: POST /auth/token, the grantAuth scheme, the TokenRequest and AccessToken schemas, the token_expired problem code, the API v1 JWT signer and its signing key, the grant liveness cache, and PropertyRepository.PutIfAbsent. ResolveGrant is now Authenticate. Short-lived tokens return later only as narrow media tokens for ?access_token= on media URLs, together with the media endpoints. Signed-off-by: Deluan <deluan@navidrome.org>
1056 lines
No EOL
32 KiB
JSON
1056 lines
No EOL
32 KiB
JSON
{
|
|
"openapi": "3.0.3",
|
|
"info": {
|
|
"title": "Navidrome API",
|
|
"version": "1.0.0",
|
|
"description": "Navidrome API v1. Spec-first, additive within v1. Clients discover implemented\ncapability modules through `GET /capabilities` and never sniff versions.\n\nEnums are open: new values may be added to any enum within v1. Clients must\naccept values they do not recognise instead of failing.\n\nEvery operation declares `x-stability-level`: `alpha` operations may change or\ndisappear without notice, `beta` and `stable` operations only change additively.\nA level is only ever raised, never lowered.\n\n`HEAD` is accepted wherever `GET` is. A `405` response lists the allowed methods\nin its `Allow` header.\n\nOperations that need a grant declare `security: [{bearerAuth: []}]` and the scope they need in\n`x-scope` (OpenAPI 3.0 does not allow scopes on bearer schemes). Clients send the grant secret as\n`Authorization: Bearer \u003csecret\u003e`. A revoked grant stops working within one minute at most.\n",
|
|
"license": {
|
|
"name": "GPL-3.0",
|
|
"url": "https://www.gnu.org/licenses/gpl-3.0.html"
|
|
}
|
|
},
|
|
"servers": [
|
|
{
|
|
"url": "/api/v1"
|
|
}
|
|
],
|
|
"tags": [
|
|
{
|
|
"name": "server",
|
|
"description": "Server discovery and the published OpenAPI document."
|
|
},
|
|
{
|
|
"name": "auth",
|
|
"description": "Grants and login methods."
|
|
}
|
|
],
|
|
"paths": {
|
|
"/server": {
|
|
"get": {
|
|
"operationId": "getServerInfo",
|
|
"x-module": "core",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"server"
|
|
],
|
|
"security": [],
|
|
"summary": "Describe the server",
|
|
"description": "Returns the public server description. No authentication required.\nCapability modules are listed by `GET /capabilities`.\n",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Server description.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/ServerInfo"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"500": {
|
|
"$ref": "#/components/responses/InternalError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/capabilities": {
|
|
"get": {
|
|
"operationId": "getCapabilities",
|
|
"x-module": "core",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"server"
|
|
],
|
|
"summary": "List implemented capability modules",
|
|
"description": "The capability modules this server implements. Any valid grant may read it, whatever its scopes.",
|
|
"security": [
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Implemented modules.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Capabilities"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"$ref": "#/components/responses/Unauthorized"
|
|
},
|
|
"500": {
|
|
"$ref": "#/components/responses/InternalError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/openapi.json": {
|
|
"get": {
|
|
"operationId": "getOpenAPISpecJSON",
|
|
"x-module": "core",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"server"
|
|
],
|
|
"security": [],
|
|
"summary": "Get the OpenAPI document (JSON)",
|
|
"description": "The bundled OpenAPI document of the running server version. Supports ETag revalidation.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "The OpenAPI document.",
|
|
"headers": {
|
|
"ETag": {
|
|
"$ref": "#/components/headers/ETag"
|
|
}
|
|
},
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"type": "object",
|
|
"description": "OpenAPI 3.0 document."
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"304": {
|
|
"$ref": "#/components/responses/NotModified"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/auth/grants": {
|
|
"get": {
|
|
"operationId": "listGrants",
|
|
"x-module": "core",
|
|
"x-scope": "read",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "List my grants",
|
|
"description": "The caller's grants, most recently used first. Grants idle long enough to have expired are not listed.",
|
|
"security": [
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"$ref": "#/components/parameters/offset"
|
|
},
|
|
{
|
|
"$ref": "#/components/parameters/limit"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "A page of grants.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/GrantList"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"$ref": "#/components/responses/BadRequest"
|
|
},
|
|
"401": {
|
|
"$ref": "#/components/responses/Unauthorized"
|
|
},
|
|
"403": {
|
|
"$ref": "#/components/responses/Forbidden"
|
|
},
|
|
"500": {
|
|
"$ref": "#/components/responses/InternalError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/openapi.yaml": {
|
|
"get": {
|
|
"operationId": "getOpenAPISpecYAML",
|
|
"x-module": "core",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"server"
|
|
],
|
|
"security": [],
|
|
"summary": "Get the OpenAPI document (YAML)",
|
|
"description": "The bundled OpenAPI document of the running server version. Supports ETag revalidation.",
|
|
"responses": {
|
|
"200": {
|
|
"description": "The OpenAPI document.",
|
|
"headers": {
|
|
"ETag": {
|
|
"$ref": "#/components/headers/ETag"
|
|
}
|
|
},
|
|
"content": {
|
|
"application/yaml": {
|
|
"schema": {
|
|
"type": "object",
|
|
"description": "OpenAPI 3.0 document."
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"304": {
|
|
"$ref": "#/components/responses/NotModified"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/auth/grants/{id}": {
|
|
"delete": {
|
|
"operationId": "revokeGrant",
|
|
"x-module": "core",
|
|
"x-scope": "read",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Revoke one of my grants",
|
|
"description": "Revokes the grant; requests with its secret fail from then on. Another user's grant id answers 404.",
|
|
"security": [
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"parameters": [
|
|
{
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true,
|
|
"description": "Grant id.",
|
|
"schema": {
|
|
"type": "string",
|
|
"maxLength": 64
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"204": {
|
|
"description": "Revoked."
|
|
},
|
|
"400": {
|
|
"$ref": "#/components/responses/BadRequest"
|
|
},
|
|
"401": {
|
|
"$ref": "#/components/responses/Unauthorized"
|
|
},
|
|
"403": {
|
|
"$ref": "#/components/responses/Forbidden"
|
|
},
|
|
"404": {
|
|
"$ref": "#/components/responses/NotFound"
|
|
},
|
|
"500": {
|
|
"$ref": "#/components/responses/InternalError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/auth/logout": {
|
|
"post": {
|
|
"operationId": "logout",
|
|
"x-module": "core",
|
|
"x-scope": "read",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Log out",
|
|
"description": "Revokes the grant that made this request.",
|
|
"security": [
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Logged out.",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/LogoutResponse"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"401": {
|
|
"$ref": "#/components/responses/Unauthorized"
|
|
},
|
|
"403": {
|
|
"$ref": "#/components/responses/Forbidden"
|
|
},
|
|
"500": {
|
|
"$ref": "#/components/responses/InternalError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/auth/login": {
|
|
"post": {
|
|
"operationId": "login",
|
|
"x-module": "password",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Log in with a password",
|
|
"description": "Checks the username and password and returns a new grant. Unknown user and wrong password fail the same way.",
|
|
"security": [],
|
|
"requestBody": {
|
|
"description": "The credentials and a description of the client.",
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/CredentialsRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "The new grant.",
|
|
"headers": {
|
|
"Cache-Control": {
|
|
"$ref": "#/components/headers/CacheControlNoStore"
|
|
}
|
|
},
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/GrantCreated"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"$ref": "#/components/responses/BadRequest"
|
|
},
|
|
"401": {
|
|
"$ref": "#/components/responses/Unauthorized"
|
|
},
|
|
"413": {
|
|
"$ref": "#/components/responses/PayloadTooLarge"
|
|
},
|
|
"429": {
|
|
"$ref": "#/components/responses/TooManyRequests"
|
|
},
|
|
"500": {
|
|
"$ref": "#/components/responses/InternalError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/auth/setup": {
|
|
"post": {
|
|
"operationId": "setupFirstAdmin",
|
|
"x-module": "password",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Create the first admin",
|
|
"description": "Creates the first administrator while `setupRequired` is true and returns a grant for it. Answers 409 `setup_complete` once any user exists. A server with no setup step always answers 409.",
|
|
"security": [],
|
|
"requestBody": {
|
|
"description": "The credentials and a description of the client.",
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/CredentialsRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"201": {
|
|
"description": "The admin was created.",
|
|
"headers": {
|
|
"Cache-Control": {
|
|
"$ref": "#/components/headers/CacheControlNoStore"
|
|
}
|
|
},
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/GrantCreated"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"400": {
|
|
"$ref": "#/components/responses/BadRequest"
|
|
},
|
|
"409": {
|
|
"$ref": "#/components/responses/Conflict"
|
|
},
|
|
"413": {
|
|
"$ref": "#/components/responses/PayloadTooLarge"
|
|
},
|
|
"429": {
|
|
"$ref": "#/components/responses/TooManyRequests"
|
|
},
|
|
"500": {
|
|
"$ref": "#/components/responses/InternalError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/auth/password": {
|
|
"post": {
|
|
"operationId": "changePassword",
|
|
"x-module": "password",
|
|
"x-scope": "password",
|
|
"x-stability-level": "alpha",
|
|
"tags": [
|
|
"auth"
|
|
],
|
|
"summary": "Change my password",
|
|
"description": "Changes the caller's password. By default every other grant of the user is revoked; the calling grant survives. On Navidrome the change also ends the user's sessions on its other APIs, regardless of `revokeOtherGrants`, which only covers API v1 grants. Answers 409 `password_managed_externally` when the password is not stored by this server.",
|
|
"security": [
|
|
{
|
|
"bearerAuth": []
|
|
}
|
|
],
|
|
"requestBody": {
|
|
"description": "The current and the new password.",
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/PasswordChangeRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"204": {
|
|
"description": "Password changed."
|
|
},
|
|
"400": {
|
|
"$ref": "#/components/responses/BadRequest"
|
|
},
|
|
"401": {
|
|
"$ref": "#/components/responses/Unauthorized"
|
|
},
|
|
"403": {
|
|
"$ref": "#/components/responses/Forbidden"
|
|
},
|
|
"409": {
|
|
"$ref": "#/components/responses/Conflict"
|
|
},
|
|
"413": {
|
|
"$ref": "#/components/responses/PayloadTooLarge"
|
|
},
|
|
"429": {
|
|
"$ref": "#/components/responses/TooManyRequests"
|
|
},
|
|
"500": {
|
|
"$ref": "#/components/responses/InternalError"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"components": {
|
|
"securitySchemes": {
|
|
"bearerAuth": {
|
|
"type": "http",
|
|
"scheme": "bearer",
|
|
"description": "Grant secret from a login method (`POST /auth/login`, `POST /auth/setup`). Opaque. The required scope is in each operation's `x-scope`."
|
|
}
|
|
},
|
|
"schemas": {
|
|
"ServerInfo": {
|
|
"type": "object",
|
|
"description": "Public server description. Everything an add-server screen needs before login.",
|
|
"required": [
|
|
"name",
|
|
"serverVersion",
|
|
"specVersion",
|
|
"setupRequired",
|
|
"loginMethods"
|
|
],
|
|
"properties": {
|
|
"name": {
|
|
"type": "string",
|
|
"description": "Human-readable server product name."
|
|
},
|
|
"serverVersion": {
|
|
"type": "string",
|
|
"description": "Version of the running server build."
|
|
},
|
|
"specVersion": {
|
|
"type": "string",
|
|
"description": "Version of the OpenAPI document this server implements."
|
|
},
|
|
"setupRequired": {
|
|
"type": "boolean",
|
|
"description": "True until the first admin user has been created."
|
|
},
|
|
"loginMethods": {
|
|
"$ref": "#/components/schemas/LoginMethods"
|
|
}
|
|
}
|
|
},
|
|
"LoginMethods": {
|
|
"type": "object",
|
|
"description": "Login methods this server accepts, keyed by method. A missing key means the method is not offered.\nKeys are optional on purpose: discovery is read by clients of any version against servers of any\nversion, so new methods are added as new optional keys. Clients ignore keys they do not know.\n",
|
|
"properties": {
|
|
"password": {
|
|
"$ref": "#/components/schemas/PasswordLoginMethod"
|
|
}
|
|
}
|
|
},
|
|
"PasswordLoginMethod": {
|
|
"type": "object",
|
|
"description": "Username and password login (`POST /auth/login`). No settings yet."
|
|
},
|
|
"Problem": {
|
|
"type": "object",
|
|
"description": "RFC 9457 problem details, returned for every 4xx and 5xx response.",
|
|
"required": [
|
|
"title",
|
|
"status",
|
|
"code"
|
|
],
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"description": "URI reference identifying the problem type. Omitted while the problem carries no semantics\nbeyond its HTTP status code, which RFC 9457 defines as `about:blank`. Problems with their\nown semantics get their own URI; switch on `code` instead.\n"
|
|
},
|
|
"title": {
|
|
"type": "string",
|
|
"description": "Short human-readable summary, the same for all occurrences of this problem type."
|
|
},
|
|
"status": {
|
|
"type": "integer",
|
|
"description": "HTTP status code of this response."
|
|
},
|
|
"detail": {
|
|
"type": "string",
|
|
"description": "Human-readable explanation specific to this occurrence. Omitted unless the server marked the text as safe to show clients."
|
|
},
|
|
"code": {
|
|
"type": "string",
|
|
"description": "Machine-readable error code, and the value clients switch on. New codes may be added.",
|
|
"enum": [
|
|
"validation",
|
|
"unauthorized",
|
|
"forbidden",
|
|
"insufficient_scope",
|
|
"not_found",
|
|
"method_not_allowed",
|
|
"setup_complete",
|
|
"password_managed_externally",
|
|
"payload_too_large",
|
|
"rate_limited",
|
|
"unavailable",
|
|
"internal"
|
|
]
|
|
},
|
|
"referenceId": {
|
|
"type": "string",
|
|
"description": "Present on internal errors. Quote it when reporting a problem; it tags the server's log lines for this request."
|
|
},
|
|
"errors": {
|
|
"type": "array",
|
|
"description": "Per-field failures. Present only when `code` is `validation`.",
|
|
"items": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"ValidationError": {
|
|
"type": "object",
|
|
"description": "One field-level validation failure.",
|
|
"required": [
|
|
"field",
|
|
"message"
|
|
],
|
|
"properties": {
|
|
"field": {
|
|
"type": "string",
|
|
"description": "Name of the offending query parameter, path parameter, or body field (dotted for nested)."
|
|
},
|
|
"message": {
|
|
"type": "string",
|
|
"description": "Why the value was rejected."
|
|
}
|
|
}
|
|
},
|
|
"Capabilities": {
|
|
"type": "object",
|
|
"description": "Capability modules this server implements, keyed by module. Keys are optional; a missing key means the\nmodule is not implemented. New modules are added as new optional keys. These are server facts, not what\nthe calling grant may use.\n",
|
|
"properties": {
|
|
"core": {
|
|
"$ref": "#/components/schemas/CoreCapability"
|
|
},
|
|
"password": {
|
|
"$ref": "#/components/schemas/PasswordCapability"
|
|
}
|
|
}
|
|
},
|
|
"CoreCapability": {
|
|
"type": "object",
|
|
"description": "The mandatory core module.",
|
|
"required": [
|
|
"version"
|
|
],
|
|
"properties": {
|
|
"version": {
|
|
"type": "integer",
|
|
"description": "Module version. Bumped only on semantic change."
|
|
}
|
|
}
|
|
},
|
|
"PasswordCapability": {
|
|
"type": "object",
|
|
"description": "The password login module (login, first-admin setup, password change).",
|
|
"required": [
|
|
"version"
|
|
],
|
|
"properties": {
|
|
"version": {
|
|
"type": "integer",
|
|
"description": "Module version. Bumped only on semantic change."
|
|
}
|
|
}
|
|
},
|
|
"GrantList": {
|
|
"type": "object",
|
|
"description": "A page of the caller's grants.",
|
|
"required": [
|
|
"items",
|
|
"total",
|
|
"offset",
|
|
"limit"
|
|
],
|
|
"properties": {
|
|
"items": {
|
|
"type": "array",
|
|
"description": "Grants on this page, by last use, most recent first; never-used grants last.",
|
|
"items": {
|
|
"$ref": "#/components/schemas/Grant"
|
|
}
|
|
},
|
|
"total": {
|
|
"type": "integer",
|
|
"description": "Total number of grants."
|
|
},
|
|
"offset": {
|
|
"type": "integer",
|
|
"description": "Zero-based index of the first returned item."
|
|
},
|
|
"limit": {
|
|
"type": "integer",
|
|
"description": "Maximum number of items in this page."
|
|
}
|
|
}
|
|
},
|
|
"LogoutResponse": {
|
|
"type": "object",
|
|
"description": "Result of a logout.",
|
|
"required": [
|
|
"logoutUrl"
|
|
],
|
|
"properties": {
|
|
"logoutUrl": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "Where to send the browser to finish logging out of an external provider. Null when there is nothing more to do."
|
|
}
|
|
}
|
|
},
|
|
"CredentialsRequest": {
|
|
"type": "object",
|
|
"description": "Username, password and client description for a login or first-admin setup.",
|
|
"required": [
|
|
"username",
|
|
"password",
|
|
"client"
|
|
],
|
|
"properties": {
|
|
"username": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 255,
|
|
"description": "Login name."
|
|
},
|
|
"password": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 1024,
|
|
"description": "Password."
|
|
},
|
|
"client": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 64,
|
|
"description": "Name of the client app."
|
|
},
|
|
"clientVersion": {
|
|
"type": "string",
|
|
"maxLength": 32,
|
|
"description": "Version of the client app."
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 64,
|
|
"description": "Label for this grant. Defaults to `client`."
|
|
},
|
|
"scopes": {
|
|
"type": "array",
|
|
"maxItems": 32,
|
|
"description": "Scopes the grant may hold. Omit for `all`.",
|
|
"items": {
|
|
"$ref": "#/components/schemas/ScopeRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"GrantCreated": {
|
|
"type": "object",
|
|
"description": "Returned by every login method. The secret is shown only here; store it and never parse it.",
|
|
"required": [
|
|
"secret",
|
|
"grant",
|
|
"user"
|
|
],
|
|
"properties": {
|
|
"secret": {
|
|
"type": "string",
|
|
"maxLength": 512,
|
|
"description": "Opaque grant secret. Send it as `Authorization: Bearer \u003csecret\u003e`."
|
|
},
|
|
"grant": {
|
|
"description": "The new grant.",
|
|
"allOf": [
|
|
{
|
|
"$ref": "#/components/schemas/Grant"
|
|
}
|
|
]
|
|
},
|
|
"user": {
|
|
"description": "The user the grant belongs to.",
|
|
"allOf": [
|
|
{
|
|
"$ref": "#/components/schemas/AuthUser"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"PasswordChangeRequest": {
|
|
"type": "object",
|
|
"description": "Change the caller's own password.",
|
|
"required": [
|
|
"currentPassword",
|
|
"newPassword"
|
|
],
|
|
"properties": {
|
|
"currentPassword": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 1024,
|
|
"description": "The current password."
|
|
},
|
|
"newPassword": {
|
|
"type": "string",
|
|
"minLength": 1,
|
|
"maxLength": 1024,
|
|
"description": "The new password."
|
|
},
|
|
"revokeOtherGrants": {
|
|
"type": "boolean",
|
|
"default": true,
|
|
"description": "Revoke every other grant of the user. The calling grant always survives. Default true."
|
|
}
|
|
}
|
|
},
|
|
"Grant": {
|
|
"type": "object",
|
|
"description": "A long-lived grant held by one client of one user.",
|
|
"required": [
|
|
"id",
|
|
"name",
|
|
"client",
|
|
"clientVersion",
|
|
"scopes",
|
|
"provider",
|
|
"createdAt",
|
|
"lastUsedAt",
|
|
"lastUsedIp",
|
|
"current"
|
|
],
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"description": "Grant id."
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"description": "Label shown to the user."
|
|
},
|
|
"client": {
|
|
"type": "string",
|
|
"description": "Name of the client app that holds the grant."
|
|
},
|
|
"clientVersion": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "Version of the client app, when it sent one."
|
|
},
|
|
"scopes": {
|
|
"type": "array",
|
|
"description": "Scopes this grant carries.",
|
|
"items": {
|
|
"$ref": "#/components/schemas/Scope"
|
|
}
|
|
},
|
|
"provider": {
|
|
"type": "string",
|
|
"description": "How the grant was created, for example `password` or `setup`. Free-form; new values may appear."
|
|
},
|
|
"createdAt": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"description": "When the grant was created."
|
|
},
|
|
"lastUsedAt": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"nullable": true,
|
|
"description": "When the grant was last used, at a coarse granularity. Null until first use."
|
|
},
|
|
"lastUsedIp": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "Client IP of the last use. Null until first use."
|
|
},
|
|
"current": {
|
|
"type": "boolean",
|
|
"description": "True for the grant that made this request."
|
|
}
|
|
}
|
|
},
|
|
"Scope": {
|
|
"type": "string",
|
|
"description": "A permission scope. Scopes mirror capability modules; `x:write` includes `x`. `all` appears only on\ngrants and means every scope the user is entitled to, now and in future releases. New scopes may be added.\n",
|
|
"enum": [
|
|
"all",
|
|
"read",
|
|
"password"
|
|
]
|
|
},
|
|
"ScopeRequest": {
|
|
"type": "string",
|
|
"description": "A requested scope. Scopes the server does not know are dropped, not rejected, so newer clients keep working.",
|
|
"pattern": "^[a-z][a-z-]*(:write)?$",
|
|
"maxLength": 64
|
|
},
|
|
"AuthUser": {
|
|
"type": "object",
|
|
"description": "The user a grant belongs to.",
|
|
"required": [
|
|
"id",
|
|
"userName",
|
|
"name",
|
|
"isAdmin",
|
|
"passwordChangeable"
|
|
],
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"description": "User id."
|
|
},
|
|
"userName": {
|
|
"type": "string",
|
|
"description": "Login name."
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"description": "Display name."
|
|
},
|
|
"isAdmin": {
|
|
"type": "boolean",
|
|
"description": "Whether the user is an administrator."
|
|
},
|
|
"passwordChangeable": {
|
|
"type": "boolean",
|
|
"description": "Whether `POST /auth/password` can change this user's password. Clients hide \"change password\" when false."
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"InternalError": {
|
|
"description": "Unexpected server failure. Details are in the server log.",
|
|
"content": {
|
|
"application/problem+json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Problem"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"Unauthorized": {
|
|
"description": "Missing, invalid, or expired credentials.",
|
|
"headers": {
|
|
"WWW-Authenticate": {
|
|
"$ref": "#/components/headers/WWWAuthenticate"
|
|
}
|
|
},
|
|
"content": {
|
|
"application/problem+json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Problem"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"NotModified": {
|
|
"description": "Not modified.",
|
|
"headers": {
|
|
"ETag": {
|
|
"$ref": "#/components/headers/ETag"
|
|
}
|
|
}
|
|
},
|
|
"BadRequest": {
|
|
"description": "The request is malformed or fails validation.",
|
|
"content": {
|
|
"application/problem+json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Problem"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"Forbidden": {
|
|
"description": "The caller is authenticated but not allowed to do this.",
|
|
"headers": {
|
|
"WWW-Authenticate": {
|
|
"$ref": "#/components/headers/WWWAuthenticate"
|
|
}
|
|
},
|
|
"content": {
|
|
"application/problem+json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Problem"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"NotFound": {
|
|
"description": "No such resource or endpoint.",
|
|
"content": {
|
|
"application/problem+json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Problem"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"PayloadTooLarge": {
|
|
"description": "The request body is too large (`payload_too_large`).",
|
|
"content": {
|
|
"application/problem+json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Problem"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"TooManyRequests": {
|
|
"description": "Rate limited (`rate_limited`). Retry after the `Retry-After` seconds.",
|
|
"headers": {
|
|
"Retry-After": {
|
|
"description": "Seconds to wait before retrying.",
|
|
"schema": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"content": {
|
|
"application/problem+json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Problem"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"Conflict": {
|
|
"description": "The request conflicts with the server's state, for example `setup_complete` or `password_managed_externally`.",
|
|
"content": {
|
|
"application/problem+json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Problem"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"parameters": {
|
|
"offset": {
|
|
"name": "offset",
|
|
"in": "query",
|
|
"description": "Zero-based index of the first item to return.",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "integer",
|
|
"minimum": 0,
|
|
"default": 0
|
|
}
|
|
},
|
|
"limit": {
|
|
"name": "limit",
|
|
"in": "query",
|
|
"description": "Maximum number of items to return.",
|
|
"required": false,
|
|
"schema": {
|
|
"type": "integer",
|
|
"minimum": 1,
|
|
"maximum": 2000,
|
|
"default": 100
|
|
}
|
|
}
|
|
},
|
|
"headers": {
|
|
"WWWAuthenticate": {
|
|
"description": "RFC 6750 Bearer challenge, for example `Bearer error=\"insufficient_scope\", scope=\"read\"`.",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"ETag": {
|
|
"description": "Entity tag for `If-None-Match` revalidation.",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"CacheControlNoStore": {
|
|
"description": "Always `no-store`, because the response carries a secret.",
|
|
"schema": {
|
|
"type": "string",
|
|
"enum": [
|
|
"no-store"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |