ws_auth/oauth2/auth/
mod.rs

1#[cfg(test)]
2pub mod tests;
3
4use std::str::FromStr;
5
6#[derive(Debug, Serialize, Deserialize, Clone)]
7pub enum AuthFlow {
8    Implicit,
9    SinglePageApplication,
10    Hybrid,
11}
12
13#[derive(Debug, Serialize, Deserialize, Clone)]
14pub enum ResponseMode {
15    #[serde(rename = "query")]
16    Query,
17
18    #[serde(rename = "fragment")]
19    Fragment,
20
21    #[serde(rename = "form_post")]
22    FormPost,
23}
24
25#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
26pub enum RequestPrompt {
27    #[serde(rename = "none")]
28    None,
29
30    #[serde(rename = "login")]
31    Login,
32
33    #[serde(rename = "consent")]
34    Consent,
35
36    #[serde(rename = "select_account")]
37    SelectAccount,
38}
39
40#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
41pub enum ResponseType {
42    #[serde(rename = "none")]
43    None,
44
45    #[serde(rename = "code")]
46    Code,
47
48    #[serde(rename = "token")]
49    Token,
50
51    #[serde(rename = "id_token")]
52    IdToken,
53}
54
55impl FromStr for ResponseType {
56    type Err = ();
57
58    fn from_str(input: &str) -> Result<ResponseType, Self::Err> {
59        match input {
60            "none" => Ok(ResponseType::None),
61            "code" => Ok(ResponseType::Code),
62            "token" => Ok(ResponseType::Token),
63            "id_token" => Ok(ResponseType::IdToken),
64            _ => Err(()),
65        }
66    }
67}
68
69#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
70pub enum AuthErrorCode {
71    /// Description
72    /// Protocol error, such as a missing required parameter.
73    ///
74    /// Action:
75    /// Fix and resubmit the request. This error is a development error typically caught during initial testing.
76    ///
77    #[serde(rename = "invalid_request")]
78    InvalidRequest,
79
80    /// Description:
81    /// The client application isn't permitted to request an authorization code.
82    ///
83    /// Action:
84    /// This error usually occurs when the client application isn't registered or isn't added to the user's tenant.
85    /// The application can prompt the user with instruction for installing the application and adding it.
86    #[serde(rename = "unauthorized_client")]
87    UnauthorizedClient,
88
89    /// Description:
90    /// Resource owner denied consent.
91    ///
92    /// Action:
93    /// The client application can notify the user that it can't continue unless the user consents.
94    #[serde(rename = "access_denied")]
95    AccessDenied,
96
97    /// Description:
98    /// The authorization server doesn't support the response type in the request.
99    ///
100    /// Action:
101    /// Fix and resubmit the request. This error is a development error typically caught during initial testing.
102    /// In the hybrid flow, this error signals that you must enable the ID token implicit grant setting on the client app registration.
103    #[serde(rename = "unsupported_response_type")]
104    UnsupportedResponseType,
105
106    /// Description:
107    /// The server encountered an unexpected error.
108    ///
109    /// Action:
110    /// Retry the request. These errors can result from temporary conditions.
111    /// The client application might explain to the user that its response is delayed to a temporary error.
112    #[serde(rename = "server_error")]
113    ServerError,
114
115    /// Description:
116    /// The server is temporarily too busy to handle the request.
117    ///
118    /// Action:
119    /// Retry the request. The client application might explain to the user that its response is delayed because of a temporary condition.
120    #[serde(rename = "temporarily_unavailable")]
121    TemporarilyUnavailable,
122
123    /// Description:
124    /// The target resource is invalid because it does not exist, or it's not correctly configured.
125    ///
126    /// Action:
127    /// This error indicates the resource, if it exists, hasn't been configured in the tenant.
128    /// The application can prompt the user with instruction for installing the application and adding it.
129    #[serde(rename = "invalid_resource")]
130    InvalidResource,
131
132    /// Description:
133    /// Too many or no users found.
134    ///
135    /// Action:
136    /// The client requested silent authentication (prompt=none), but a single user couldn't be found.
137    /// This error may mean there are multiple users active in the session, or no users.
138    /// This error takes into account the tenant chosen. For example, if there are two accounts active and one social account,
139    /// and social is chosen, silent authentication works.
140    #[serde(rename = "login_required")]
141    LoginRequired,
142
143    /// Description:
144    /// The request requires user interaction.
145    ///
146    /// Action:
147    /// Another authentication step or consent is required. Retry the request without prompt=none.
148    #[serde(rename = "interaction_required")]
149    InteractionRequired,
150}
151
152/// https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
153#[derive(Debug, Serialize, Deserialize, Clone)]
154pub struct AuthRequest {
155    /// Required - TODO: Extract this from the path!
156    #[serde(skip_serializing_if = "Option::is_none")]
157    #[serde(default)]
158    pub tenant: Option<String>,
159
160    /// Required
161    #[serde(skip_serializing_if = "Option::is_none")]
162    #[serde(default)]
163    pub client_id: Option<String>,
164
165    /// Required
166    #[serde(skip_serializing_if = "Option::is_none")]
167    #[serde(default)]
168    pub response_type: Option<String>,
169
170    /// Required
171    #[serde(skip_serializing_if = "Option::is_none")]
172    #[serde(default)]
173    pub redirect_uri: Option<String>,
174
175    /// Required
176    #[serde(skip_serializing_if = "Option::is_none")]
177    #[serde(default)]
178    pub scope: Option<String>,
179
180    /// Recommended (query, fragment, form_post)
181    /// query: is unsupported when requesting an id token by using an implicit flow
182    /// fragment: id token or ONLY code
183    /// form_post: when requesting code
184    #[serde(skip_serializing_if = "Option::is_none")]
185    #[serde(default)]
186    pub response_mode: Option<ResponseMode>,
187
188    /// Recommended
189    #[serde(skip_serializing_if = "Option::is_none")]
190    #[serde(default)]
191    pub state: Option<String>,
192
193    /// Optional
194    #[serde(skip_serializing_if = "Option::is_none")]
195    #[serde(default)]
196    pub prompt: Option<RequestPrompt>,
197
198    /// Optional
199    #[serde(skip_serializing_if = "Option::is_none")]
200    #[serde(default)]
201    pub login_hint: Option<String>,
202
203    /// Optional
204    #[serde(skip_serializing_if = "Option::is_none")]
205    #[serde(default)]
206    pub domain_hint: Option<String>,
207
208    /// Recommended, Required when code_challenge_method is included
209    #[serde(skip_serializing_if = "Option::is_none")]
210    #[serde(default)]
211    pub code_challenge: Option<String>,
212
213    /// Recommended, Required
214    #[serde(skip_serializing_if = "Option::is_none")]
215    #[serde(default)]
216    pub code_challenge_method: Option<String>,
217}
218
219impl AuthRequest {
220    pub fn get_response_types(&self) -> Vec<ResponseType> {
221        let mut response_types: Vec<ResponseType> = Vec::new();
222        let raw_response_types_str = self.response_type.to_owned().unwrap();
223        let response_types_str = raw_response_types_str.split(" ");
224        for response_type_str in response_types_str.into_iter() {
225            let response_type_result: Result<ResponseType, _> =
226                ResponseType::from_str(response_type_str);
227
228            if response_type_result.is_ok() {
229                response_types.push(response_type_result.unwrap());
230            }
231        }
232
233        return response_types;
234    }
235}
236
237#[derive(Debug, Serialize, Deserialize, Clone)]
238pub struct AuthResponse {
239    /// CODE that is to be exchanged for an Access Token
240    /// CODE + ID_TOKEN = Hybrid Flow
241    #[serde(skip_serializing_if = "Option::is_none")]
242    #[serde(default)]
243    pub code: Option<String>,
244
245    /// response_type = token
246    /// If TOKEN is included in the request, the access_token is returned immediately
247    /// without making a secondary request ...
248    #[serde(skip_serializing_if = "Option::is_none")]
249    #[serde(default)]
250    pub access_token: Option<String>,
251
252    /// response_type = token
253    /// Will always be `Bearer` if used
254    #[serde(skip_serializing_if = "Option::is_none")]
255    #[serde(default)]
256    pub token_type: Option<String>,
257
258    /// response_type = token
259    #[serde(skip_serializing_if = "Option::is_none")]
260    #[serde(default)]
261    pub expires_in: Option<u64>,
262
263    /// Only provided if `id_token` response_type was specified and `openid` scope was requested.
264    /// This should never be allowed / used for authorization purposes.
265    #[serde(skip_serializing_if = "Option::is_none")]
266    #[serde(default)]
267    pub id_token: Option<String>,
268
269    #[serde(skip_serializing_if = "Option::is_none")]
270    #[serde(default)]
271    pub state: Option<String>,
272
273    #[serde(skip_serializing_if = "Option::is_none")]
274    #[serde(default)]
275    pub scope: Option<String>,
276
277    #[serde(skip_serializing_if = "Option::is_none")]
278    #[serde(default)]
279    pub refresh_token: Option<String>,
280
281    #[serde(skip_serializing)]
282    pub callback_uri: Option<String>,
283}
284
285#[derive(Debug, Serialize, Deserialize, Clone)]
286pub struct AuthErrorResponse {
287    #[serde(skip_serializing_if = "Option::is_none")]
288    #[serde(default)]
289    pub error: Option<AuthErrorCode>,
290
291    #[serde(skip_serializing_if = "Option::is_none")]
292    #[serde(default)]
293    pub error_description: Option<String>,
294
295    #[serde(skip_serializing)]
296    pub callback_uri: Option<String>,
297}