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
use std::collections::HashMap;

use josekit::{jwk::Jwk, jwt::JwtPayload};
use serde::{Deserialize, Serialize};
use serde_json::Value;

/// # CallbackParams
/// These are the fields that was recieved from the Authorization server to the client
#[derive(Default, Serialize, Deserialize, Debug)]
pub struct CallbackParams {
    /// Access token obtained
    pub access_token: Option<String>,
    /// Authorization code for exchanging at token endpoint
    pub code: Option<String>,
    /// Error recieved from the Auth server. [See RFC](https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.7)
    pub error: Option<String>,
    /// Error description recieved from the Auth server. [See RFC](https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.7)
    pub error_description: Option<String>,
    /// Error uri recieved from the Auth server. [See RFC](https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.7)
    pub error_uri: Option<String>,
    /// Token expiry
    pub expires_in: Option<String>,
    /// Id token
    pub id_token: Option<String>,
    /// State that was recieved from the Auth server
    pub state: Option<String>,
    /// Specified the access token type
    pub token_type: Option<String>,
    /// Session state
    pub session_state: Option<String>,
    /// The JARM response
    pub response: Option<String>,
    /// Issuer
    pub iss: Option<String>,
    /// Other fields from Auth server
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub other: Option<HashMap<String, Value>>,
}

impl CallbackParams {
    pub(crate) fn from_jwt_payload(payload: &JwtPayload) -> Self {
        let mut params = Self {
            access_token: Self::json_value_to_string_option(payload.claim("access_token")),
            code: Self::json_value_to_string_option(payload.claim("code")),
            error: Self::json_value_to_string_option(payload.claim("error")),
            error_description: Self::json_value_to_string_option(
                payload.claim("error_description"),
            ),
            error_uri: Self::json_value_to_string_option(payload.claim("error_uri")),
            expires_in: Self::json_value_to_string_option(payload.claim("exp")),
            id_token: Self::json_value_to_string_option(payload.claim("id_token")),
            state: Self::json_value_to_string_option(payload.claim("state")),
            token_type: Self::json_value_to_string_option(payload.claim("token_type")),
            session_state: Self::json_value_to_string_option(payload.claim("session_state")),
            response: Self::json_value_to_string_option(payload.claim("response")),
            iss: Self::json_value_to_string_option(payload.claim("iss")),
            other: None,
        };

        let mut other = HashMap::<String, Value>::new();

        for (k, v) in payload.claims_set().iter() {
            other.insert(k.to_string(), v.to_owned());
        }

        params.other = Some(other);

        params
    }

    fn json_value_to_string_option(value: Option<&Value>) -> Option<String> {
        if let Some(v) = value {
            return v.as_str().map(|x| x.to_string());
        }

        None
    }
}

/// # CallbackExtras
/// Extra details to be used for the callback
pub struct CallbackExtras {
    /// Extra request body properties to be sent to the AS during code exchange.
    pub exchange_body: Option<HashMap<String, Value>>,
    /// Extra client assertion payload parameters to be sent as part of a client JWT assertion.
    /// This is only used when the client's token_endpoint_auth_method is either client_secret_jwt or private_key_jwt
    pub client_assertion_payload: Option<HashMap<String, Value>>,
    /// When provided the client will send a DPoP Proof JWT to the Token Endpoint.
    /// The DPoP Proof JWT's algorithm is determined automatically based on the type of key and the issuer metadata.
    pub dpop: Option<Jwk>,
}

/// # OAuthCallbackChecks
/// Checks that needs to be performed against the OAuth [CallbackParams] recieved from the Auth server.
#[derive(Default, Serialize, Deserialize, Clone)]
pub struct OAuthCallbackChecks {
    /// When provided the authorization response will be checked for presence of required parameters for a given response_type. Use of this check is recommended.
    pub response_type: Option<String>,
    /// Expected state from the response
    pub state: Option<String>,
    /// PKCE code verified to be sent to the token endpoint  
    pub code_verifier: Option<String>,
    /// Specifies that the response will be a JARM response
    pub jarm: Option<bool>,
}

/// # OpenIDCallbackChecks
/// Checks that needs to be performed against the Oidc [CallbackParams] recieved from the Auth server.
#[derive(Default, Serialize, Deserialize)]
pub struct OpenIDCallbackChecks {
    /// When provided the authorization response's ID Token auth_time parameter will be checked to be conform to the max_age value. Use of this check is required if you sent a max_age parameter into an authorization request. Default: uses client's default_max_age.
    pub max_age: Option<u64>,
    /// When provided the authorization response's ID Token nonce parameter will be checked to be the this expected one.
    pub nonce: Option<String>,
    /// See [OAuthCallbackChecks]
    pub oauth_checks: Option<OAuthCallbackChecks>,
}