passage_auth/
error.rs

1use std::fmt;
2
3use jsonwebtoken as jwt;
4use serde::Deserialize;
5
6use crate::models;
7
8#[derive(Debug, thiserror::Error)]
9pub enum PassageError {
10    /// Underlying error from reqwest library after an API call was made
11    #[error("http error: {0}")]
12    Reqwest(#[from] reqwest::Error),
13    /// Passage returns error object with details of API call failure
14    #[error("api error: {:?}", .0)]
15    ApiError(ApiError),
16    /// Error there's an issue serializing or deserializing data
17    #[error("failed to serializing or deserializing api response: {0}")]
18    Serde(#[from] serde_json::Error),
19    /// Error from client side validation
20    /// or when builder fails to build request before making API call
21    #[error("invalid args: {0}")]
22    InvalidArgument(String),
23    // Authentication failures when validating a JWT.
24    #[error("failed to authenticate: {0}")]
25    AuthError(AuthError),
26}
27
28/// The Passage API returns an error enum that can contain various
29/// unique codes for each status code, along with an associated error message.
30#[derive(Debug, Deserialize)]
31#[serde(untagged)]
32pub enum ApiError {
33    Status400(models::Model400Error),
34    Status401(models::Model401Error),
35    Status403(models::Model403Error),
36    Status404(models::Model404Error),
37    Status409(models::Model409Error),
38    Status500(models::Model500Error),
39    UnknownValue(serde_json::Value),
40}
41
42impl From<ApiError> for PassageError {
43    fn from(e: ApiError) -> Self {
44        PassageError::ApiError(e)
45    }
46}
47
48/// The error type for possible authentication failures when validating a JWT.
49#[derive(Debug, PartialEq)]
50pub enum AuthError {
51    /// Failed to decode the header of the Passage JWT
52    /// (e.g. the `psg_auth_token` cookie value).
53    /// See associated `jwt::errors::Error` for details.
54    TokenHeaderDecoding(jwt::errors::Error),
55
56    /// Key IDs of public JWK and Passage JWT do not match
57    KidMismatch(Option<String>, Option<String>),
58
59    /// Public JWK was not provided
60    PubKeyMissing,
61
62    /// Failed to parse the provided public JWK
63    PubKeyParsing(String),
64
65    /// Failed to decode and validate the Passage JWT
66    /// (e.g. the `psg_auth_token` cookie value).
67    /// See associated `jwt::errors::Error` for details.
68    TokenDecoding(jwt::errors::Error),
69}
70
71impl From<jwt::errors::Error> for PassageError {
72    fn from(e: jwt::errors::Error) -> Self {
73        AuthError::TokenDecoding(e).into()
74    }
75}
76
77impl From<AuthError> for PassageError {
78    fn from(e: AuthError) -> Self {
79        PassageError::AuthError(e)
80    }
81}
82
83impl fmt::Display for AuthError {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        match self {
86            AuthError::TokenHeaderDecoding(e) => {
87                write!(f, "Failed to decode the header of the Passage JWT: {}", e)
88            }
89            AuthError::KidMismatch(kid1, kid2) => write!(
90                f,
91                "Key IDs of public JWK and Passage JWT do not match: {} vs {}",
92                kid1.as_deref().unwrap_or("None"),
93                kid2.as_deref().unwrap_or("None")
94            ),
95            AuthError::PubKeyMissing => write!(f, "Public JWK was not provided"),
96            AuthError::PubKeyParsing(e) => {
97                write!(f, "Failed to parse the provided public JWK: {}", e)
98            }
99            AuthError::TokenDecoding(e) => {
100                write!(f, "Failed to decode and validate the Passage JWT: {}", e)
101            }
102        }
103    }
104}