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
211
212
213
214
215
216
217
218
#[cfg(test)]
pub mod tests;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum AuthFlow {
    Implicit,
    SinglePageApplication,
    Hybrid,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum ResponseMode {
    #[serde(rename = "query")]
    Query,

    #[serde(rename = "fragment")]
    Fragment,

    #[serde(rename = "form_post")]
    FormPost,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum RequestPrompt {
    #[serde(rename = "none")]
    None,

    #[serde(rename = "login")]
    Login,

    #[serde(rename = "consent")]
    Consent,

    #[serde(rename = "select_account")]
    SelectAccount,
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub enum AuthErrorCode {
    /// Description
    /// Protocol error, such as a missing required parameter.
    ///
    /// Action:
    /// Fix and resubmit the request. This error is a development error typically caught during initial testing.
    ///
    #[serde(rename = "invalid_request")]
    InvalidRequest,

    /// Description:
    /// The client application isn't permitted to request an authorization code.
    ///
    /// Action:
    /// This error usually occurs when the client application isn't registered or isn't added to the user's tenant.
    /// The application can prompt the user with instruction for installing the application and adding it.
    #[serde(rename = "unauthorized_client")]
    UnauthorizedClient,

    /// Description:
    /// Resource owner denied consent.
    ///
    /// Action:
    /// The client application can notify the user that it can't continue unless the user consents.
    #[serde(rename = "access_denied")]
    AccessDenied,

    /// Description:
    /// The authorization server doesn't support the response type in the request.
    ///
    /// Action:
    /// Fix and resubmit the request. This error is a development error typically caught during initial testing.
    /// In the hybrid flow, this error signals that you must enable the ID token implicit grant setting on the client app registration.
    #[serde(rename = "unsupported_response_type")]
    UnsupportedResponseType,

    /// Description:
    /// The server encountered an unexpected error.
    ///
    /// Action:
    /// Retry the request. These errors can result from temporary conditions.
    /// The client application might explain to the user that its response is delayed to a temporary error.
    #[serde(rename = "server_error")]
    ServerError,

    /// Description:
    /// The server is temporarily too busy to handle the request.
    ///
    /// Action:
    /// Retry the request. The client application might explain to the user that its response is delayed because of a temporary condition.
    #[serde(rename = "temporarily_unavailable")]
    TemporarilyUnavailable,

    /// Description:
    /// The target resource is invalid because it does not exist, or it's not correctly configured.
    ///
    /// Action:
    /// This error indicates the resource, if it exists, hasn't been configured in the tenant.
    /// The application can prompt the user with instruction for installing the application and adding it.
    #[serde(rename = "invalid_resource")]
    InvalidResource,

    /// Description:
    /// Too many or no users found.
    ///
    /// Action:
    /// The client requested silent authentication (prompt=none), but a single user couldn't be found.
    /// This error may mean there are multiple users active in the session, or no users.
    /// This error takes into account the tenant chosen. For example, if there are two accounts active and one social account,
    /// and social is chosen, silent authentication works.
    #[serde(rename = "login_required")]
    LoginRequired,

    /// Description:
    /// The request requires user interaction.
    ///
    /// Action:
    /// Another authentication step or consent is required. Retry the request without prompt=none.
    #[serde(rename = "interaction_required")]
    InteractionRequired,
}

/// https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AuthRequest {
    /// Required - TODO: Extract this from the path!
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub tenant: Option<String>,

    /// Required
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub client_id: Option<String>,

    /// Required
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub response_type: Option<String>,

    /// Required
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub redirect_uri: Option<String>,

    /// Required
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub scope: Option<String>,

    /// Recommended (query, fragment, form_post)
    /// query: is unsupported when requesting an id token by using an implicit flow
    /// fragment: id token or ONLY code
    /// form_post: when requesting code
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub response_mode: Option<ResponseMode>,

    /// Recommended
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub state: Option<String>,

    /// Optional
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub prompt: Option<RequestPrompt>,

    /// Optional
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub login_hint: Option<String>,

    /// Optional
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub domain_hint: Option<String>,

    /// Recommended, Required when code_challenge_method is included
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub code_challenge: Option<String>,

    /// Recommended, Required
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub code_challenge_method: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AuthResponse {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub code: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub id_token: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub state: Option<String>,

    #[serde(skip_serializing)]
    pub callback_uri: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AuthErrorResponse {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub error: Option<AuthErrorCode>,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub error_description: Option<String>,

    #[serde(skip_serializing)]
    pub callback_uri: Option<String>,
}