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

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

use crate::helpers::get_serde_value_as_string;

/// # CallbackParams
/// These are the fields that is recieved from the Authorization server to the client.
/// Which of these fields are present will depend up on the type of authorization request
#[derive(Default, Serialize, 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 url
    pub iss: Option<String>,
    /// Scopes requested
    pub scope: Option<String>,
    /// Other fields received from Auth server
    #[serde(flatten, skip_serializing_if = "Option::is_none")]
    pub other: Option<HashMap<String, String>>,
}

impl CallbackParams {
    /// Sets the access_token field.
    pub fn access_token(mut self, access_token: Option<String>) -> Self {
        self.access_token = access_token;
        self
    }

    /// Sets the code field.
    pub fn code(mut self, code: Option<String>) -> Self {
        self.code = code;
        self
    }

    /// Sets the error field.
    pub fn error(mut self, error: Option<String>) -> Self {
        self.error = error;
        self
    }

    /// Sets the error_description field.
    pub fn error_description(mut self, error_description: Option<String>) -> Self {
        self.error_description = error_description;
        self
    }

    /// Sets the error_uri field.
    pub fn error_uri(mut self, error_uri: Option<String>) -> Self {
        self.error_uri = error_uri;
        self
    }

    /// Sets the expires_in field.
    pub fn expires_in(mut self, expires_in: Option<String>) -> Self {
        self.expires_in = expires_in;
        self
    }

    /// Sets the id_token field.
    pub fn id_token(mut self, id_token: Option<String>) -> Self {
        self.id_token = id_token;
        self
    }

    /// Sets the state field.
    pub fn state(mut self, state: Option<String>) -> Self {
        self.state = state;
        self
    }

    /// Sets the token_type field.
    pub fn token_type(mut self, token_type: Option<String>) -> Self {
        self.token_type = token_type;
        self
    }

    /// Sets the session_state field.
    pub fn session_state(mut self, session_state: Option<String>) -> Self {
        self.session_state = session_state;
        self
    }

    /// Sets the response field.
    pub fn response(mut self, response: Option<String>) -> Self {
        self.response = response;
        self
    }

    /// Sets the iss field.
    pub fn iss(mut self, iss: Option<String>) -> Self {
        self.iss = iss;
        self
    }

    /// Sets the scope field.
    pub fn scope(mut self, scope: Option<String>) -> Self {
        self.scope = scope;
        self
    }

    /// Sets the other field.
    pub fn other(mut self, other: Option<HashMap<String, String>>) -> Self {
        self.other = other;
        self
    }
}

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")),
            scope: Self::json_value_to_string_option(payload.claim("scope")),
            other: None,
        };

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

        for (k, v) in payload.claims_set().iter() {
            if let Ok(v_string) = get_serde_value_as_string(v) {
                other.insert(k.to_string(), v_string);
            }
        }

        params.other = Some(other);

        params
    }

    fn json_value_to_string_option(value: Option<&Value>) -> Option<String> {
        if let Some(v) = value {
            return get_serde_value_as_string(v).ok();
        }

        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, String>>,
    /// 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.
    pub dpop: Option<Jwk>,
}

/// # OAuthCallbackChecks
/// Checks that needs to be performed against the OAuth [CallbackParams] recieved from the Auth server.
#[derive(Default, Serialize, Deserialize)]
pub struct OAuthCallbackChecks<'a> {
    /// 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<&'a str>,
    /// Expected state from the response
    pub state: Option<&'a str>,
    /// PKCE code verified to be sent to the token endpoint  
    pub code_verifier: Option<&'a str>,
    /// 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<'a> {
    /// 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<&'a str>,
    /// See [OAuthCallbackChecks]
    pub oauth_checks: Option<OAuthCallbackChecks<'a>>,
}