atproto-oauth 0.14.2

OAuth workflow implementation for AT Protocol - PKCE, DPoP, and secure authentication flows
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
//! # Structured Error Types for OAuth Operations
//!
//! Comprehensive error handling for AT Protocol OAuth operations using structured error types
//! with the `thiserror` library. All errors follow the project convention of prefixed error codes
//! with descriptive messages.
//!
//! ## Error Categories
//!
//! - **`JWTError`** (jwt-1 to jwt-18): JSON Web Token validation, parsing, and verification errors
//! - **`JWKError`** (jwk-1 to jwk-7): JSON Web Key conversion, processing, and thumbprint errors
//! - **`OAuthClientError`** (client-1 to client-14): OAuth client operations and server communication errors
//! - **`ResourceValidationError`** (resource-1 to resource-2): OAuth protected resource configuration validation errors
//! - **`AuthServerValidationError`** (auth-server-1 to auth-server-12): OAuth authorization server configuration validation errors  
//! - **`DpopError`** (dpop-1 to dpop-6): DPoP (Demonstration of Proof-of-Possession) operation errors
//! - **`OAuthStorageError`** (storage-1 to storage-4): OAuth request storage operations including cache lock failures and data access errors
//!
//! ## Error Format
//!
//! All errors use the standardized format: `error-atproto-oauth-{domain}-{number} {message}: {details}`

use thiserror::Error;

/// Error types that can occur when working with JSON Web Tokens
#[derive(Debug, Error)]
pub enum JWTError {
    /// Occurs when JWT does not have the expected 3-part format (header.payload.signature)
    #[error("error-atproto-oauth-jwt-1 Invalid JWT format: expected 3 parts separated by dots")]
    InvalidFormat,

    /// Occurs when JWT header cannot be base64 decoded or parsed as JSON
    #[error("error-atproto-oauth-jwt-2 Invalid JWT header: failed to decode or parse")]
    InvalidHeader,

    /// Occurs when JWT algorithm does not match the provided key type
    #[error(
        "error-atproto-oauth-jwt-3 Unsupported JWT algorithm: algorithm {algorithm} incompatible with key type {key_type}"
    )]
    UnsupportedAlgorithm {
        /// The algorithm specified in the JWT header
        algorithm: String,
        /// The type of key provided for verification
        key_type: String,
    },

    /// Occurs when JWT claims cannot be base64 decoded or parsed as JSON
    #[error("error-atproto-oauth-jwt-4 Invalid JWT claims: failed to decode or parse")]
    InvalidClaims,

    /// Occurs when JWT signature cannot be base64 decoded
    #[error("error-atproto-oauth-jwt-5 Invalid JWT signature: failed to decode signature")]
    InvalidSignature,

    /// Occurs when system time cannot be obtained for timestamp validation
    #[error("error-atproto-oauth-jwt-6 System time error: unable to get current timestamp")]
    SystemTimeError,

    /// Occurs when JWT has passed its expiration time
    #[error("error-atproto-oauth-jwt-7 JWT expired: token is past expiration time")]
    TokenExpired,

    /// Occurs when JWT is used before its not-before time
    #[error("error-atproto-oauth-jwt-8 JWT not valid yet: token is before not-before time")]
    TokenNotValidYet,

    /// Occurs when signature verification fails
    #[error("error-atproto-oauth-jwt-9 Signature verification failed: invalid signature")]
    SignatureVerificationFailed,

    /// Occurs when JWT payload cannot be base64 decoded
    #[error("error-atproto-oauth-jwt-10 Invalid JWT payload: failed to decode payload")]
    InvalidPayload,

    /// Occurs when JWT payload cannot be parsed as JSON
    #[error("error-atproto-oauth-jwt-11 Invalid JWT payload JSON: failed to parse payload as JSON")]
    InvalidPayloadJson,

    /// Occurs when a required JWT claim is missing
    #[error("error-atproto-oauth-jwt-12 Missing required claim: {claim}")]
    MissingClaim {
        /// The name of the missing claim
        claim: String,
    },

    /// Occurs when JWT type field has wrong value
    #[error("error-atproto-oauth-jwt-13 Invalid token type: expected '{expected}', got '{actual}'")]
    InvalidTokenType {
        /// The expected token type
        expected: String,
        /// The actual token type found
        actual: String,
    },

    /// Occurs when HTTP method in JWT doesn't match expected value
    #[error(
        "error-atproto-oauth-jwt-14 HTTP method mismatch: expected '{expected}', got '{actual}'"
    )]
    HttpMethodMismatch {
        /// The expected HTTP method
        expected: String,
        /// The actual HTTP method in the JWT
        actual: String,
    },

    /// Occurs when HTTP URI in JWT doesn't match expected value
    #[error("error-atproto-oauth-jwt-15 HTTP URI mismatch: expected '{expected}', got '{actual}'")]
    HttpUriMismatch {
        /// The expected HTTP URI
        expected: String,
        /// The actual HTTP URI in the JWT
        actual: String,
    },

    /// Occurs when access token hash validation fails
    #[error("error-atproto-oauth-jwt-16 Access token hash mismatch: invalid 'ath' claim")]
    AccessTokenHashMismatch,

    /// Occurs when nonce value is not in the expected values list
    #[error("error-atproto-oauth-jwt-17 Invalid nonce: value '{nonce}' not in expected values")]
    InvalidNonce {
        /// The nonce value that was not found in expected values
        nonce: String,
    },

    /// Occurs when JWT has invalid timestamp claim
    #[error("error-atproto-oauth-jwt-18 Invalid timestamp: {reason}")]
    InvalidTimestamp {
        /// The reason for the timestamp validation failure
        reason: String,
    },
}

/// Error types that can occur when working with JSON Web Keys
#[derive(Debug, Error)]
pub enum JWKError {
    /// Occurs when P-256 JWK conversion to KeyData fails
    #[error("error-atproto-oauth-jwk-1 P-256 JWK conversion failed: unable to convert to KeyData")]
    P256ConversionFailed,

    /// Occurs when P-384 JWK conversion to KeyData fails
    #[error("error-atproto-oauth-jwk-2 P-384 JWK conversion failed: unable to convert to KeyData")]
    P384ConversionFailed,

    /// Occurs when K-256 JWK conversion to KeyData fails
    #[error("error-atproto-oauth-jwk-3 K-256 JWK conversion failed: unable to convert to KeyData")]
    K256ConversionFailed,

    /// Occurs when an unsupported elliptic curve is encountered
    #[error("error-atproto-oauth-jwk-4 Unsupported curve: {curve}")]
    UnsupportedCurve {
        /// The unsupported curve name
        curve: String,
    },

    /// Occurs when an unsupported key type is encountered
    #[error("error-atproto-oauth-jwk-5 Unsupported key type: {kty}")]
    UnsupportedKeyType {
        /// The unsupported key type
        kty: String,
    },

    /// Occurs when a required field is missing from the JWK
    #[error("error-atproto-oauth-jwk-6 Missing required field: {field}")]
    MissingField {
        /// The missing field name
        field: String,
    },

    /// Occurs when JWK serialization fails
    #[error("error-atproto-oauth-jwk-7 JWK serialization failed: {message}")]
    SerializationError {
        /// The serialization error message
        message: String,
    },
}

/// Represents errors that can occur during OAuth client operations.
///
/// These errors are related to the OAuth client functionality, including
/// interacting with authorization servers, protected resources, and token management.
#[derive(Debug, Error)]
pub enum OAuthClientError {
    /// Error when a request to the authorization server fails.
    ///
    /// This error occurs when the OAuth client fails to establish a connection
    /// or complete a request to the authorization server.
    #[error("error-atproto-oauth-client-1 Authorization Server Request Failed: {0:?}")]
    AuthorizationServerRequestFailed(reqwest::Error),

    /// Error when the authorization server response is malformed.
    ///
    /// This error occurs when the response from the authorization server
    /// cannot be properly parsed or processed.
    #[error("error-atproto-oauth-client-2 Malformed Authorization Server Response: {0:?}")]
    MalformedAuthorizationServerResponse(reqwest::Error),

    /// Error when the authorization server response is invalid.
    ///
    /// This error occurs when the response from the authorization server
    /// is well-formed but contains invalid or unexpected data.
    #[error("error-atproto-oauth-client-3 Invalid Authorization Server Response: {0:?}")]
    InvalidAuthorizationServerResponse(anyhow::Error),

    /// Error when an OAuth protected resource is invalid.
    ///
    /// This error occurs when trying to access a protected resource that
    /// is not properly configured for OAuth access.
    #[error("error-atproto-oauth-client-4 Invalid OAuth Protected Resource")]
    InvalidOAuthProtectedResource,

    /// Error when a request to an OAuth protected resource fails.
    ///
    /// This error occurs when the OAuth client fails to establish a connection
    /// or complete a request to a protected resource.
    #[error("error-atproto-oauth-client-5 OAuth Protected Resource Request Failed: {0:?}")]
    OAuthProtectedResourceRequestFailed(reqwest::Error),

    /// Error when a protected resource response is malformed.
    ///
    /// This error occurs when the response from a protected resource
    /// cannot be properly parsed or processed.
    #[error("error-atproto-oauth-client-6 Malformed OAuth Protected Resource Response: {0:?}")]
    MalformedOAuthProtectedResourceResponse(reqwest::Error),

    /// Error when a protected resource response is invalid.
    ///
    /// This error occurs when the response from a protected resource
    /// is well-formed but contains invalid or unexpected data.
    #[error("error-atproto-oauth-client-7 Invalid OAuth Protected Resource Response: {0:?}")]
    InvalidOAuthProtectedResourceResponse(anyhow::Error),

    /// Error when token minting fails.
    ///
    /// This error occurs when the system fails to mint (create) a new
    /// OAuth token, typically due to cryptographic or validation issues.
    #[error("error-atproto-oauth-client-8 Token minting failed: {0:?}")]
    MintTokenFailed(anyhow::Error),

    /// Error when JWT header creation from key data fails.
    ///
    /// This error occurs when attempting to create a JWT header from
    /// cryptographic key data during OAuth workflow operations.
    #[error("error-atproto-oauth-client-9 JWT header creation from key failed: {0:?}")]
    JWTHeaderCreationFailed(anyhow::Error),

    /// Error when DPoP token creation fails.
    ///
    /// This error occurs when attempting to create a DPoP proof token
    /// during OAuth workflow operations.
    #[error("error-atproto-oauth-client-10 DPoP token creation failed: {0:?}")]
    DpopTokenCreationFailed(anyhow::Error),

    /// Error when PAR (Pushed Authorization Request) HTTP request fails.
    ///
    /// This error occurs when the HTTP request to the pushed authorization
    /// request endpoint fails during OAuth workflow operations.
    #[error("error-atproto-oauth-client-11 PAR HTTP request failed: {0:?}")]
    PARHttpRequestFailed(reqwest_middleware::Error),

    /// Error when PAR response JSON parsing fails.
    ///
    /// This error occurs when the response from the pushed authorization
    /// request endpoint cannot be parsed as JSON.
    #[error("error-atproto-oauth-client-12 PAR response JSON parsing failed: {0:?}")]
    PARResponseJsonParsingFailed(reqwest::Error),

    /// Error when token endpoint HTTP request fails.
    ///
    /// This error occurs when the HTTP request to the token endpoint
    /// fails during OAuth token exchange operations.
    #[error("error-atproto-oauth-client-13 Token endpoint HTTP request failed: {0:?}")]
    TokenHttpRequestFailed(reqwest_middleware::Error),

    /// Error when token response JSON parsing fails.
    ///
    /// This error occurs when the response from the token endpoint
    /// cannot be parsed as JSON.
    #[error("error-atproto-oauth-client-14 Token response JSON parsing failed: {0:?}")]
    TokenResponseJsonParsingFailed(reqwest::Error),
}

/// Represents errors that can occur during OAuth resource validation.
///
/// These errors occur when validating the configuration of an OAuth resource server
/// against the requirements of the AT Protocol.
#[derive(Debug, Error)]
pub enum ResourceValidationError {
    /// Error when the resource server URI doesn't match the PDS URI.
    ///
    /// This error occurs when the resource server URI in the OAuth configuration
    /// does not match the expected Personal Data Server (PDS) URI, which is required
    /// for proper AT Protocol OAuth integration.
    #[error("error-atproto-oauth-resource-1 Resource must match PDS")]
    ResourceMustMatchPds,

    /// Error when the authorization servers list doesn't contain exactly one server.
    ///
    /// This error occurs when the OAuth resource configuration doesn't specify
    /// exactly one authorization server as required by AT Protocol specification.
    #[error("error-atproto-oauth-resource-2 Authorization servers must contain exactly one server")]
    AuthorizationServersMustContainExactlyOne,
}

/// Represents errors that can occur during OAuth authorization server validation.
///
/// These errors occur when validating the configuration of an OAuth authorization server
/// against the requirements specified by the AT Protocol.
#[derive(Debug, Error)]
pub enum AuthServerValidationError {
    /// Error when the authorization server issuer doesn't match the PDS.
    ///
    /// This error occurs when the issuer URI in the OAuth authorization server metadata
    /// does not match the expected Personal Data Server (PDS) URI.
    #[error("error-atproto-oauth-auth-server-1 Issuer must match PDS")]
    IssuerMustMatchPds,

    /// Error when the 'code' response type is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'code' response type,
    /// which is required for the authorization code grant flow in AT Protocol.
    #[error("error-atproto-oauth-auth-server-2 Response types supported must include 'code'")]
    ResponseTypesSupportMustIncludeCode,

    /// Error when the 'authorization_code' grant type is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'authorization_code'
    /// grant type, which is required for the AT Protocol OAuth flow.
    #[error(
        "error-atproto-oauth-auth-server-3 Grant types supported must include 'authorization_code'"
    )]
    GrantTypesSupportMustIncludeAuthorizationCode,

    /// Error when the 'refresh_token' grant type is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'refresh_token'
    /// grant type, which is required for maintaining long-term access in AT Protocol.
    #[error("error-atproto-oauth-auth-server-4 Grant types supported must include 'refresh_token'")]
    GrantTypesSupportMustIncludeRefreshToken,

    /// Error when the 'S256' code challenge method is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'S256' code
    /// challenge method for PKCE, which is required for secure authorization code flow.
    #[error(
        "error-atproto-oauth-auth-server-5 Code challenge methods supported must include 'S256'"
    )]
    CodeChallengeMethodsSupportedMustIncludeS256,

    /// Error when the 'none' token endpoint auth method is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'none'
    /// token endpoint authentication method, which is used for public clients.
    #[error(
        "error-atproto-oauth-auth-server-6 Token endpoint auth methods supported must include 'none'"
    )]
    TokenEndpointAuthMethodsSupportedMustIncludeNone,

    /// Error when the 'private_key_jwt' token endpoint auth method is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'private_key_jwt'
    /// token endpoint authentication method, which is required for AT Protocol clients.
    #[error(
        "error-atproto-oauth-auth-server-7 Token endpoint auth methods supported must include 'private_key_jwt'"
    )]
    TokenEndpointAuthMethodsSupportedMustIncludePrivateKeyJwt,

    /// Error when the 'ES256' signing algorithm is not supported for token endpoint auth.
    ///
    /// This error occurs when the authorization server doesn't support the 'ES256' signing
    /// algorithm for token endpoint authentication, which is required for AT Protocol.
    #[error(
        "error-atproto-oauth-auth-server-8 Token endpoint auth signing algorithm values must include 'ES256'"
    )]
    TokenEndpointAuthSigningAlgValuesMustIncludeES256,

    /// Error when the 'atproto' scope is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'atproto'
    /// scope, which is required for accessing AT Protocol resources.
    #[error("error-atproto-oauth-auth-server-9 Scopes supported must include 'atproto'")]
    ScopesSupportedMustIncludeAtProto,

    /// Error when the 'transition:generic' scope is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'transition:generic'
    /// scope, which is required for transitional functionality in AT Protocol.
    #[error(
        "error-atproto-oauth-auth-server-10 Scopes supported must include 'transition:generic'"
    )]
    ScopesSupportedMustIncludeTransitionGeneric,

    /// Error when the 'ES256' DPoP signing algorithm is not supported.
    ///
    /// This error occurs when the authorization server doesn't support the 'ES256'
    /// signing algorithm for DPoP proofs, which is required for AT Protocol security.
    #[error(
        "error-atproto-oauth-auth-server-11 DPoP signing algorithm values supported must include 'ES256'"
    )]
    DpopSigningAlgValuesSupportedMustIncludeES256,

    /// Error when required server features are not supported.
    ///
    /// This error occurs when the authorization server doesn't support required features
    /// such as pushed authorization requests, client ID metadata, or authorization response parameters.
    #[error(
        "error-atproto-oauth-auth-server-12 Authorization response parameters, pushed requests, client ID metadata must be supported"
    )]
    RequiredServerFeaturesMustBeSupported,
}

/// Represents errors that can occur during DPoP (Demonstration of Proof-of-Possession) operations.
///
/// These errors occur when creating, validating, or using DPoP proofs for OAuth security.
#[derive(Debug, Error)]
pub enum DpopError {
    /// Error when server returns an unexpected OAuth error.
    ///
    /// This error occurs when the server returns an OAuth error other than
    /// the expected "use_dpop_nonce" error during DPoP nonce handling.
    #[error("error-atproto-oauth-dpop-1 Unexpected OAuth error: {error}")]
    UnexpectedOAuthError {
        /// The unexpected error returned by the server
        error: String,
    },

    /// Error when DPoP-Nonce header is missing from server response.
    ///
    /// This error occurs when the server indicates that a DPoP nonce is required
    /// but fails to include the DPoP-Nonce header in the response.
    #[error("error-atproto-oauth-dpop-2 Missing DPoP-Nonce response header")]
    MissingDpopNonceHeader,

    /// Error when DPoP token minting fails.
    ///
    /// This error occurs when the system fails to create (mint) a DPoP proof token,
    /// typically due to cryptographic key issues or claim validation problems.
    #[error("error-atproto-oauth-dpop-3 DPoP token minting failed: {0:?}")]
    TokenMintingFailed(anyhow::Error),

    /// Error when HTTP header creation fails.
    ///
    /// This error occurs when the DPoP proof token cannot be converted into
    /// a valid HTTP header value, typically due to invalid characters.
    #[error("error-atproto-oauth-dpop-4 HTTP header creation failed: {0:?}")]
    HeaderCreationFailed(reqwest::header::InvalidHeaderValue),

    /// Error when response body JSON parsing fails.
    ///
    /// This error occurs when the server response body cannot be properly
    /// parsed as JSON, typically due to malformed content or network issues.
    #[error("error-atproto-oauth-dpop-5 Response body JSON parsing failed: {0:?}")]
    ResponseBodyParsingFailed(reqwest::Error),

    /// Error when response body is not a valid JSON object.
    ///
    /// This error occurs when the server response body is valid JSON but
    /// not in the expected object format for OAuth error responses.
    #[error("error-atproto-oauth-dpop-6 Response body is not a valid JSON object")]
    ResponseBodyObjectParsingFailed,
}

/// Error types that can occur when working with OAuth request storage operations
#[derive(Debug, Error)]
pub enum OAuthStorageError {
    /// Occurs when cache lock acquisition fails during OAuth request retrieval operations
    #[error(
        "error-atproto-oauth-storage-1 Cache lock acquisition failed for get operation: {details}"
    )]
    CacheLockFailedGet {
        /// Details about the lock failure
        details: String,
    },

    /// Occurs when cache lock acquisition fails during OAuth request insertion operations
    #[error(
        "error-atproto-oauth-storage-2 Cache lock acquisition failed for insert operation: {details}"
    )]
    CacheLockFailedInsert {
        /// Details about the lock failure
        details: String,
    },

    /// Occurs when cache lock acquisition fails during OAuth request deletion operations
    #[error(
        "error-atproto-oauth-storage-3 Cache lock acquisition failed for delete operation: {details}"
    )]
    CacheLockFailedDelete {
        /// Details about the lock failure
        details: String,
    },

    /// Occurs when cache lock acquisition fails during expired OAuth request cleanup operations
    #[error(
        "error-atproto-oauth-storage-4 Cache lock acquisition failed for cleanup operation: {details}"
    )]
    CacheLockFailedCleanup {
        /// Details about the lock failure
        details: String,
    },
}