A better Rust ATProto crate
1

Configure Feed

Select the types of activity you want to include in your feed.

at main 5.3 kB View raw
1use jacquard_common::session::SessionStoreError; 2use miette::Diagnostic; 3#[cfg(feature = "scope-check")] 4use smol_str::SmolStr; 5 6use crate::request::RequestError; 7use crate::resolver::ResolverError; 8 9/// High-level errors emitted by OAuth helpers. 10#[derive(Debug, thiserror::Error, Diagnostic)] 11#[non_exhaustive] 12pub enum OAuthError { 13 /// An error occurred during identity or metadata resolution. 14 #[error(transparent)] 15 #[diagnostic(code(jacquard_oauth::resolver))] 16 Resolver(#[from] ResolverError), 17 18 /// An error occurred while making an OAuth HTTP request. 19 #[error(transparent)] 20 #[diagnostic(code(jacquard_oauth::request))] 21 Request(#[from] RequestError), 22 23 /// An error occurred reading or writing session state. 24 #[error(transparent)] 25 #[diagnostic(code(jacquard_oauth::storage))] 26 Storage(#[from] SessionStoreError), 27 28 /// An error occurred during DPoP proof generation or validation. 29 #[error(transparent)] 30 #[diagnostic(code(jacquard_oauth::dpop))] 31 Dpop(#[from] crate::dpop::DpopError), 32 33 /// An error occurred with the client's key set. 34 #[error(transparent)] 35 #[diagnostic(code(jacquard_oauth::keyset))] 36 Keyset(#[from] crate::keyset::Error), 37 38 /// An ATProto-specific OAuth error (e.g. scope validation, client ID). 39 #[error(transparent)] 40 #[diagnostic(code(jacquard_oauth::atproto))] 41 Atproto(#[from] crate::atproto::Error), 42 43 /// An error occurred managing or refreshing an OAuth session. 44 #[error(transparent)] 45 #[diagnostic(code(jacquard_oauth::session))] 46 Session(#[from] crate::session::Error), 47 48 /// A JSON serialization or deserialization error. 49 #[error(transparent)] 50 #[diagnostic(code(jacquard_oauth::serde_json))] 51 SerdeJson(#[from] serde_json::Error), 52 53 /// A URI parse error. 54 #[error(transparent)] 55 #[diagnostic(code(jacquard_oauth::url))] 56 Url(#[from] jacquard_common::deps::fluent_uri::ParseError), 57 58 /// A form (URL-encoded) serialization error. 59 #[error(transparent)] 60 #[diagnostic(code(jacquard_oauth::form))] 61 Form(#[from] serde_html_form::ser::Error), 62 63 /// Invalid OAuth helper input. 64 #[error("invalid OAuth request: {0}")] 65 #[diagnostic(code(jacquard_oauth::invalid_request))] 66 InvalidRequest(String), 67 68 /// An error validating an authorization callback. 69 #[error(transparent)] 70 #[diagnostic(code(jacquard_oauth::callback))] 71 Callback(#[from] CallbackError), 72 73 /// An error occurred checking request scope permissions. 74 #[cfg(feature = "scope-check")] 75 #[error(transparent)] 76 #[diagnostic(transparent)] 77 ScopeCheck(#[from] ScopeError), 78} 79 80/// Typed callback validation errors (redirect handling). 81#[derive(Debug, thiserror::Error, Diagnostic)] 82#[non_exhaustive] 83pub enum CallbackError { 84 /// The `state` parameter was absent from the authorization callback. 85 /// 86 /// State is required to prevent CSRF attacks per RFC 6749 §10.12. 87 #[error("missing state parameter in callback")] 88 #[diagnostic(code(jacquard_oauth::callback::missing_state))] 89 MissingState, 90 /// The `iss` (issuer) parameter was absent from the authorization callback. 91 /// 92 /// RFC 9207 requires `iss` to be present so that clients can reject 93 /// mix-up attacks from malicious authorization servers. 94 #[error("missing `iss` parameter")] 95 #[diagnostic(code(jacquard_oauth::callback::missing_iss))] 96 MissingIssuer, 97 /// The issuer in the callback did not match the expected authorization server. 98 #[error("issuer mismatch: expected {expected}, got {got}")] 99 #[diagnostic(code(jacquard_oauth::callback::issuer_mismatch))] 100 IssuerMismatch { 101 /// The issuer that was expected. 102 expected: String, 103 /// The issuer that was actually present in the callback. 104 got: String, 105 }, 106 /// The authorization request timed out before a callback was received. 107 #[error("timeout")] 108 #[diagnostic(code(jacquard_oauth::callback::timeout))] 109 Timeout, 110 /// The local loopback callback server could not start or accept callbacks. 111 #[error("loopback callback server error: {0}")] 112 #[diagnostic(code(jacquard_oauth::callback::loopback_server))] 113 LoopbackServer(String), 114 /// An error occurred resolving permission sets during session creation. 115 #[cfg(feature = "scope-check")] 116 #[error("scope resolution failed: {detail}")] 117 #[diagnostic(code(jacquard_oauth::callback::scope_resolution))] 118 ScopeResolution { 119 /// Description of the resolution failure. 120 detail: String, 121 }, 122} 123 124/// Error returned when a request's required scope is not covered by the session's granted scopes. 125#[cfg(feature = "scope-check")] 126#[derive(Debug, thiserror::Error, Diagnostic)] 127#[error("request to `{nsid}` not permitted: no granted scope covers this endpoint")] 128#[diagnostic( 129 code(jacquard_oauth::scope_check), 130 help("granted scopes: {granted}. The endpoint requires an `rpc:` scope covering `{nsid}`.") 131)] 132pub struct ScopeError { 133 /// The NSID of the XRPC method that was denied. 134 pub nsid: SmolStr, 135 /// Human-readable summary of the granted scopes for diagnostic output. 136 pub granted: SmolStr, 137} 138 139/// Convenience alias for `Result<T, OAuthError>`. 140pub type Result<T> = core::result::Result<T, OAuthError>;