@myronsi/messenger-api 2.1.0-alpha.12 → 2.1.0-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/openapi.yaml +1718 -105
- package/dist/schema.d.ts +4239 -1814
- package/dist/ws-docs.json +30 -0
- package/dist/ws-events.d.ts +69 -1
- package/dist/ws-events.schema.json +833 -153
- package/package.json +1 -1
package/dist/openapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: Messenger API
|
|
4
|
-
version: 2.1.0-alpha.
|
|
4
|
+
version: 2.1.0-alpha.14
|
|
5
5
|
summary: REST contract of the Messenger backend (v2).
|
|
6
6
|
description: |
|
|
7
7
|
Contract for the Go backend. The WebSocket protocol is described in `websocket/` and
|
|
@@ -223,7 +223,7 @@ paths:
|
|
|
223
223
|
application/json:
|
|
224
224
|
schema: { $ref: "#/components/schemas/RegisterResponse" }
|
|
225
225
|
"400": { $ref: "#/components/responses/BadRequest" }
|
|
226
|
-
"401": { $ref: "#/components/responses/
|
|
226
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
227
227
|
"403": { $ref: "#/components/responses/Forbidden" }
|
|
228
228
|
"409": { $ref: "#/components/responses/Conflict" }
|
|
229
229
|
"422": { $ref: "#/components/responses/ValidationFailed" }
|
|
@@ -266,7 +266,7 @@ paths:
|
|
|
266
266
|
application/json:
|
|
267
267
|
schema: { $ref: "#/components/schemas/RecoveryKeyResult" }
|
|
268
268
|
"400": { $ref: "#/components/responses/BadRequest" }
|
|
269
|
-
"401": { $ref: "#/components/responses/
|
|
269
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
270
270
|
"403": { $ref: "#/components/responses/Forbidden" }
|
|
271
271
|
"409": { $ref: "#/components/responses/Conflict" }
|
|
272
272
|
"422": { $ref: "#/components/responses/ValidationFailed" }
|
|
@@ -293,56 +293,6 @@ paths:
|
|
|
293
293
|
"422": { $ref: "#/components/responses/ValidationFailed" }
|
|
294
294
|
"426": { $ref: "#/components/responses/ClientOutdated" }
|
|
295
295
|
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
296
|
-
/auth/recover:
|
|
297
|
-
parameters:
|
|
298
|
-
- $ref: "#/components/parameters/ClientVersion"
|
|
299
|
-
- $ref: "#/components/parameters/ClientApiVersion"
|
|
300
|
-
post:
|
|
301
|
-
tags: [auth]
|
|
302
|
-
operationId: startRecovery
|
|
303
|
-
summary: Start account recovery (temporary)
|
|
304
|
-
description: Temporary port of the Python recovery flow; it is redesigned in release 1.1.
|
|
305
|
-
x-temporary: true
|
|
306
|
-
security: []
|
|
307
|
-
requestBody:
|
|
308
|
-
required: true
|
|
309
|
-
content:
|
|
310
|
-
application/json:
|
|
311
|
-
schema: { $ref: "#/components/schemas/RecoveryRequest" }
|
|
312
|
-
responses:
|
|
313
|
-
"426": { $ref: "#/components/responses/ClientOutdated" }
|
|
314
|
-
"200":
|
|
315
|
-
description: Recovery token
|
|
316
|
-
content:
|
|
317
|
-
application/json:
|
|
318
|
-
schema: { $ref: "#/components/schemas/RecoveryResponse" }
|
|
319
|
-
"400": { $ref: "#/components/responses/BadRequest" }
|
|
320
|
-
"404": { $ref: "#/components/responses/NotFound" }
|
|
321
|
-
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
322
|
-
/auth/reset-password:
|
|
323
|
-
parameters:
|
|
324
|
-
- $ref: "#/components/parameters/ClientVersion"
|
|
325
|
-
- $ref: "#/components/parameters/ClientApiVersion"
|
|
326
|
-
post:
|
|
327
|
-
tags: [auth]
|
|
328
|
-
operationId: resetPassword
|
|
329
|
-
summary: Set a new password with a recovery token (temporary)
|
|
330
|
-
x-temporary: true
|
|
331
|
-
security: []
|
|
332
|
-
requestBody:
|
|
333
|
-
required: true
|
|
334
|
-
content:
|
|
335
|
-
application/json:
|
|
336
|
-
schema: { $ref: "#/components/schemas/ResetPasswordRequest" }
|
|
337
|
-
responses:
|
|
338
|
-
"426": { $ref: "#/components/responses/ClientOutdated" }
|
|
339
|
-
"204":
|
|
340
|
-
description: Password changed; all sessions are revoked
|
|
341
|
-
"400": { $ref: "#/components/responses/BadRequest" }
|
|
342
|
-
"401": { $ref: "#/components/responses/Unauthorized" }
|
|
343
|
-
"422": { $ref: "#/components/responses/ValidationFailed" }
|
|
344
|
-
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
345
|
-
|
|
346
296
|
/ws/ticket:
|
|
347
297
|
parameters:
|
|
348
298
|
- $ref: "#/components/parameters/ClientVersion"
|
|
@@ -637,7 +587,7 @@ paths:
|
|
|
637
587
|
tags: [me]
|
|
638
588
|
operationId: disableTwoFactor
|
|
639
589
|
summary: Turn two-factor off
|
|
640
|
-
description: "A wrong password or code answers `403` (`invalid_credentials` or `invalid_two_factor_code`); `409` when two-factor is off."
|
|
590
|
+
description: "A wrong password or code answers `403` (`invalid_credentials` or `invalid_two_factor_code`); a correct password without `code` answers `401` with `code: two_factor_required`; `409` when two-factor is off."
|
|
641
591
|
requestBody:
|
|
642
592
|
required: true
|
|
643
593
|
content:
|
|
@@ -648,7 +598,7 @@ paths:
|
|
|
648
598
|
"204":
|
|
649
599
|
description: Two-factor authentication disabled
|
|
650
600
|
"400": { $ref: "#/components/responses/BadRequest" }
|
|
651
|
-
"401": { $ref: "#/components/responses/
|
|
601
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
652
602
|
"403": { $ref: "#/components/responses/Forbidden" }
|
|
653
603
|
"409": { $ref: "#/components/responses/Conflict" }
|
|
654
604
|
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
@@ -2120,34 +2070,1364 @@ paths:
|
|
|
2120
2070
|
"409":
|
|
2121
2071
|
$ref: "#/components/responses/Conflict"
|
|
2122
2072
|
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2073
|
+
/auth/recovery-requests:
|
|
2074
|
+
parameters:
|
|
2075
|
+
- &a1
|
|
2076
|
+
$ref: "#/components/parameters/ClientVersion"
|
|
2077
|
+
- &a2
|
|
2078
|
+
$ref: "#/components/parameters/ClientApiVersion"
|
|
2079
|
+
post:
|
|
2080
|
+
tags:
|
|
2081
|
+
- auth
|
|
2082
|
+
operationId: startDeviceRecovery
|
|
2083
|
+
summary: Request approval from a signed-in device
|
|
2084
|
+
security: []
|
|
2085
|
+
requestBody:
|
|
2086
|
+
required: true
|
|
2087
|
+
content:
|
|
2088
|
+
application/json:
|
|
2089
|
+
schema:
|
|
2090
|
+
$ref: "#/components/schemas/DeviceRecoveryStart"
|
|
2091
|
+
responses:
|
|
2092
|
+
"200":
|
|
2093
|
+
description: Success
|
|
2094
|
+
content:
|
|
2095
|
+
application/json:
|
|
2096
|
+
schema:
|
|
2097
|
+
$ref: "#/components/schemas/DeviceRecoveryResult"
|
|
2098
|
+
"400":
|
|
2099
|
+
$ref: "#/components/responses/BadRequest"
|
|
2100
|
+
"401":
|
|
2101
|
+
$ref: "#/components/responses/Unauthorized"
|
|
2102
|
+
"403":
|
|
2103
|
+
$ref: "#/components/responses/Forbidden"
|
|
2104
|
+
"409":
|
|
2105
|
+
$ref: "#/components/responses/Conflict"
|
|
2106
|
+
"422":
|
|
2107
|
+
$ref: "#/components/responses/ValidationFailed"
|
|
2108
|
+
"426":
|
|
2109
|
+
$ref: "#/components/responses/ClientOutdated"
|
|
2110
|
+
"429":
|
|
2111
|
+
$ref: "#/components/responses/TooManyRequests"
|
|
2112
|
+
/auth/recovery-requests/{requestId}:
|
|
2113
|
+
parameters:
|
|
2114
|
+
- *a1
|
|
2115
|
+
- *a2
|
|
2116
|
+
- &a3
|
|
2117
|
+
name: requestId
|
|
2118
|
+
in: path
|
|
2119
|
+
required: true
|
|
2120
|
+
schema:
|
|
2121
|
+
type: string
|
|
2122
|
+
minLength: 32
|
|
2123
|
+
maxLength: 128
|
|
2124
|
+
pattern: ^[A-Za-z0-9_-]+$
|
|
2125
|
+
description: Secret one-time recovery request capability. Never log or persist
|
|
2126
|
+
it in browser storage.
|
|
2127
|
+
get:
|
|
2128
|
+
tags:
|
|
2129
|
+
- auth
|
|
2130
|
+
operationId: deviceRecoveryStatus
|
|
2131
|
+
summary: Poll the status of a secret recovery request
|
|
2132
|
+
security: []
|
|
2133
|
+
responses:
|
|
2134
|
+
"200":
|
|
2135
|
+
description: Success
|
|
2136
|
+
content:
|
|
2137
|
+
application/json:
|
|
2138
|
+
schema:
|
|
2139
|
+
$ref: "#/components/schemas/DeviceRecoveryStatus"
|
|
2140
|
+
"400":
|
|
2141
|
+
$ref: "#/components/responses/BadRequest"
|
|
2142
|
+
"401":
|
|
2143
|
+
$ref: "#/components/responses/Unauthorized"
|
|
2144
|
+
"403":
|
|
2145
|
+
$ref: "#/components/responses/Forbidden"
|
|
2146
|
+
"409":
|
|
2147
|
+
$ref: "#/components/responses/Conflict"
|
|
2148
|
+
"422":
|
|
2149
|
+
$ref: "#/components/responses/ValidationFailed"
|
|
2150
|
+
"426":
|
|
2151
|
+
$ref: "#/components/responses/ClientOutdated"
|
|
2152
|
+
"429":
|
|
2153
|
+
$ref: "#/components/responses/TooManyRequests"
|
|
2154
|
+
/auth/recovery-requests/{requestId}/approve:
|
|
2155
|
+
parameters:
|
|
2156
|
+
- *a1
|
|
2157
|
+
- *a2
|
|
2158
|
+
- &a4
|
|
2159
|
+
name: requestId
|
|
2160
|
+
in: path
|
|
2161
|
+
required: true
|
|
2162
|
+
schema:
|
|
2163
|
+
type: string
|
|
2164
|
+
minLength: 32
|
|
2165
|
+
maxLength: 128
|
|
2166
|
+
pattern: ^[A-Za-z0-9_-]+$
|
|
2167
|
+
description: Approval handle from the recovery_request event. It is different from the
|
|
2168
|
+
requester's capability and cannot poll or complete the request.
|
|
2169
|
+
post:
|
|
2170
|
+
tags:
|
|
2171
|
+
- auth
|
|
2172
|
+
operationId: approveDeviceRecovery
|
|
2173
|
+
summary: Approve a recovery request using number matching, the password and the second factor
|
|
2174
|
+
requestBody:
|
|
2175
|
+
required: true
|
|
2176
|
+
content:
|
|
2177
|
+
application/json:
|
|
2178
|
+
schema:
|
|
2179
|
+
$ref: "#/components/schemas/DeviceRecoveryApproval"
|
|
2180
|
+
responses:
|
|
2181
|
+
"200":
|
|
2182
|
+
description: Success
|
|
2183
|
+
content:
|
|
2184
|
+
application/json:
|
|
2185
|
+
schema:
|
|
2186
|
+
$ref: "#/components/schemas/DeviceRecoveryStatus"
|
|
2187
|
+
"400":
|
|
2188
|
+
$ref: "#/components/responses/BadRequest"
|
|
2189
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
2190
|
+
"403":
|
|
2191
|
+
$ref: "#/components/responses/Forbidden"
|
|
2192
|
+
"404":
|
|
2193
|
+
$ref: "#/components/responses/NotFound"
|
|
2194
|
+
"409":
|
|
2195
|
+
$ref: "#/components/responses/Conflict"
|
|
2196
|
+
"422":
|
|
2197
|
+
$ref: "#/components/responses/ValidationFailed"
|
|
2198
|
+
"426":
|
|
2199
|
+
$ref: "#/components/responses/ClientOutdated"
|
|
2200
|
+
"429":
|
|
2201
|
+
$ref: "#/components/responses/TooManyRequests"
|
|
2202
|
+
/auth/recovery-requests/{requestId}/deny:
|
|
2203
|
+
parameters:
|
|
2204
|
+
- *a1
|
|
2205
|
+
- *a2
|
|
2206
|
+
- *a4
|
|
2207
|
+
post:
|
|
2208
|
+
tags:
|
|
2209
|
+
- auth
|
|
2210
|
+
operationId: denyDeviceRecovery
|
|
2211
|
+
summary: Deny an unexpected recovery request
|
|
2212
|
+
responses:
|
|
2213
|
+
"200":
|
|
2214
|
+
description: Success
|
|
2215
|
+
content:
|
|
2216
|
+
application/json:
|
|
2217
|
+
schema:
|
|
2218
|
+
$ref: "#/components/schemas/DeviceRecoveryStatus"
|
|
2219
|
+
"400":
|
|
2220
|
+
$ref: "#/components/responses/BadRequest"
|
|
2221
|
+
"401":
|
|
2222
|
+
$ref: "#/components/responses/Unauthorized"
|
|
2223
|
+
"403":
|
|
2224
|
+
$ref: "#/components/responses/Forbidden"
|
|
2225
|
+
"404":
|
|
2226
|
+
$ref: "#/components/responses/NotFound"
|
|
2227
|
+
"409":
|
|
2228
|
+
$ref: "#/components/responses/Conflict"
|
|
2229
|
+
"422":
|
|
2230
|
+
$ref: "#/components/responses/ValidationFailed"
|
|
2231
|
+
"426":
|
|
2232
|
+
$ref: "#/components/responses/ClientOutdated"
|
|
2233
|
+
"429":
|
|
2234
|
+
$ref: "#/components/responses/TooManyRequests"
|
|
2235
|
+
/auth/recovery-requests/{requestId}/complete:
|
|
2236
|
+
parameters:
|
|
2237
|
+
- *a1
|
|
2238
|
+
- *a2
|
|
2239
|
+
- *a3
|
|
2240
|
+
post:
|
|
2241
|
+
tags:
|
|
2242
|
+
- auth
|
|
2243
|
+
operationId: completeDeviceRecovery
|
|
2244
|
+
summary: Consume an approved request and reset the password
|
|
2245
|
+
security: []
|
|
2246
|
+
requestBody:
|
|
2247
|
+
required: true
|
|
2248
|
+
content:
|
|
2249
|
+
application/json:
|
|
2250
|
+
schema:
|
|
2251
|
+
$ref: "#/components/schemas/DeviceRecoveryComplete"
|
|
2252
|
+
responses:
|
|
2253
|
+
"200":
|
|
2254
|
+
description: Success
|
|
2255
|
+
content:
|
|
2256
|
+
application/json:
|
|
2257
|
+
schema:
|
|
2258
|
+
$ref: "#/components/schemas/RegisterResponse"
|
|
2259
|
+
"400":
|
|
2260
|
+
$ref: "#/components/responses/BadRequest"
|
|
2261
|
+
"401":
|
|
2262
|
+
$ref: "#/components/responses/Unauthorized"
|
|
2263
|
+
"403":
|
|
2264
|
+
$ref: "#/components/responses/Forbidden"
|
|
2265
|
+
"409":
|
|
2266
|
+
$ref: "#/components/responses/Conflict"
|
|
2267
|
+
"422":
|
|
2268
|
+
$ref: "#/components/responses/ValidationFailed"
|
|
2269
|
+
"426":
|
|
2270
|
+
$ref: "#/components/responses/ClientOutdated"
|
|
2271
|
+
"429":
|
|
2272
|
+
$ref: "#/components/responses/TooManyRequests"
|
|
2273
|
+
/auth/recovery-requests/{requestId}/cancel:
|
|
2274
|
+
parameters:
|
|
2275
|
+
- *a1
|
|
2276
|
+
- *a2
|
|
2277
|
+
- *a3
|
|
2278
|
+
post:
|
|
2279
|
+
tags:
|
|
2280
|
+
- auth
|
|
2281
|
+
operationId: cancelDeviceRecovery
|
|
2282
|
+
summary: Withdraw a pending recovery request from the requesting browser
|
|
2283
|
+
description: Ends a pending request so it no longer counts towards the limit of three active requests. It
|
|
2284
|
+
answers 204 for every capability, including unknown, decided and expired ones.
|
|
2285
|
+
security: []
|
|
2286
|
+
responses:
|
|
2287
|
+
"204":
|
|
2288
|
+
description: Cancelled or nothing to cancel
|
|
2289
|
+
"400":
|
|
2290
|
+
$ref: "#/components/responses/BadRequest"
|
|
2291
|
+
"426":
|
|
2292
|
+
$ref: "#/components/responses/ClientOutdated"
|
|
2293
|
+
"429":
|
|
2294
|
+
$ref: "#/components/responses/TooManyRequests"
|
|
2135
2295
|
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2296
|
+
/auth/qr-login:
|
|
2297
|
+
parameters:
|
|
2298
|
+
- $ref: "#/components/parameters/ClientVersion"
|
|
2299
|
+
- $ref: "#/components/parameters/ClientApiVersion"
|
|
2300
|
+
post:
|
|
2301
|
+
tags: [auth]
|
|
2302
|
+
operationId: createQrLogin
|
|
2303
|
+
summary: Start a sign-in with a QR code
|
|
2304
|
+
description: |
|
|
2305
|
+
Called by a device that is not signed in. Show `url` as a QR code and poll
|
|
2306
|
+
`POST /auth/qr-login/{requestId}/poll` with `poll_token` until the status is final. The request expires
|
|
2307
|
+
after 2 minutes. Keep `poll_token` in memory only; it is the secret that receives the session.
|
|
2308
|
+
security: []
|
|
2309
|
+
responses:
|
|
2310
|
+
"201":
|
|
2311
|
+
description: Created
|
|
2312
|
+
content:
|
|
2313
|
+
application/json:
|
|
2314
|
+
schema: { $ref: "#/components/schemas/QrLoginStart" }
|
|
2315
|
+
example:
|
|
2316
|
+
request_id: 3q2-7wEAAAD9W8b0kZ3lYfHk6gF1n9cQmR2pVx4sTtU
|
|
2317
|
+
poll_token: example-poll-token
|
|
2318
|
+
url: https://messenger.example.com/qr/3q2-7wEAAAD9W8b0kZ3lYfHk6gF1n9cQmR2pVx4sTtU
|
|
2319
|
+
expires_at: "2026-11-06T12:02:00Z"
|
|
2320
|
+
"400": { $ref: "#/components/responses/BadRequest" }
|
|
2321
|
+
"426": { $ref: "#/components/responses/ClientOutdated" }
|
|
2322
|
+
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
2323
|
+
/auth/qr-login/{requestId}:
|
|
2324
|
+
parameters:
|
|
2325
|
+
- $ref: "#/components/parameters/ClientVersion"
|
|
2326
|
+
- $ref: "#/components/parameters/ClientApiVersion"
|
|
2327
|
+
- $ref: "#/components/parameters/QrLoginRequestId"
|
|
2328
|
+
get:
|
|
2329
|
+
tags: [auth]
|
|
2330
|
+
operationId: getQrLogin
|
|
2331
|
+
summary: Show who asks to sign in with a QR code
|
|
2332
|
+
description: |
|
|
2333
|
+
Called by a signed-in device after it opened the link of the QR code. The first call moves the request from
|
|
2334
|
+
`pending` to `scanned` and binds it to the caller: other accounts get `404` from then on. Show `device` and
|
|
2335
|
+
`location` and let the user confirm that the sign-in is their own before approving. Unknown, expired and
|
|
2336
|
+
used requests answer `404`.
|
|
2337
|
+
responses:
|
|
2338
|
+
"200":
|
|
2339
|
+
description: Success
|
|
2340
|
+
content:
|
|
2341
|
+
application/json:
|
|
2342
|
+
schema: { $ref: "#/components/schemas/QrLoginRequest" }
|
|
2343
|
+
example:
|
|
2344
|
+
request_id: 3q2-7wEAAAD9W8b0kZ3lYfHk6gF1n9cQmR2pVx4sTtU
|
|
2345
|
+
status: scanned
|
|
2346
|
+
device: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Firefox/140.0
|
|
2347
|
+
location: { city: Berlin, country_code: DE, country: Germany }
|
|
2348
|
+
created_at: "2026-11-06T12:00:00Z"
|
|
2349
|
+
expires_at: "2026-11-06T12:02:00Z"
|
|
2350
|
+
"400": { $ref: "#/components/responses/BadRequest" }
|
|
2351
|
+
"401": { $ref: "#/components/responses/Unauthorized" }
|
|
2352
|
+
"404": { $ref: "#/components/responses/NotFound" }
|
|
2353
|
+
"426": { $ref: "#/components/responses/ClientOutdated" }
|
|
2354
|
+
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
2355
|
+
/auth/qr-login/{requestId}/poll:
|
|
2356
|
+
parameters:
|
|
2357
|
+
- $ref: "#/components/parameters/ClientVersion"
|
|
2358
|
+
- $ref: "#/components/parameters/ClientApiVersion"
|
|
2359
|
+
- $ref: "#/components/parameters/QrLoginRequestId"
|
|
2360
|
+
post:
|
|
2361
|
+
tags: [auth]
|
|
2362
|
+
operationId: pollQrLogin
|
|
2363
|
+
summary: Poll a QR sign-in and receive the session once approved
|
|
2364
|
+
description: |
|
|
2365
|
+
Called by the device that created the request, about once per second (at most 120 polls per 2 minutes per
|
|
2366
|
+
client address). While the request is open the answer is `pending` or `scanned`. The first poll after
|
|
2367
|
+
approval signs the device in exactly like a successful `POST /auth/login`: it sets the refresh cookie and
|
|
2368
|
+
returns `status: approved` with the token fields. Later polls, unknown requests, a wrong `poll_token` and
|
|
2369
|
+
expired requests all answer `expired`.
|
|
2370
|
+
security: []
|
|
2371
|
+
requestBody:
|
|
2372
|
+
required: true
|
|
2373
|
+
content:
|
|
2374
|
+
application/json:
|
|
2375
|
+
schema: { $ref: "#/components/schemas/QrLoginPollRequest" }
|
|
2376
|
+
responses:
|
|
2377
|
+
"200":
|
|
2378
|
+
description: The current status, plus the session when it was just approved
|
|
2379
|
+
headers:
|
|
2380
|
+
Set-Cookie: { $ref: "#/components/headers/RefreshCookie" }
|
|
2381
|
+
content:
|
|
2382
|
+
application/json:
|
|
2383
|
+
schema: { $ref: "#/components/schemas/QrLoginPollResult" }
|
|
2384
|
+
examples:
|
|
2385
|
+
waiting:
|
|
2386
|
+
value: { status: scanned }
|
|
2387
|
+
approved:
|
|
2388
|
+
value:
|
|
2389
|
+
status: approved
|
|
2390
|
+
access_token: example-access-token
|
|
2391
|
+
token_type: Bearer
|
|
2392
|
+
expires_in: 900
|
|
2393
|
+
"400": { $ref: "#/components/responses/BadRequest" }
|
|
2394
|
+
"426": { $ref: "#/components/responses/ClientOutdated" }
|
|
2395
|
+
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
2396
|
+
/auth/qr-login/{requestId}/approve:
|
|
2397
|
+
parameters:
|
|
2398
|
+
- $ref: "#/components/parameters/ClientVersion"
|
|
2399
|
+
- $ref: "#/components/parameters/ClientApiVersion"
|
|
2400
|
+
- $ref: "#/components/parameters/QrLoginRequestId"
|
|
2401
|
+
post:
|
|
2402
|
+
tags: [auth]
|
|
2403
|
+
operationId: approveQrLogin
|
|
2404
|
+
summary: Sign the device of a QR code into this account
|
|
2405
|
+
description: |
|
|
2406
|
+
Single use. The request must be `pending` or `scanned`, not expired and not bound to another account
|
|
2407
|
+
(`404` otherwise; `409` when it was already approved or denied). The calling session must be active and
|
|
2408
|
+
used within `RECOVERY_APPROVAL_MAX_AGE` (`403 forbidden` otherwise). With two-factor on, send `code` (an
|
|
2409
|
+
authenticator code or an unused recovery code); without it the answer is `401 two_factor_required` and the
|
|
2410
|
+
request stays approvable. A wrong code answers `400 invalid_credentials`. The body may be omitted.
|
|
2411
|
+
requestBody:
|
|
2412
|
+
required: false
|
|
2413
|
+
content:
|
|
2414
|
+
application/json:
|
|
2415
|
+
schema: { $ref: "#/components/schemas/QrLoginApproval" }
|
|
2416
|
+
example: { code: "123456" }
|
|
2417
|
+
responses:
|
|
2418
|
+
"204": { description: Approved; the new device receives its session on its next poll }
|
|
2419
|
+
"400": { $ref: "#/components/responses/BadRequest" }
|
|
2420
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
2421
|
+
"403": { $ref: "#/components/responses/Forbidden" }
|
|
2422
|
+
"404": { $ref: "#/components/responses/NotFound" }
|
|
2423
|
+
"409": { $ref: "#/components/responses/Conflict" }
|
|
2424
|
+
"426": { $ref: "#/components/responses/ClientOutdated" }
|
|
2425
|
+
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
2426
|
+
/auth/qr-login/{requestId}/deny:
|
|
2427
|
+
parameters:
|
|
2428
|
+
- $ref: "#/components/parameters/ClientVersion"
|
|
2429
|
+
- $ref: "#/components/parameters/ClientApiVersion"
|
|
2430
|
+
- $ref: "#/components/parameters/QrLoginRequestId"
|
|
2431
|
+
post:
|
|
2432
|
+
tags: [auth]
|
|
2433
|
+
operationId: denyQrLogin
|
|
2434
|
+
summary: Refuse a QR sign-in
|
|
2435
|
+
description: The new device then polls `denied`. Same `404` and `409` rules as approving.
|
|
2436
|
+
responses:
|
|
2437
|
+
"204": { description: Denied }
|
|
2438
|
+
"400": { $ref: "#/components/responses/BadRequest" }
|
|
2439
|
+
"401": { $ref: "#/components/responses/Unauthorized" }
|
|
2440
|
+
"404": { $ref: "#/components/responses/NotFound" }
|
|
2441
|
+
"409": { $ref: "#/components/responses/Conflict" }
|
|
2442
|
+
"426": { $ref: "#/components/responses/ClientOutdated" }
|
|
2443
|
+
"429": { $ref: "#/components/responses/TooManyRequests" }
|
|
2140
2444
|
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2445
|
+
/auth/passkeys/register/options:
|
|
2446
|
+
parameters: &a1
|
|
2447
|
+
- &a2
|
|
2448
|
+
$ref: "#/components/parameters/ClientVersion"
|
|
2449
|
+
- &a3
|
|
2450
|
+
$ref: "#/components/parameters/ClientApiVersion"
|
|
2451
|
+
post:
|
|
2452
|
+
tags:
|
|
2453
|
+
- auth
|
|
2454
|
+
operationId: passkeyRegistrationOptions
|
|
2455
|
+
summary: Begin passkey registration after password and second-factor verification
|
|
2456
|
+
requestBody:
|
|
2457
|
+
required: true
|
|
2458
|
+
content:
|
|
2459
|
+
application/json:
|
|
2460
|
+
schema:
|
|
2461
|
+
$ref: "#/components/schemas/PasskeyRegistration"
|
|
2462
|
+
responses:
|
|
2463
|
+
"200":
|
|
2464
|
+
description: Success
|
|
2465
|
+
content:
|
|
2466
|
+
application/json:
|
|
2467
|
+
schema:
|
|
2468
|
+
$ref: "#/components/schemas/PasskeyOptions"
|
|
2469
|
+
"400":
|
|
2470
|
+
description: Bad request
|
|
2471
|
+
content:
|
|
2472
|
+
application/problem+json:
|
|
2473
|
+
schema:
|
|
2474
|
+
$ref: "#/components/schemas/Problem"
|
|
2475
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
2476
|
+
"403":
|
|
2477
|
+
description: Forbidden
|
|
2478
|
+
content:
|
|
2479
|
+
application/problem+json:
|
|
2480
|
+
schema:
|
|
2481
|
+
$ref: "#/components/schemas/Problem"
|
|
2482
|
+
"404":
|
|
2483
|
+
description: Not found
|
|
2484
|
+
content:
|
|
2485
|
+
application/problem+json:
|
|
2486
|
+
schema:
|
|
2487
|
+
$ref: "#/components/schemas/Problem"
|
|
2488
|
+
"409":
|
|
2489
|
+
description: Conflict
|
|
2490
|
+
content:
|
|
2491
|
+
application/problem+json:
|
|
2492
|
+
schema:
|
|
2493
|
+
$ref: "#/components/schemas/Problem"
|
|
2494
|
+
"422":
|
|
2495
|
+
description: Invalid input
|
|
2496
|
+
content:
|
|
2497
|
+
application/problem+json:
|
|
2498
|
+
schema:
|
|
2499
|
+
$ref: "#/components/schemas/Problem"
|
|
2500
|
+
"426":
|
|
2501
|
+
description: Client outdated
|
|
2502
|
+
content:
|
|
2503
|
+
application/problem+json:
|
|
2504
|
+
schema:
|
|
2505
|
+
$ref: "#/components/schemas/Problem"
|
|
2506
|
+
"429":
|
|
2507
|
+
description: Rate limited
|
|
2508
|
+
content:
|
|
2509
|
+
application/problem+json:
|
|
2510
|
+
schema:
|
|
2511
|
+
$ref: "#/components/schemas/Problem"
|
|
2512
|
+
"503":
|
|
2513
|
+
description: Passkeys are not configured
|
|
2514
|
+
content:
|
|
2515
|
+
application/problem+json:
|
|
2516
|
+
schema:
|
|
2517
|
+
$ref: "#/components/schemas/Problem"
|
|
2518
|
+
/auth/passkeys/register/verify:
|
|
2519
|
+
parameters: *a1
|
|
2520
|
+
post:
|
|
2521
|
+
tags:
|
|
2522
|
+
- auth
|
|
2523
|
+
operationId: verifyPasskeyRegistration
|
|
2524
|
+
summary: Verify and store a passkey
|
|
2525
|
+
requestBody:
|
|
2526
|
+
required: true
|
|
2527
|
+
content:
|
|
2528
|
+
application/json:
|
|
2529
|
+
schema:
|
|
2530
|
+
$ref: "#/components/schemas/PasskeyVerification"
|
|
2531
|
+
responses:
|
|
2532
|
+
"204":
|
|
2533
|
+
description: Success
|
|
2534
|
+
"400":
|
|
2535
|
+
description: Bad request
|
|
2536
|
+
content:
|
|
2537
|
+
application/problem+json:
|
|
2538
|
+
schema:
|
|
2539
|
+
$ref: "#/components/schemas/Problem"
|
|
2540
|
+
"401":
|
|
2541
|
+
description: Unauthorized
|
|
2542
|
+
content:
|
|
2543
|
+
application/problem+json:
|
|
2544
|
+
schema:
|
|
2545
|
+
$ref: "#/components/schemas/Problem"
|
|
2546
|
+
"403":
|
|
2547
|
+
description: Forbidden
|
|
2548
|
+
content:
|
|
2549
|
+
application/problem+json:
|
|
2550
|
+
schema:
|
|
2551
|
+
$ref: "#/components/schemas/Problem"
|
|
2552
|
+
"404":
|
|
2553
|
+
description: Not found
|
|
2554
|
+
content:
|
|
2555
|
+
application/problem+json:
|
|
2556
|
+
schema:
|
|
2557
|
+
$ref: "#/components/schemas/Problem"
|
|
2558
|
+
"409":
|
|
2559
|
+
description: Conflict
|
|
2560
|
+
content:
|
|
2561
|
+
application/problem+json:
|
|
2562
|
+
schema:
|
|
2563
|
+
$ref: "#/components/schemas/Problem"
|
|
2564
|
+
"422":
|
|
2565
|
+
description: Invalid input
|
|
2566
|
+
content:
|
|
2567
|
+
application/problem+json:
|
|
2568
|
+
schema:
|
|
2569
|
+
$ref: "#/components/schemas/Problem"
|
|
2570
|
+
"426":
|
|
2571
|
+
description: Client outdated
|
|
2572
|
+
content:
|
|
2573
|
+
application/problem+json:
|
|
2574
|
+
schema:
|
|
2575
|
+
$ref: "#/components/schemas/Problem"
|
|
2576
|
+
"429":
|
|
2577
|
+
description: Rate limited
|
|
2578
|
+
content:
|
|
2579
|
+
application/problem+json:
|
|
2580
|
+
schema:
|
|
2581
|
+
$ref: "#/components/schemas/Problem"
|
|
2582
|
+
"503":
|
|
2583
|
+
description: Passkeys are not configured
|
|
2584
|
+
content:
|
|
2585
|
+
application/problem+json:
|
|
2586
|
+
schema:
|
|
2587
|
+
$ref: "#/components/schemas/Problem"
|
|
2588
|
+
/auth/passkeys:
|
|
2589
|
+
parameters: *a1
|
|
2590
|
+
get:
|
|
2591
|
+
tags:
|
|
2592
|
+
- auth
|
|
2593
|
+
operationId: listPasskeys
|
|
2594
|
+
summary: List the account passkeys
|
|
2595
|
+
responses:
|
|
2596
|
+
"200":
|
|
2597
|
+
description: Success
|
|
2598
|
+
content:
|
|
2599
|
+
application/json:
|
|
2600
|
+
schema:
|
|
2601
|
+
$ref: "#/components/schemas/PasskeyPage"
|
|
2602
|
+
"400":
|
|
2603
|
+
description: Bad request
|
|
2604
|
+
content:
|
|
2605
|
+
application/problem+json:
|
|
2606
|
+
schema:
|
|
2607
|
+
$ref: "#/components/schemas/Problem"
|
|
2608
|
+
"401":
|
|
2609
|
+
description: Unauthorized
|
|
2610
|
+
content:
|
|
2611
|
+
application/problem+json:
|
|
2612
|
+
schema:
|
|
2613
|
+
$ref: "#/components/schemas/Problem"
|
|
2614
|
+
"403":
|
|
2615
|
+
description: Forbidden
|
|
2616
|
+
content:
|
|
2617
|
+
application/problem+json:
|
|
2618
|
+
schema:
|
|
2619
|
+
$ref: "#/components/schemas/Problem"
|
|
2620
|
+
"404":
|
|
2621
|
+
description: Not found
|
|
2622
|
+
content:
|
|
2623
|
+
application/problem+json:
|
|
2624
|
+
schema:
|
|
2625
|
+
$ref: "#/components/schemas/Problem"
|
|
2626
|
+
"409":
|
|
2627
|
+
description: Conflict
|
|
2628
|
+
content:
|
|
2629
|
+
application/problem+json:
|
|
2630
|
+
schema:
|
|
2631
|
+
$ref: "#/components/schemas/Problem"
|
|
2632
|
+
"422":
|
|
2633
|
+
description: Invalid input
|
|
2634
|
+
content:
|
|
2635
|
+
application/problem+json:
|
|
2636
|
+
schema:
|
|
2637
|
+
$ref: "#/components/schemas/Problem"
|
|
2638
|
+
"426":
|
|
2639
|
+
description: Client outdated
|
|
2640
|
+
content:
|
|
2641
|
+
application/problem+json:
|
|
2642
|
+
schema:
|
|
2643
|
+
$ref: "#/components/schemas/Problem"
|
|
2644
|
+
"429":
|
|
2645
|
+
description: Rate limited
|
|
2646
|
+
content:
|
|
2647
|
+
application/problem+json:
|
|
2648
|
+
schema:
|
|
2649
|
+
$ref: "#/components/schemas/Problem"
|
|
2650
|
+
"503":
|
|
2651
|
+
description: Passkeys are not configured
|
|
2652
|
+
content:
|
|
2653
|
+
application/problem+json:
|
|
2654
|
+
schema:
|
|
2655
|
+
$ref: "#/components/schemas/Problem"
|
|
2656
|
+
/auth/passkeys/{passkeyId}:
|
|
2657
|
+
parameters:
|
|
2658
|
+
- *a2
|
|
2659
|
+
- *a3
|
|
2660
|
+
- name: passkeyId
|
|
2661
|
+
in: path
|
|
2662
|
+
required: true
|
|
2663
|
+
schema:
|
|
2664
|
+
type: string
|
|
2665
|
+
minLength: 1
|
|
2666
|
+
maxLength: 2048
|
|
2667
|
+
pattern: ^[A-Za-z0-9_-]+$
|
|
2668
|
+
patch:
|
|
2669
|
+
tags:
|
|
2670
|
+
- auth
|
|
2671
|
+
operationId: renamePasskey
|
|
2672
|
+
summary: Rename a passkey
|
|
2673
|
+
requestBody:
|
|
2674
|
+
required: true
|
|
2675
|
+
content:
|
|
2676
|
+
application/json:
|
|
2677
|
+
schema:
|
|
2678
|
+
$ref: "#/components/schemas/PasskeyRename"
|
|
2679
|
+
responses:
|
|
2680
|
+
"204":
|
|
2681
|
+
description: Success
|
|
2682
|
+
"400":
|
|
2683
|
+
description: Bad request
|
|
2684
|
+
content:
|
|
2685
|
+
application/problem+json:
|
|
2686
|
+
schema:
|
|
2687
|
+
$ref: "#/components/schemas/Problem"
|
|
2688
|
+
"401":
|
|
2689
|
+
description: Unauthorized
|
|
2690
|
+
content:
|
|
2691
|
+
application/problem+json:
|
|
2692
|
+
schema:
|
|
2693
|
+
$ref: "#/components/schemas/Problem"
|
|
2694
|
+
"403":
|
|
2695
|
+
description: Forbidden
|
|
2696
|
+
content:
|
|
2697
|
+
application/problem+json:
|
|
2698
|
+
schema:
|
|
2699
|
+
$ref: "#/components/schemas/Problem"
|
|
2700
|
+
"404":
|
|
2701
|
+
description: Not found
|
|
2702
|
+
content:
|
|
2703
|
+
application/problem+json:
|
|
2704
|
+
schema:
|
|
2705
|
+
$ref: "#/components/schemas/Problem"
|
|
2706
|
+
"409":
|
|
2707
|
+
description: Conflict
|
|
2708
|
+
content:
|
|
2709
|
+
application/problem+json:
|
|
2710
|
+
schema:
|
|
2711
|
+
$ref: "#/components/schemas/Problem"
|
|
2712
|
+
"422":
|
|
2713
|
+
description: Invalid input
|
|
2714
|
+
content:
|
|
2715
|
+
application/problem+json:
|
|
2716
|
+
schema:
|
|
2717
|
+
$ref: "#/components/schemas/Problem"
|
|
2718
|
+
"426":
|
|
2719
|
+
description: Client outdated
|
|
2720
|
+
content:
|
|
2721
|
+
application/problem+json:
|
|
2722
|
+
schema:
|
|
2723
|
+
$ref: "#/components/schemas/Problem"
|
|
2724
|
+
"429":
|
|
2725
|
+
description: Rate limited
|
|
2726
|
+
content:
|
|
2727
|
+
application/problem+json:
|
|
2728
|
+
schema:
|
|
2729
|
+
$ref: "#/components/schemas/Problem"
|
|
2730
|
+
"503":
|
|
2731
|
+
description: Passkeys are not configured
|
|
2732
|
+
content:
|
|
2733
|
+
application/problem+json:
|
|
2734
|
+
schema:
|
|
2735
|
+
$ref: "#/components/schemas/Problem"
|
|
2736
|
+
delete:
|
|
2737
|
+
tags:
|
|
2738
|
+
- auth
|
|
2739
|
+
operationId: removePasskey
|
|
2740
|
+
summary: Remove a passkey; the last credential requires the password
|
|
2741
|
+
requestBody:
|
|
2742
|
+
required: true
|
|
2743
|
+
content:
|
|
2744
|
+
application/json:
|
|
2745
|
+
schema:
|
|
2746
|
+
$ref: "#/components/schemas/PasskeyRemoval"
|
|
2747
|
+
responses:
|
|
2748
|
+
"204":
|
|
2749
|
+
description: Success
|
|
2750
|
+
"400":
|
|
2751
|
+
description: Bad request
|
|
2752
|
+
content:
|
|
2753
|
+
application/problem+json:
|
|
2754
|
+
schema:
|
|
2755
|
+
$ref: "#/components/schemas/Problem"
|
|
2756
|
+
"401":
|
|
2757
|
+
description: Unauthorized
|
|
2758
|
+
content:
|
|
2759
|
+
application/problem+json:
|
|
2760
|
+
schema:
|
|
2761
|
+
$ref: "#/components/schemas/Problem"
|
|
2762
|
+
"403":
|
|
2763
|
+
description: Forbidden
|
|
2764
|
+
content:
|
|
2765
|
+
application/problem+json:
|
|
2766
|
+
schema:
|
|
2767
|
+
$ref: "#/components/schemas/Problem"
|
|
2768
|
+
"404":
|
|
2769
|
+
description: Not found
|
|
2770
|
+
content:
|
|
2771
|
+
application/problem+json:
|
|
2772
|
+
schema:
|
|
2773
|
+
$ref: "#/components/schemas/Problem"
|
|
2774
|
+
"409":
|
|
2775
|
+
description: Conflict
|
|
2776
|
+
content:
|
|
2777
|
+
application/problem+json:
|
|
2778
|
+
schema:
|
|
2779
|
+
$ref: "#/components/schemas/Problem"
|
|
2780
|
+
"422":
|
|
2781
|
+
description: Invalid input
|
|
2782
|
+
content:
|
|
2783
|
+
application/problem+json:
|
|
2784
|
+
schema:
|
|
2785
|
+
$ref: "#/components/schemas/Problem"
|
|
2786
|
+
"426":
|
|
2787
|
+
description: Client outdated
|
|
2788
|
+
content:
|
|
2789
|
+
application/problem+json:
|
|
2790
|
+
schema:
|
|
2791
|
+
$ref: "#/components/schemas/Problem"
|
|
2792
|
+
"429":
|
|
2793
|
+
description: Rate limited
|
|
2794
|
+
content:
|
|
2795
|
+
application/problem+json:
|
|
2796
|
+
schema:
|
|
2797
|
+
$ref: "#/components/schemas/Problem"
|
|
2798
|
+
"503":
|
|
2799
|
+
description: Passkeys are not configured
|
|
2800
|
+
content:
|
|
2801
|
+
application/problem+json:
|
|
2802
|
+
schema:
|
|
2803
|
+
$ref: "#/components/schemas/Problem"
|
|
2804
|
+
/auth/passkeys/login/options:
|
|
2805
|
+
parameters: *a1
|
|
2806
|
+
post:
|
|
2807
|
+
tags:
|
|
2808
|
+
- auth
|
|
2809
|
+
operationId: passkeyLoginOptions
|
|
2810
|
+
summary: Begin discoverable passkey sign-in
|
|
2811
|
+
security: []
|
|
2812
|
+
requestBody:
|
|
2813
|
+
required: true
|
|
2814
|
+
content:
|
|
2815
|
+
application/json:
|
|
2816
|
+
schema:
|
|
2817
|
+
$ref: "#/components/schemas/PasskeyLoginOptions"
|
|
2818
|
+
responses:
|
|
2819
|
+
"200":
|
|
2820
|
+
description: Success
|
|
2821
|
+
content:
|
|
2822
|
+
application/json:
|
|
2823
|
+
schema:
|
|
2824
|
+
$ref: "#/components/schemas/PasskeyOptions"
|
|
2825
|
+
"400":
|
|
2826
|
+
description: Bad request
|
|
2827
|
+
content:
|
|
2828
|
+
application/problem+json:
|
|
2829
|
+
schema:
|
|
2830
|
+
$ref: "#/components/schemas/Problem"
|
|
2831
|
+
"401":
|
|
2832
|
+
description: Unauthorized
|
|
2833
|
+
content:
|
|
2834
|
+
application/problem+json:
|
|
2835
|
+
schema:
|
|
2836
|
+
$ref: "#/components/schemas/Problem"
|
|
2837
|
+
"403":
|
|
2838
|
+
description: Forbidden
|
|
2839
|
+
content:
|
|
2840
|
+
application/problem+json:
|
|
2841
|
+
schema:
|
|
2842
|
+
$ref: "#/components/schemas/Problem"
|
|
2843
|
+
"404":
|
|
2844
|
+
description: Not found
|
|
2845
|
+
content:
|
|
2846
|
+
application/problem+json:
|
|
2847
|
+
schema:
|
|
2848
|
+
$ref: "#/components/schemas/Problem"
|
|
2849
|
+
"409":
|
|
2850
|
+
description: Conflict
|
|
2851
|
+
content:
|
|
2852
|
+
application/problem+json:
|
|
2853
|
+
schema:
|
|
2854
|
+
$ref: "#/components/schemas/Problem"
|
|
2855
|
+
"422":
|
|
2856
|
+
description: Invalid input
|
|
2857
|
+
content:
|
|
2858
|
+
application/problem+json:
|
|
2859
|
+
schema:
|
|
2860
|
+
$ref: "#/components/schemas/Problem"
|
|
2861
|
+
"426":
|
|
2862
|
+
description: Client outdated
|
|
2863
|
+
content:
|
|
2864
|
+
application/problem+json:
|
|
2865
|
+
schema:
|
|
2866
|
+
$ref: "#/components/schemas/Problem"
|
|
2867
|
+
"429":
|
|
2868
|
+
description: Rate limited
|
|
2869
|
+
content:
|
|
2870
|
+
application/problem+json:
|
|
2871
|
+
schema:
|
|
2872
|
+
$ref: "#/components/schemas/Problem"
|
|
2873
|
+
"503":
|
|
2874
|
+
description: Passkeys are not configured
|
|
2875
|
+
content:
|
|
2876
|
+
application/problem+json:
|
|
2877
|
+
schema:
|
|
2878
|
+
$ref: "#/components/schemas/Problem"
|
|
2879
|
+
/auth/passkeys/login/verify:
|
|
2880
|
+
parameters: *a1
|
|
2881
|
+
post:
|
|
2882
|
+
tags:
|
|
2883
|
+
- auth
|
|
2884
|
+
operationId: verifyPasskeyLogin
|
|
2885
|
+
summary: Verify a passkey and create a session
|
|
2886
|
+
security: []
|
|
2887
|
+
requestBody:
|
|
2888
|
+
required: true
|
|
2889
|
+
content:
|
|
2890
|
+
application/json:
|
|
2891
|
+
schema:
|
|
2892
|
+
$ref: "#/components/schemas/PasskeyVerification"
|
|
2893
|
+
responses:
|
|
2894
|
+
"200":
|
|
2895
|
+
description: Success
|
|
2896
|
+
content:
|
|
2897
|
+
application/json:
|
|
2898
|
+
schema:
|
|
2899
|
+
$ref: "#/components/schemas/TokenResponse"
|
|
2900
|
+
"400":
|
|
2901
|
+
description: Bad request
|
|
2902
|
+
content:
|
|
2903
|
+
application/problem+json:
|
|
2904
|
+
schema:
|
|
2905
|
+
$ref: "#/components/schemas/Problem"
|
|
2906
|
+
"401":
|
|
2907
|
+
description: Unauthorized
|
|
2908
|
+
content:
|
|
2909
|
+
application/problem+json:
|
|
2910
|
+
schema:
|
|
2911
|
+
$ref: "#/components/schemas/Problem"
|
|
2912
|
+
"403":
|
|
2913
|
+
description: Forbidden
|
|
2914
|
+
content:
|
|
2915
|
+
application/problem+json:
|
|
2916
|
+
schema:
|
|
2917
|
+
$ref: "#/components/schemas/Problem"
|
|
2918
|
+
"404":
|
|
2919
|
+
description: Not found
|
|
2920
|
+
content:
|
|
2921
|
+
application/problem+json:
|
|
2922
|
+
schema:
|
|
2923
|
+
$ref: "#/components/schemas/Problem"
|
|
2924
|
+
"409":
|
|
2925
|
+
description: Conflict
|
|
2926
|
+
content:
|
|
2927
|
+
application/problem+json:
|
|
2928
|
+
schema:
|
|
2929
|
+
$ref: "#/components/schemas/Problem"
|
|
2930
|
+
"422":
|
|
2931
|
+
description: Invalid input
|
|
2932
|
+
content:
|
|
2933
|
+
application/problem+json:
|
|
2934
|
+
schema:
|
|
2935
|
+
$ref: "#/components/schemas/Problem"
|
|
2936
|
+
"426":
|
|
2937
|
+
description: Client outdated
|
|
2938
|
+
content:
|
|
2939
|
+
application/problem+json:
|
|
2940
|
+
schema:
|
|
2941
|
+
$ref: "#/components/schemas/Problem"
|
|
2942
|
+
"429":
|
|
2943
|
+
description: Rate limited
|
|
2944
|
+
content:
|
|
2945
|
+
application/problem+json:
|
|
2946
|
+
schema:
|
|
2947
|
+
$ref: "#/components/schemas/Problem"
|
|
2948
|
+
"503":
|
|
2949
|
+
description: Passkeys are not configured
|
|
2950
|
+
content:
|
|
2951
|
+
application/problem+json:
|
|
2952
|
+
schema:
|
|
2953
|
+
$ref: "#/components/schemas/Problem"
|
|
2954
|
+
|
|
2955
|
+
/me/recovery-status:
|
|
2956
|
+
parameters: &a1
|
|
2957
|
+
- $ref: "#/components/parameters/ClientVersion"
|
|
2958
|
+
- $ref: "#/components/parameters/ClientApiVersion"
|
|
2959
|
+
get:
|
|
2960
|
+
tags:
|
|
2961
|
+
- auth
|
|
2962
|
+
operationId: getRecoveryOverview
|
|
2963
|
+
summary: List recovery methods and their current setup status
|
|
2964
|
+
responses:
|
|
2965
|
+
"200":
|
|
2966
|
+
description: Success
|
|
2967
|
+
content:
|
|
2968
|
+
application/json:
|
|
2969
|
+
schema:
|
|
2970
|
+
$ref: "#/components/schemas/RecoveryOverview"
|
|
2971
|
+
"400":
|
|
2972
|
+
description: Invalid recovery credentials
|
|
2973
|
+
content:
|
|
2974
|
+
application/problem+json:
|
|
2975
|
+
schema:
|
|
2976
|
+
$ref: "#/components/schemas/Problem"
|
|
2977
|
+
"401":
|
|
2978
|
+
description: Unauthorized
|
|
2979
|
+
content:
|
|
2980
|
+
application/problem+json:
|
|
2981
|
+
schema:
|
|
2982
|
+
$ref: "#/components/schemas/Problem"
|
|
2983
|
+
"403":
|
|
2984
|
+
description: Forbidden
|
|
2985
|
+
content:
|
|
2986
|
+
application/problem+json:
|
|
2987
|
+
schema:
|
|
2988
|
+
$ref: "#/components/schemas/Problem"
|
|
2989
|
+
"409":
|
|
2990
|
+
description: Security delay or conflicting state
|
|
2991
|
+
content:
|
|
2992
|
+
application/problem+json:
|
|
2993
|
+
schema:
|
|
2994
|
+
$ref: "#/components/schemas/Problem"
|
|
2995
|
+
"422":
|
|
2996
|
+
description: Invalid input
|
|
2997
|
+
content:
|
|
2998
|
+
application/problem+json:
|
|
2999
|
+
schema:
|
|
3000
|
+
$ref: "#/components/schemas/Problem"
|
|
3001
|
+
"426":
|
|
3002
|
+
description: Client outdated
|
|
3003
|
+
content:
|
|
3004
|
+
application/problem+json:
|
|
3005
|
+
schema:
|
|
3006
|
+
$ref: "#/components/schemas/Problem"
|
|
3007
|
+
"429":
|
|
3008
|
+
description: Rate limited
|
|
3009
|
+
content:
|
|
3010
|
+
application/problem+json:
|
|
3011
|
+
schema:
|
|
3012
|
+
$ref: "#/components/schemas/Problem"
|
|
3013
|
+
"503":
|
|
3014
|
+
description: Email recovery is not configured
|
|
3015
|
+
content:
|
|
3016
|
+
application/problem+json:
|
|
3017
|
+
schema:
|
|
3018
|
+
$ref: "#/components/schemas/Problem"
|
|
3019
|
+
/me/email:
|
|
3020
|
+
parameters: *a1
|
|
3021
|
+
get:
|
|
3022
|
+
tags:
|
|
3023
|
+
- auth
|
|
3024
|
+
operationId: getRecoveryEmail
|
|
3025
|
+
summary: Get private recovery email status
|
|
3026
|
+
responses:
|
|
3027
|
+
"200":
|
|
3028
|
+
description: Success
|
|
3029
|
+
content:
|
|
3030
|
+
application/json:
|
|
3031
|
+
schema:
|
|
3032
|
+
$ref: "#/components/schemas/RecoveryEmailStatus"
|
|
3033
|
+
"400":
|
|
3034
|
+
description: Invalid recovery credentials
|
|
3035
|
+
content:
|
|
3036
|
+
application/problem+json:
|
|
3037
|
+
schema:
|
|
3038
|
+
$ref: "#/components/schemas/Problem"
|
|
3039
|
+
"401":
|
|
3040
|
+
description: Unauthorized
|
|
3041
|
+
content:
|
|
3042
|
+
application/problem+json:
|
|
3043
|
+
schema:
|
|
3044
|
+
$ref: "#/components/schemas/Problem"
|
|
3045
|
+
"403":
|
|
3046
|
+
description: Forbidden
|
|
3047
|
+
content:
|
|
3048
|
+
application/problem+json:
|
|
3049
|
+
schema:
|
|
3050
|
+
$ref: "#/components/schemas/Problem"
|
|
3051
|
+
"409":
|
|
3052
|
+
description: Security delay or conflicting state
|
|
3053
|
+
content:
|
|
3054
|
+
application/problem+json:
|
|
3055
|
+
schema:
|
|
3056
|
+
$ref: "#/components/schemas/Problem"
|
|
3057
|
+
"422":
|
|
3058
|
+
description: Invalid input
|
|
3059
|
+
content:
|
|
3060
|
+
application/problem+json:
|
|
3061
|
+
schema:
|
|
3062
|
+
$ref: "#/components/schemas/Problem"
|
|
3063
|
+
"426":
|
|
3064
|
+
description: Client outdated
|
|
3065
|
+
content:
|
|
3066
|
+
application/problem+json:
|
|
3067
|
+
schema:
|
|
3068
|
+
$ref: "#/components/schemas/Problem"
|
|
3069
|
+
"429":
|
|
3070
|
+
description: Rate limited
|
|
3071
|
+
content:
|
|
3072
|
+
application/problem+json:
|
|
3073
|
+
schema:
|
|
3074
|
+
$ref: "#/components/schemas/Problem"
|
|
3075
|
+
"503":
|
|
3076
|
+
description: Email recovery is not configured
|
|
3077
|
+
content:
|
|
3078
|
+
application/problem+json:
|
|
3079
|
+
schema:
|
|
3080
|
+
$ref: "#/components/schemas/Problem"
|
|
3081
|
+
put:
|
|
3082
|
+
tags:
|
|
3083
|
+
- auth
|
|
3084
|
+
operationId: setRecoveryEmail
|
|
3085
|
+
summary: Set or resend a private recovery email for verification
|
|
3086
|
+
requestBody:
|
|
3087
|
+
required: true
|
|
3088
|
+
content:
|
|
3089
|
+
application/json:
|
|
3090
|
+
schema:
|
|
3091
|
+
$ref: "#/components/schemas/SetRecoveryEmail"
|
|
3092
|
+
responses:
|
|
3093
|
+
"204":
|
|
3094
|
+
description: Success
|
|
3095
|
+
"400":
|
|
3096
|
+
description: Invalid recovery credentials
|
|
3097
|
+
content:
|
|
3098
|
+
application/problem+json:
|
|
3099
|
+
schema:
|
|
3100
|
+
$ref: "#/components/schemas/Problem"
|
|
3101
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
3102
|
+
"403":
|
|
3103
|
+
description: Forbidden
|
|
3104
|
+
content:
|
|
3105
|
+
application/problem+json:
|
|
3106
|
+
schema:
|
|
3107
|
+
$ref: "#/components/schemas/Problem"
|
|
3108
|
+
"409":
|
|
3109
|
+
description: Security delay or conflicting state
|
|
3110
|
+
content:
|
|
3111
|
+
application/problem+json:
|
|
3112
|
+
schema:
|
|
3113
|
+
$ref: "#/components/schemas/Problem"
|
|
3114
|
+
"422":
|
|
3115
|
+
description: Invalid input
|
|
3116
|
+
content:
|
|
3117
|
+
application/problem+json:
|
|
3118
|
+
schema:
|
|
3119
|
+
$ref: "#/components/schemas/Problem"
|
|
3120
|
+
"426":
|
|
3121
|
+
description: Client outdated
|
|
3122
|
+
content:
|
|
3123
|
+
application/problem+json:
|
|
3124
|
+
schema:
|
|
3125
|
+
$ref: "#/components/schemas/Problem"
|
|
3126
|
+
"429":
|
|
3127
|
+
description: Rate limited
|
|
3128
|
+
content:
|
|
3129
|
+
application/problem+json:
|
|
3130
|
+
schema:
|
|
3131
|
+
$ref: "#/components/schemas/Problem"
|
|
3132
|
+
"503":
|
|
3133
|
+
description: Email recovery is not configured
|
|
3134
|
+
content:
|
|
3135
|
+
application/problem+json:
|
|
3136
|
+
schema:
|
|
3137
|
+
$ref: "#/components/schemas/Problem"
|
|
3138
|
+
delete:
|
|
3139
|
+
tags:
|
|
3140
|
+
- auth
|
|
3141
|
+
operationId: removeRecoveryEmail
|
|
3142
|
+
summary: Remove the recovery email after password verification
|
|
3143
|
+
requestBody:
|
|
3144
|
+
required: true
|
|
3145
|
+
content:
|
|
3146
|
+
application/json:
|
|
3147
|
+
schema:
|
|
3148
|
+
$ref: "#/components/schemas/RemoveRecoveryEmail"
|
|
3149
|
+
responses:
|
|
3150
|
+
"204":
|
|
3151
|
+
description: Success
|
|
3152
|
+
"400":
|
|
3153
|
+
description: Invalid recovery credentials
|
|
3154
|
+
content:
|
|
3155
|
+
application/problem+json:
|
|
3156
|
+
schema:
|
|
3157
|
+
$ref: "#/components/schemas/Problem"
|
|
3158
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
3159
|
+
"403":
|
|
3160
|
+
description: Forbidden
|
|
3161
|
+
content:
|
|
3162
|
+
application/problem+json:
|
|
3163
|
+
schema:
|
|
3164
|
+
$ref: "#/components/schemas/Problem"
|
|
3165
|
+
"409":
|
|
3166
|
+
description: Security delay or conflicting state
|
|
3167
|
+
content:
|
|
3168
|
+
application/problem+json:
|
|
3169
|
+
schema:
|
|
3170
|
+
$ref: "#/components/schemas/Problem"
|
|
3171
|
+
"422":
|
|
3172
|
+
description: Invalid input
|
|
3173
|
+
content:
|
|
3174
|
+
application/problem+json:
|
|
3175
|
+
schema:
|
|
3176
|
+
$ref: "#/components/schemas/Problem"
|
|
3177
|
+
"426":
|
|
3178
|
+
description: Client outdated
|
|
3179
|
+
content:
|
|
3180
|
+
application/problem+json:
|
|
3181
|
+
schema:
|
|
3182
|
+
$ref: "#/components/schemas/Problem"
|
|
3183
|
+
"429":
|
|
3184
|
+
description: Rate limited
|
|
3185
|
+
content:
|
|
3186
|
+
application/problem+json:
|
|
3187
|
+
schema:
|
|
3188
|
+
$ref: "#/components/schemas/Problem"
|
|
3189
|
+
"503":
|
|
3190
|
+
description: Email recovery is not configured
|
|
3191
|
+
content:
|
|
3192
|
+
application/problem+json:
|
|
3193
|
+
schema:
|
|
3194
|
+
$ref: "#/components/schemas/Problem"
|
|
3195
|
+
/auth/email/verify:
|
|
3196
|
+
parameters: *a1
|
|
3197
|
+
post:
|
|
3198
|
+
tags:
|
|
3199
|
+
- auth
|
|
3200
|
+
operationId: verifyRecoveryEmail
|
|
3201
|
+
summary: Consume a one-time email verification token
|
|
3202
|
+
security: []
|
|
3203
|
+
requestBody:
|
|
3204
|
+
required: true
|
|
3205
|
+
content:
|
|
3206
|
+
application/json:
|
|
3207
|
+
schema:
|
|
3208
|
+
$ref: "#/components/schemas/VerifyRecoveryEmail"
|
|
3209
|
+
responses:
|
|
3210
|
+
"204":
|
|
3211
|
+
description: Success
|
|
3212
|
+
"400":
|
|
3213
|
+
description: Invalid recovery credentials
|
|
3214
|
+
content:
|
|
3215
|
+
application/problem+json:
|
|
3216
|
+
schema:
|
|
3217
|
+
$ref: "#/components/schemas/Problem"
|
|
3218
|
+
"401":
|
|
3219
|
+
description: Unauthorized
|
|
3220
|
+
content:
|
|
3221
|
+
application/problem+json:
|
|
3222
|
+
schema:
|
|
3223
|
+
$ref: "#/components/schemas/Problem"
|
|
3224
|
+
"403":
|
|
3225
|
+
description: Forbidden
|
|
3226
|
+
content:
|
|
3227
|
+
application/problem+json:
|
|
3228
|
+
schema:
|
|
3229
|
+
$ref: "#/components/schemas/Problem"
|
|
3230
|
+
"409":
|
|
3231
|
+
description: Security delay or conflicting state
|
|
3232
|
+
content:
|
|
3233
|
+
application/problem+json:
|
|
3234
|
+
schema:
|
|
3235
|
+
$ref: "#/components/schemas/Problem"
|
|
3236
|
+
"422":
|
|
3237
|
+
description: Invalid input
|
|
3238
|
+
content:
|
|
3239
|
+
application/problem+json:
|
|
3240
|
+
schema:
|
|
3241
|
+
$ref: "#/components/schemas/Problem"
|
|
3242
|
+
"426":
|
|
3243
|
+
description: Client outdated
|
|
3244
|
+
content:
|
|
3245
|
+
application/problem+json:
|
|
3246
|
+
schema:
|
|
3247
|
+
$ref: "#/components/schemas/Problem"
|
|
3248
|
+
"429":
|
|
3249
|
+
description: Rate limited
|
|
3250
|
+
content:
|
|
3251
|
+
application/problem+json:
|
|
3252
|
+
schema:
|
|
3253
|
+
$ref: "#/components/schemas/Problem"
|
|
3254
|
+
"503":
|
|
3255
|
+
description: Email recovery is not configured
|
|
3256
|
+
content:
|
|
3257
|
+
application/problem+json:
|
|
3258
|
+
schema:
|
|
3259
|
+
$ref: "#/components/schemas/Problem"
|
|
3260
|
+
/auth/password-reset/request:
|
|
3261
|
+
parameters: *a1
|
|
3262
|
+
post:
|
|
3263
|
+
tags:
|
|
3264
|
+
- auth
|
|
3265
|
+
operationId: requestEmailReset
|
|
3266
|
+
summary: Queue a recovery email without revealing whether an account exists
|
|
3267
|
+
security: []
|
|
3268
|
+
requestBody:
|
|
3269
|
+
required: true
|
|
3270
|
+
content:
|
|
3271
|
+
application/json:
|
|
3272
|
+
schema:
|
|
3273
|
+
$ref: "#/components/schemas/EmailResetRequest"
|
|
3274
|
+
responses:
|
|
3275
|
+
"202":
|
|
3276
|
+
description: Success
|
|
3277
|
+
content:
|
|
3278
|
+
application/json:
|
|
3279
|
+
schema:
|
|
3280
|
+
$ref: "#/components/schemas/EmailResetAccepted"
|
|
3281
|
+
"400":
|
|
3282
|
+
description: Invalid recovery credentials
|
|
3283
|
+
content:
|
|
3284
|
+
application/problem+json:
|
|
3285
|
+
schema:
|
|
3286
|
+
$ref: "#/components/schemas/Problem"
|
|
3287
|
+
"401":
|
|
3288
|
+
description: Unauthorized
|
|
3289
|
+
content:
|
|
3290
|
+
application/problem+json:
|
|
3291
|
+
schema:
|
|
3292
|
+
$ref: "#/components/schemas/Problem"
|
|
3293
|
+
"403":
|
|
3294
|
+
description: Forbidden
|
|
3295
|
+
content:
|
|
3296
|
+
application/problem+json:
|
|
3297
|
+
schema:
|
|
3298
|
+
$ref: "#/components/schemas/Problem"
|
|
3299
|
+
"409":
|
|
3300
|
+
description: Security delay or conflicting state
|
|
3301
|
+
content:
|
|
3302
|
+
application/problem+json:
|
|
3303
|
+
schema:
|
|
3304
|
+
$ref: "#/components/schemas/Problem"
|
|
3305
|
+
"422":
|
|
3306
|
+
description: Invalid input
|
|
3307
|
+
content:
|
|
3308
|
+
application/problem+json:
|
|
3309
|
+
schema:
|
|
3310
|
+
$ref: "#/components/schemas/Problem"
|
|
3311
|
+
"426":
|
|
3312
|
+
description: Client outdated
|
|
3313
|
+
content:
|
|
3314
|
+
application/problem+json:
|
|
3315
|
+
schema:
|
|
3316
|
+
$ref: "#/components/schemas/Problem"
|
|
3317
|
+
"429":
|
|
3318
|
+
description: Rate limited
|
|
3319
|
+
content:
|
|
3320
|
+
application/problem+json:
|
|
3321
|
+
schema:
|
|
3322
|
+
$ref: "#/components/schemas/Problem"
|
|
3323
|
+
"503":
|
|
3324
|
+
description: Email recovery is not configured
|
|
3325
|
+
content:
|
|
3326
|
+
application/problem+json:
|
|
3327
|
+
schema:
|
|
3328
|
+
$ref: "#/components/schemas/Problem"
|
|
3329
|
+
/auth/password-reset/confirm:
|
|
3330
|
+
parameters: *a1
|
|
3331
|
+
post:
|
|
3332
|
+
tags:
|
|
3333
|
+
- auth
|
|
3334
|
+
operationId: confirmEmailReset
|
|
3335
|
+
summary: Reset a password using a one-time email token and the current second
|
|
3336
|
+
factor
|
|
3337
|
+
security: []
|
|
3338
|
+
requestBody:
|
|
3339
|
+
required: true
|
|
3340
|
+
content:
|
|
3341
|
+
application/json:
|
|
3342
|
+
schema:
|
|
3343
|
+
$ref: "#/components/schemas/EmailResetConfirmation"
|
|
3344
|
+
responses:
|
|
3345
|
+
"200":
|
|
3346
|
+
description: Success
|
|
3347
|
+
content:
|
|
3348
|
+
application/json:
|
|
3349
|
+
schema:
|
|
3350
|
+
$ref: "#/components/schemas/RegisterResponse"
|
|
3351
|
+
"400":
|
|
3352
|
+
description: Invalid recovery credentials
|
|
3353
|
+
content:
|
|
3354
|
+
application/problem+json:
|
|
3355
|
+
schema:
|
|
3356
|
+
$ref: "#/components/schemas/Problem"
|
|
3357
|
+
"401": { $ref: "#/components/responses/TwoFactorRequired" }
|
|
3358
|
+
"403":
|
|
3359
|
+
description: Forbidden
|
|
3360
|
+
content:
|
|
3361
|
+
application/problem+json:
|
|
3362
|
+
schema:
|
|
3363
|
+
$ref: "#/components/schemas/Problem"
|
|
3364
|
+
"404":
|
|
3365
|
+
$ref: "#/components/responses/NotFound"
|
|
3366
|
+
"409":
|
|
3367
|
+
description: Security delay or conflicting state
|
|
3368
|
+
content:
|
|
3369
|
+
application/problem+json:
|
|
3370
|
+
schema:
|
|
3371
|
+
$ref: "#/components/schemas/Problem"
|
|
3372
|
+
"422":
|
|
3373
|
+
description: Invalid input
|
|
3374
|
+
content:
|
|
3375
|
+
application/problem+json:
|
|
3376
|
+
schema:
|
|
3377
|
+
$ref: "#/components/schemas/Problem"
|
|
3378
|
+
"426":
|
|
3379
|
+
description: Client outdated
|
|
3380
|
+
content:
|
|
3381
|
+
application/problem+json:
|
|
3382
|
+
schema:
|
|
3383
|
+
$ref: "#/components/schemas/Problem"
|
|
3384
|
+
"429":
|
|
3385
|
+
description: Rate limited
|
|
3386
|
+
content:
|
|
3387
|
+
application/problem+json:
|
|
3388
|
+
schema:
|
|
3389
|
+
$ref: "#/components/schemas/Problem"
|
|
3390
|
+
"503":
|
|
3391
|
+
description: Email recovery is not configured
|
|
3392
|
+
content:
|
|
3393
|
+
application/problem+json:
|
|
3394
|
+
schema:
|
|
3395
|
+
$ref: "#/components/schemas/Problem"
|
|
3396
|
+
|
|
3397
|
+
components:
|
|
3398
|
+
securitySchemes:
|
|
3399
|
+
bearerAuth:
|
|
3400
|
+
type: http
|
|
3401
|
+
scheme: bearer
|
|
3402
|
+
bearerFormat: JWT
|
|
3403
|
+
description: Short-lived access token from login or refresh.
|
|
3404
|
+
cookieAuth:
|
|
3405
|
+
type: apiKey
|
|
3406
|
+
in: cookie
|
|
3407
|
+
name: refresh_token
|
|
3408
|
+
description: HttpOnly refresh cookie set by login; scoped to `/api/v2/auth`.
|
|
3409
|
+
|
|
3410
|
+
headers:
|
|
3411
|
+
RefreshCookie:
|
|
3412
|
+
description: HttpOnly, Secure, SameSite refresh cookie scoped to `/api/v2/auth/refresh`, so the browser sends it only to the refresh endpoint. Login and refresh set it; logout clears it (expired cookie).
|
|
3413
|
+
schema: { type: string }
|
|
3414
|
+
|
|
3415
|
+
parameters:
|
|
3416
|
+
QrLoginRequestId:
|
|
3417
|
+
name: requestId
|
|
3418
|
+
in: path
|
|
3419
|
+
required: true
|
|
3420
|
+
description: ID of a QR sign-in request, from the QR code link `/qr/<request_id>`.
|
|
3421
|
+
schema: { type: string, minLength: 32, maxLength: 128, pattern: "^[A-Za-z0-9_-]+$" }
|
|
3422
|
+
Limit:
|
|
3423
|
+
name: limit
|
|
3424
|
+
in: query
|
|
3425
|
+
description: Page size
|
|
3426
|
+
schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
|
|
3427
|
+
After:
|
|
3428
|
+
name: after
|
|
3429
|
+
in: query
|
|
3430
|
+
description: Opaque `next_cursor` of the previous page
|
|
2151
3431
|
schema: { $ref: "#/components/schemas/Cursor" }
|
|
2152
3432
|
ChatId:
|
|
2153
3433
|
name: chat_id
|
|
@@ -2209,6 +3489,17 @@ components:
|
|
|
2209
3489
|
content:
|
|
2210
3490
|
application/problem+json:
|
|
2211
3491
|
schema: { $ref: "#/components/schemas/Problem" }
|
|
3492
|
+
TwoFactorRequired:
|
|
3493
|
+
description: |
|
|
3494
|
+
Missing, invalid or expired credentials, or `code: two_factor_required`. `two_factor_required` means every
|
|
3495
|
+
other credential in the request was valid, the account has two-factor on, and no `code` was sent. Nothing
|
|
3496
|
+
single-use (recovery key, email token, recovery request) was consumed: send the same request again with
|
|
3497
|
+
`code`. Invalid other credentials get the usual error of the operation instead, whatever the two-factor
|
|
3498
|
+
state, and a wrong `code` keeps its usual error. On operations that need an access token this 401 does not
|
|
3499
|
+
mean the session is lost; tell the two apart by `code`.
|
|
3500
|
+
content:
|
|
3501
|
+
application/problem+json:
|
|
3502
|
+
schema: { $ref: "#/components/schemas/Problem" }
|
|
2212
3503
|
Forbidden:
|
|
2213
3504
|
description: Authenticated but not allowed
|
|
2214
3505
|
content:
|
|
@@ -2510,20 +3801,25 @@ components:
|
|
|
2510
3801
|
type: array
|
|
2511
3802
|
items:
|
|
2512
3803
|
type: object
|
|
2513
|
-
required: [id, type, created_at]
|
|
3804
|
+
required: [id, type, created_at, location]
|
|
2514
3805
|
properties:
|
|
2515
3806
|
id: { $ref: "#/components/schemas/Id" }
|
|
2516
3807
|
type: { type: string }
|
|
2517
3808
|
created_at: { type: string, format: date-time }
|
|
3809
|
+
user_agent: { type: [string, "null"], maxLength: 512 }
|
|
3810
|
+
location:
|
|
3811
|
+
description: Approximate location of the client when the event happened; `null` when unknown.
|
|
3812
|
+
oneOf:
|
|
3813
|
+
- $ref: "#/components/schemas/Location"
|
|
3814
|
+
- type: "null"
|
|
2518
3815
|
RegisterResponse:
|
|
2519
3816
|
allOf:
|
|
2520
3817
|
- $ref: "#/components/schemas/TokenResponse"
|
|
2521
3818
|
- type: object
|
|
2522
|
-
description:
|
|
3819
|
+
description: The one-time recovery key shown after registration.
|
|
2523
3820
|
properties:
|
|
2524
3821
|
recovery_key: { type: string, description: Shown once; only a keyed hash is stored }
|
|
2525
|
-
|
|
2526
|
-
qr_part: { type: string }
|
|
3822
|
+
username: { $ref: "#/components/schemas/Username" }
|
|
2527
3823
|
TwoFactorChallenge:
|
|
2528
3824
|
type: object
|
|
2529
3825
|
required: [two_factor_required, login_challenge]
|
|
@@ -2534,24 +3830,6 @@ components:
|
|
|
2534
3830
|
oneOf:
|
|
2535
3831
|
- $ref: "#/components/schemas/TokenResponse"
|
|
2536
3832
|
- $ref: "#/components/schemas/TwoFactorChallenge"
|
|
2537
|
-
RecoveryRequest:
|
|
2538
|
-
type: object
|
|
2539
|
-
required: [username]
|
|
2540
|
-
properties:
|
|
2541
|
-
username: { $ref: "#/components/schemas/Username" }
|
|
2542
|
-
device_part: { type: string }
|
|
2543
|
-
qr_part: { type: string }
|
|
2544
|
-
RecoveryResponse:
|
|
2545
|
-
type: object
|
|
2546
|
-
required: [recovery_token]
|
|
2547
|
-
properties:
|
|
2548
|
-
recovery_token: { type: string }
|
|
2549
|
-
ResetPasswordRequest:
|
|
2550
|
-
type: object
|
|
2551
|
-
required: [recovery_token, new_password]
|
|
2552
|
-
properties:
|
|
2553
|
-
recovery_token: { type: string }
|
|
2554
|
-
new_password: { type: string, minLength: 8, maxLength: 128, writeOnly: true }
|
|
2555
3833
|
WebSocketTicket:
|
|
2556
3834
|
type: object
|
|
2557
3835
|
required: [ticket, expires_in]
|
|
@@ -2598,7 +3876,7 @@ components:
|
|
|
2598
3876
|
session_duration_days: { type: integer, minimum: 1, maximum: 365 }
|
|
2599
3877
|
Session:
|
|
2600
3878
|
type: object
|
|
2601
|
-
required: [id, created_at, last_used_at, is_current]
|
|
3879
|
+
required: [id, created_at, last_used_at, is_current, location]
|
|
2602
3880
|
properties:
|
|
2603
3881
|
id: { type: string, format: uuid }
|
|
2604
3882
|
device:
|
|
@@ -2608,6 +3886,25 @@ components:
|
|
|
2608
3886
|
last_used_at: { $ref: "#/components/schemas/Timestamp" }
|
|
2609
3887
|
expires_at: { $ref: "#/components/schemas/Timestamp" }
|
|
2610
3888
|
is_current: { type: boolean }
|
|
3889
|
+
location:
|
|
3890
|
+
description: Approximate location of the device when it signed in or last refreshed its token; `null` when unknown.
|
|
3891
|
+
oneOf:
|
|
3892
|
+
- $ref: "#/components/schemas/Location"
|
|
3893
|
+
- type: "null"
|
|
3894
|
+
Location:
|
|
3895
|
+
type: object
|
|
3896
|
+
description: |
|
|
3897
|
+
Approximate location of a client address, looked up in an offline GeoIP database (IP geolocation by
|
|
3898
|
+
DB-IP, https://db-ip.com, CC BY 4.0). The address itself is never exposed. Each field is `null` when the
|
|
3899
|
+
database does not know it; clients localise the country name from `country_code`.
|
|
3900
|
+
required: [city, country_code, country]
|
|
3901
|
+
properties:
|
|
3902
|
+
city: { type: [string, "null"], description: City name in English }
|
|
3903
|
+
country_code:
|
|
3904
|
+
type: [string, "null"]
|
|
3905
|
+
pattern: "^[A-Z]{2}$"
|
|
3906
|
+
description: ISO 3166-1 alpha-2 country code
|
|
3907
|
+
country: { type: [string, "null"], description: Country name in English }
|
|
2611
3908
|
TwoFactorSetup:
|
|
2612
3909
|
type: object
|
|
2613
3910
|
required: [secret, otpauth_uri]
|
|
@@ -2629,7 +3926,7 @@ components:
|
|
|
2629
3926
|
description: One-time codes for signing in without the authenticator app. Shown only once.
|
|
2630
3927
|
DisableTwoFactorRequest:
|
|
2631
3928
|
type: object
|
|
2632
|
-
required: [password
|
|
3929
|
+
required: [password]
|
|
2633
3930
|
properties:
|
|
2634
3931
|
password: { type: string, maxLength: 128, writeOnly: true }
|
|
2635
3932
|
code:
|
|
@@ -3249,3 +4546,319 @@ components:
|
|
|
3249
4546
|
type: [string, "null"]
|
|
3250
4547
|
description: First message of a direct-message request
|
|
3251
4548
|
created_at: { $ref: "#/components/schemas/Timestamp" }
|
|
4549
|
+
|
|
4550
|
+
DeviceRecoveryStart:
|
|
4551
|
+
type: object
|
|
4552
|
+
required:
|
|
4553
|
+
- username
|
|
4554
|
+
properties:
|
|
4555
|
+
username:
|
|
4556
|
+
$ref: "#/components/schemas/Username"
|
|
4557
|
+
DeviceRecoveryResult:
|
|
4558
|
+
type: object
|
|
4559
|
+
required:
|
|
4560
|
+
- request_id
|
|
4561
|
+
- match_number
|
|
4562
|
+
- expires_at
|
|
4563
|
+
properties:
|
|
4564
|
+
request_id:
|
|
4565
|
+
type: string
|
|
4566
|
+
minLength: 32
|
|
4567
|
+
maxLength: 128
|
|
4568
|
+
pattern: ^[A-Za-z0-9_-]+$
|
|
4569
|
+
match_number:
|
|
4570
|
+
type: integer
|
|
4571
|
+
minimum: 10
|
|
4572
|
+
maximum: 99
|
|
4573
|
+
expires_at:
|
|
4574
|
+
type: string
|
|
4575
|
+
format: date-time
|
|
4576
|
+
DeviceRecoveryStatus:
|
|
4577
|
+
type: object
|
|
4578
|
+
required:
|
|
4579
|
+
- status
|
|
4580
|
+
properties:
|
|
4581
|
+
status:
|
|
4582
|
+
type: string
|
|
4583
|
+
enum:
|
|
4584
|
+
- pending
|
|
4585
|
+
- approved
|
|
4586
|
+
- denied
|
|
4587
|
+
- used
|
|
4588
|
+
- expired
|
|
4589
|
+
DeviceRecoveryApproval:
|
|
4590
|
+
type: object
|
|
4591
|
+
required:
|
|
4592
|
+
- match_number
|
|
4593
|
+
- password
|
|
4594
|
+
properties:
|
|
4595
|
+
match_number:
|
|
4596
|
+
type: integer
|
|
4597
|
+
minimum: 10
|
|
4598
|
+
maximum: 99
|
|
4599
|
+
password:
|
|
4600
|
+
type: string
|
|
4601
|
+
maxLength: 1024
|
|
4602
|
+
code:
|
|
4603
|
+
type: string
|
|
4604
|
+
maxLength: 128
|
|
4605
|
+
DeviceRecoveryComplete:
|
|
4606
|
+
type: object
|
|
4607
|
+
required:
|
|
4608
|
+
- new_password
|
|
4609
|
+
properties:
|
|
4610
|
+
new_password:
|
|
4611
|
+
type: string
|
|
4612
|
+
minLength: 8
|
|
4613
|
+
maxLength: 128
|
|
4614
|
+
writeOnly: true
|
|
4615
|
+
|
|
4616
|
+
QrLoginStatus:
|
|
4617
|
+
type: string
|
|
4618
|
+
description: |
|
|
4619
|
+
`pending` until a signed-in device opens the request, `scanned` once it did, then `approved` or `denied`.
|
|
4620
|
+
`expired` when the request timed out or was used (only in poll answers).
|
|
4621
|
+
enum: [pending, scanned, approved, denied, expired]
|
|
4622
|
+
QrLoginStart:
|
|
4623
|
+
type: object
|
|
4624
|
+
required: [request_id, poll_token, url, expires_at]
|
|
4625
|
+
properties:
|
|
4626
|
+
request_id:
|
|
4627
|
+
type: string
|
|
4628
|
+
minLength: 32
|
|
4629
|
+
maxLength: 128
|
|
4630
|
+
pattern: ^[A-Za-z0-9_-]+$
|
|
4631
|
+
poll_token:
|
|
4632
|
+
type: string
|
|
4633
|
+
minLength: 32
|
|
4634
|
+
maxLength: 128
|
|
4635
|
+
pattern: ^[A-Za-z0-9_-]+$
|
|
4636
|
+
description: Secret of the requesting device; only a keyed hash is stored.
|
|
4637
|
+
url:
|
|
4638
|
+
type: string
|
|
4639
|
+
description: "`<RECOVERY_WEB_URL>/qr/<request_id>`, the content of the QR code."
|
|
4640
|
+
expires_at: { $ref: "#/components/schemas/Timestamp" }
|
|
4641
|
+
QrLoginRequest:
|
|
4642
|
+
type: object
|
|
4643
|
+
required: [request_id, status, device, location, created_at, expires_at]
|
|
4644
|
+
properties:
|
|
4645
|
+
request_id: { type: string }
|
|
4646
|
+
status: { $ref: "#/components/schemas/QrLoginStatus" }
|
|
4647
|
+
device:
|
|
4648
|
+
type: [string, "null"]
|
|
4649
|
+
maxLength: 512
|
|
4650
|
+
description: User agent of the device that asks to sign in; `null` when it sent none.
|
|
4651
|
+
location:
|
|
4652
|
+
description: Approximate location of the device that asks to sign in; `null` when unknown.
|
|
4653
|
+
oneOf:
|
|
4654
|
+
- $ref: "#/components/schemas/Location"
|
|
4655
|
+
- type: "null"
|
|
4656
|
+
created_at: { $ref: "#/components/schemas/Timestamp" }
|
|
4657
|
+
expires_at: { $ref: "#/components/schemas/Timestamp" }
|
|
4658
|
+
QrLoginPollRequest:
|
|
4659
|
+
type: object
|
|
4660
|
+
required: [poll_token]
|
|
4661
|
+
properties:
|
|
4662
|
+
poll_token: { type: string, maxLength: 128, writeOnly: true }
|
|
4663
|
+
QrLoginPollResult:
|
|
4664
|
+
oneOf:
|
|
4665
|
+
- type: object
|
|
4666
|
+
required: [status]
|
|
4667
|
+
properties:
|
|
4668
|
+
status:
|
|
4669
|
+
type: string
|
|
4670
|
+
enum: [pending, scanned, denied, expired]
|
|
4671
|
+
- allOf:
|
|
4672
|
+
- $ref: "#/components/schemas/TokenResponse"
|
|
4673
|
+
- type: object
|
|
4674
|
+
required: [status]
|
|
4675
|
+
properties:
|
|
4676
|
+
status: { type: string, const: approved }
|
|
4677
|
+
QrLoginApproval:
|
|
4678
|
+
type: object
|
|
4679
|
+
properties:
|
|
4680
|
+
code:
|
|
4681
|
+
type: string
|
|
4682
|
+
maxLength: 128
|
|
4683
|
+
description: An authenticator code or an unused recovery code; required when two-factor is on.
|
|
4684
|
+
|
|
4685
|
+
PasskeyOptions:
|
|
4686
|
+
type: object
|
|
4687
|
+
required:
|
|
4688
|
+
- challenge_id
|
|
4689
|
+
- public_key
|
|
4690
|
+
properties:
|
|
4691
|
+
challenge_id: &a1
|
|
4692
|
+
type: string
|
|
4693
|
+
minLength: 32
|
|
4694
|
+
maxLength: 128
|
|
4695
|
+
public_key: &a2
|
|
4696
|
+
type: object
|
|
4697
|
+
additionalProperties: true
|
|
4698
|
+
PasskeyRegistration:
|
|
4699
|
+
type: object
|
|
4700
|
+
required:
|
|
4701
|
+
- password
|
|
4702
|
+
- name
|
|
4703
|
+
properties:
|
|
4704
|
+
password: &a4
|
|
4705
|
+
type: string
|
|
4706
|
+
maxLength: 128
|
|
4707
|
+
writeOnly: true
|
|
4708
|
+
code:
|
|
4709
|
+
type: string
|
|
4710
|
+
maxLength: 128
|
|
4711
|
+
name: &a3
|
|
4712
|
+
type: string
|
|
4713
|
+
minLength: 1
|
|
4714
|
+
maxLength: 64
|
|
4715
|
+
PasskeyVerification:
|
|
4716
|
+
type: object
|
|
4717
|
+
required:
|
|
4718
|
+
- challenge_id
|
|
4719
|
+
- credential
|
|
4720
|
+
properties:
|
|
4721
|
+
challenge_id: *a1
|
|
4722
|
+
credential: *a2
|
|
4723
|
+
PasskeyLoginOptions:
|
|
4724
|
+
type: object
|
|
4725
|
+
required: []
|
|
4726
|
+
properties:
|
|
4727
|
+
username:
|
|
4728
|
+
$ref: "#/components/schemas/Username"
|
|
4729
|
+
PasskeyRename:
|
|
4730
|
+
type: object
|
|
4731
|
+
required:
|
|
4732
|
+
- name
|
|
4733
|
+
properties:
|
|
4734
|
+
name: *a3
|
|
4735
|
+
PasskeyRemoval:
|
|
4736
|
+
type: object
|
|
4737
|
+
required: []
|
|
4738
|
+
properties:
|
|
4739
|
+
password: *a4
|
|
4740
|
+
Passkey:
|
|
4741
|
+
type: object
|
|
4742
|
+
required:
|
|
4743
|
+
- id
|
|
4744
|
+
- name
|
|
4745
|
+
- created_at
|
|
4746
|
+
- last_used_at
|
|
4747
|
+
properties:
|
|
4748
|
+
id:
|
|
4749
|
+
type: string
|
|
4750
|
+
name: *a3
|
|
4751
|
+
created_at:
|
|
4752
|
+
type: string
|
|
4753
|
+
format: date-time
|
|
4754
|
+
last_used_at:
|
|
4755
|
+
type:
|
|
4756
|
+
- string
|
|
4757
|
+
- "null"
|
|
4758
|
+
format: date-time
|
|
4759
|
+
PasskeyPage:
|
|
4760
|
+
type: object
|
|
4761
|
+
required:
|
|
4762
|
+
- items
|
|
4763
|
+
properties:
|
|
4764
|
+
items:
|
|
4765
|
+
type: array
|
|
4766
|
+
items:
|
|
4767
|
+
$ref: "#/components/schemas/Passkey"
|
|
4768
|
+
|
|
4769
|
+
RecoveryEmailStatus:
|
|
4770
|
+
type: object
|
|
4771
|
+
required:
|
|
4772
|
+
- address
|
|
4773
|
+
- state
|
|
4774
|
+
- enabled
|
|
4775
|
+
properties:
|
|
4776
|
+
address:
|
|
4777
|
+
type: string
|
|
4778
|
+
state:
|
|
4779
|
+
type: string
|
|
4780
|
+
enum:
|
|
4781
|
+
- not_added
|
|
4782
|
+
- pending
|
|
4783
|
+
- verified
|
|
4784
|
+
enabled:
|
|
4785
|
+
type: boolean
|
|
4786
|
+
RecoveryOverview:
|
|
4787
|
+
type: object
|
|
4788
|
+
required:
|
|
4789
|
+
- recovery_key
|
|
4790
|
+
- other_devices
|
|
4791
|
+
- passkeys
|
|
4792
|
+
- passkeys_enabled
|
|
4793
|
+
- email
|
|
4794
|
+
properties:
|
|
4795
|
+
recovery_key:
|
|
4796
|
+
$ref: "#/components/schemas/RecoveryKeyStatus"
|
|
4797
|
+
other_devices:
|
|
4798
|
+
type: integer
|
|
4799
|
+
minimum: 0
|
|
4800
|
+
passkeys:
|
|
4801
|
+
type: integer
|
|
4802
|
+
minimum: 0
|
|
4803
|
+
passkeys_enabled:
|
|
4804
|
+
type: boolean
|
|
4805
|
+
email:
|
|
4806
|
+
$ref: "#/components/schemas/RecoveryEmailStatus"
|
|
4807
|
+
SetRecoveryEmail:
|
|
4808
|
+
type: object
|
|
4809
|
+
required:
|
|
4810
|
+
- email
|
|
4811
|
+
- password
|
|
4812
|
+
properties:
|
|
4813
|
+
email:
|
|
4814
|
+
type: string
|
|
4815
|
+
format: email
|
|
4816
|
+
maxLength: 254
|
|
4817
|
+
password: &a1
|
|
4818
|
+
type: string
|
|
4819
|
+
minLength: 8
|
|
4820
|
+
maxLength: 128
|
|
4821
|
+
writeOnly: true
|
|
4822
|
+
code: &a2
|
|
4823
|
+
type: string
|
|
4824
|
+
maxLength: 128
|
|
4825
|
+
RemoveRecoveryEmail:
|
|
4826
|
+
type: object
|
|
4827
|
+
required:
|
|
4828
|
+
- password
|
|
4829
|
+
properties:
|
|
4830
|
+
password: *a1
|
|
4831
|
+
code: *a2
|
|
4832
|
+
VerifyRecoveryEmail:
|
|
4833
|
+
type: object
|
|
4834
|
+
required:
|
|
4835
|
+
- token
|
|
4836
|
+
properties:
|
|
4837
|
+
token: &a3
|
|
4838
|
+
type: string
|
|
4839
|
+
minLength: 32
|
|
4840
|
+
maxLength: 128
|
|
4841
|
+
EmailResetRequest:
|
|
4842
|
+
type: object
|
|
4843
|
+
required:
|
|
4844
|
+
- username_or_email
|
|
4845
|
+
properties:
|
|
4846
|
+
username_or_email:
|
|
4847
|
+
type: string
|
|
4848
|
+
maxLength: 254
|
|
4849
|
+
EmailResetConfirmation:
|
|
4850
|
+
type: object
|
|
4851
|
+
required:
|
|
4852
|
+
- token
|
|
4853
|
+
- new_password
|
|
4854
|
+
properties:
|
|
4855
|
+
token: *a3
|
|
4856
|
+
new_password: *a1
|
|
4857
|
+
code: *a2
|
|
4858
|
+
EmailResetAccepted:
|
|
4859
|
+
type: object
|
|
4860
|
+
required:
|
|
4861
|
+
- message
|
|
4862
|
+
properties:
|
|
4863
|
+
message:
|
|
4864
|
+
type: string
|