feat: sign in with a passkey (WebAuthn, ES256, discoverable credentials) and manage your passkeys [CI]

This commit is contained in:
justme committed 2026-10-07 21:57:19 +08:00
1 parent ddb6608e32
commit 6385c1b9a3
31 files changed
+2296 -42

No files matched your search

+477
View File
@@ -1187,6 +1187,98 @@
}
}
},
"/api/admin/auth/passkey/options": {
"post": {
"tags": [
"admin-auth"
],
"summary": "A challenge for signing in with a passkey (discoverable credential, no username needed)",
"operationId": "auth_passkey_options",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PasskeyRequestOptionsDto"
}
}
}
},
"429": {
"description": "Too Many Requests",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
}
}
}
},
"/api/admin/auth/passkey/login": {
"post": {
"tags": [
"admin-auth"
],
"summary": "Sign in with a passkey assertion; same response as the password sign-in",
"operationId": "auth_passkey_login",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PasskeyLoginRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LoginResponse"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
},
"429": {
"description": "Too Many Requests",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
}
}
}
},
"/api/admin/releases": {
"post": {
"tags": [
@@ -1329,6 +1421,210 @@
"x-moshiro-capability": "authenticated"
}
},
"/api/admin/auth/passkeys": {
"get": {
"tags": [
"admin-auth"
],
"summary": "The signed-in user's passkeys",
"operationId": "auth_passkeys_list",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PageDtoOfPasskeyDto"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
}
},
"x-moshiro-capability": "authenticated"
},
"post": {
"tags": [
"admin-auth"
],
"summary": "Register a passkey from a WebAuthn attestation (ES256, user verification required)",
"operationId": "auth_passkeys_register",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegisterPasskeyRequest"
}
}
},
"required": true
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PasskeyDto"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
},
"409": {
"description": "Conflict",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
}
},
"x-moshiro-capability": "authenticated"
}
},
"/api/admin/auth/passkeys/options": {
"post": {
"tags": [
"admin-auth"
],
"summary": "A challenge for registering a new passkey (the user must be signed in)",
"operationId": "auth_passkeys_options",
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PasskeyCreationOptionsDto"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
}
},
"x-moshiro-capability": "authenticated"
}
},
"/api/admin/auth/passkeys/{id}": {
"delete": {
"tags": [
"admin-auth"
],
"summary": "Remove one of your own passkeys",
"operationId": "auth_passkeys_delete",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"204": {
"description": "No Content",
"content": {
"application/json": {
"schema": { }
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
},
"404": {
"description": "Not Found",
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/ProblemDto"
}
}
}
}
},
"x-moshiro-capability": "authenticated"
}
},
"/api/admin/auth/me": {
"get": {
"tags": [
@@ -6821,6 +7117,27 @@
}
}
},
"PageDtoOfPasskeyDto": {
"required": [
"items",
"next_cursor"
],
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PasskeyDto"
}
},
"next_cursor": {
"type": [
"null",
"string"
]
}
}
},
"PageDtoOfPlanDto": {
"required": [
"items",
@@ -7070,6 +7387,133 @@
}
}
},
"PasskeyCreationOptionsDto": {
"required": [
"challenge",
"rp_id",
"rp_name",
"user_id",
"user_name",
"exclude_credentials",
"timeout_ms"
],
"type": "object",
"properties": {
"challenge": {
"type": "string"
},
"rp_id": {
"type": "string"
},
"rp_name": {
"type": "string"
},
"user_id": {
"type": "string"
},
"user_name": {
"type": "string"
},
"exclude_credentials": {
"type": "array",
"items": {
"type": "string"
}
},
"timeout_ms": {
"pattern": "^-?(?:0|[1-9]\\d*)$",
"type": [
"integer",
"string"
],
"format": "int32"
}
}
},
"PasskeyDto": {
"required": [
"id",
"name",
"created_at",
"last_used_at",
"transports"
],
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"created_at": {
"format": "date-time"
},
"last_used_at": {
"format": "date-time"
},
"transports": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PasskeyLoginRequest": {
"required": [
"credential_id",
"authenticator_data",
"client_data_json",
"signature",
"return_session"
],
"type": "object",
"properties": {
"credential_id": {
"type": "string"
},
"authenticator_data": {
"type": "string"
},
"client_data_json": {
"type": "string"
},
"signature": {
"type": "string"
},
"return_session": {
"type": [
"null",
"boolean"
]
}
}
},
"PasskeyRequestOptionsDto": {
"required": [
"challenge",
"rp_id",
"timeout_ms"
],
"type": "object",
"properties": {
"challenge": {
"type": "string"
},
"rp_id": {
"type": "string"
},
"timeout_ms": {
"pattern": "^-?(?:0|[1-9]\\d*)$",
"type": [
"integer",
"string"
],
"format": "int32"
}
}
},
"PatchProbeRequest": {
"required": [
"name"
@@ -7482,6 +7926,39 @@
}
}
},
"RegisterPasskeyRequest": {
"required": [
"name",
"credential_id",
"attestation_object",
"client_data_json",
"transports"
],
"type": "object",
"properties": {
"name": {
"type": "string"
},
"credential_id": {
"type": "string"
},
"attestation_object": {
"type": "string"
},
"client_data_json": {
"type": "string"
},
"transports": {
"type": [
"null",
"array"
],
"items": {
"type": "string"
}
}
}
},
"RejectedDto": {
"required": [
"id",