oxide_auth/code_grant/
error.rs

1//! Errors defined in [rfc6749].
2//!
3//! [rfc6749]: https://tools.ietf.org/html/rfc6749#section-6
4
5use std::fmt;
6use std::borrow::Cow;
7use std::vec;
8use url::Url;
9
10/// Error codes returned from an authorization code request.
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum AuthorizationErrorType {
13    /// The request is missing a required parameter, includes an invalid parameter value, includes
14    /// a parameter more than once, or is otherwise malformed.
15    InvalidRequest,
16
17    /// The client is not authorized to request an authorization code using this method.
18    UnauthorizedClient,
19
20    /// The resource owner or authorization server denied the request.
21    AccessDenied,
22
23    /// The authorization server does not support obtaining an authorization code using this method.
24    UnsupportedResponseType,
25
26    /// The requested scope is invalid, unknown, or malformed.
27    InvalidScope,
28
29    /// The authorization server encountered an unexpected condition that prevented it from
30    /// fulfilling the request. (This error code is needed because a 500 Internal Server Error HTTP
31    /// status code cannot be returned to the client via an HTTP redirect.)
32    ServerError,
33
34    /// The authorization server is currently unable to handle the request due to a temporary
35    /// overloading or maintenance of the server.  (This error code is needed because a 503 Service
36    /// Unavailable HTTP status code cannot be returned to the client via an HTTP redirect.)
37    TemporarilyUnavailable,
38}
39
40impl AuthorizationErrorType {
41    fn description(self) -> &'static str {
42        match self {
43            AuthorizationErrorType::InvalidRequest => "invalid_request",
44            AuthorizationErrorType::UnauthorizedClient => "unauthorized_client",
45            AuthorizationErrorType::AccessDenied => "access_denied",
46            AuthorizationErrorType::UnsupportedResponseType => "unsupported_response_type",
47            AuthorizationErrorType::InvalidScope => "invalid_scope",
48            AuthorizationErrorType::ServerError => "server_error",
49            AuthorizationErrorType::TemporarilyUnavailable => "temporarily_unavailable",
50        }
51    }
52}
53
54/// Represents parameters of an error in an [Authorization Error Response][Authorization Error].
55///
56/// [Authorization Error]: https://tools.ietf.org/html/rfc6749#section-4.2.2.1
57#[derive(Clone, Debug)]
58pub struct AuthorizationError {
59    error: AuthorizationErrorType,
60    description: Option<Cow<'static, str>>,
61    uri: Option<Cow<'static, str>>,
62}
63
64impl AuthorizationError {
65    #[allow(dead_code)]
66    pub(crate) fn new(error: AuthorizationErrorType) -> Self {
67        AuthorizationError {
68            error,
69            description: None,
70            uri: None,
71        }
72    }
73
74    /// Set the error type
75    pub fn set_type(&mut self, new_type: AuthorizationErrorType) {
76        self.error = new_type;
77    }
78
79    /// Get the formal kind of error.
80    ///
81    /// This can not currently be changed as to uphold the inner invariants for RFC compliance.
82    pub fn kind(&mut self) -> AuthorizationErrorType {
83        self.error
84    }
85
86    /// Provide a short text explanation for the error.
87    pub fn explain<D: Into<Cow<'static, str>>>(&mut self, description: D) {
88        self.description = Some(description.into())
89    }
90
91    /// A uri identifying a resource explaining the error in detail.
92    pub fn explain_uri(&mut self, uri: Url) {
93        self.uri = Some(String::from(uri).into())
94    }
95
96    /// Iterate over the key value pairs that describe this error.
97    ///
98    /// These pairs must be added to the detailed description of an error. To this end the pairs
99    /// appear as part of a form urlencoded query component in the `Location` header of a server
100    /// response.
101    pub fn iter(&self) -> <Self as IntoIterator>::IntoIter {
102        self.into_iter()
103    }
104}
105
106/// All defined error codes
107///
108/// Details also found in <https://tools.ietf.org/html/rfc6749#section-5.2>.
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
110pub enum AccessTokenErrorType {
111    /// The request is missing a required parameter, includes an unsupported parameter value (other
112    // than grant type), repeats a parameter, includes multiple credentials, utilizes more than one
113    /// mechanism for authenticating the client, or is otherwise malformed.
114    InvalidRequest,
115
116    /// Client authentication failed (e.g., unknown client, no client authentication included, or
117    /// unsupported authentication method).  The authorization server MAY return an HTTP 401
118    /// (Unauthorized) status code to indicate which HTTP authentication schemes are supported.
119    /// If the client attempted to authenticate via the "Authorization" request header field, the
120    /// authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include
121    /// the "WWW-Authenticate" response header field matching the authentication scheme used by the
122    /// client.
123    InvalidClient,
124
125    /// The provided authorization grant (e.g., authorization code, resource owner credentials) or
126    /// refresh token is invalid, expired, revoked, does not match the redirection URI used in the
127    /// authorization request, or was issued to another client.
128    InvalidGrant,
129
130    /// The authenticated client is not authorized to use this authorization grant type.
131    UnauthorizedClient,
132
133    /// The authorization grant type is not supported by the authorization server.
134    UnsupportedGrantType,
135
136    /// The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the
137    /// resource owner.
138    InvalidScope,
139}
140
141impl AccessTokenErrorType {
142    fn description(self) -> &'static str {
143        match self {
144            AccessTokenErrorType::InvalidRequest => "invalid_request",
145            AccessTokenErrorType::InvalidClient => "invalid_client",
146            AccessTokenErrorType::InvalidGrant => "invalid_grant",
147            AccessTokenErrorType::UnauthorizedClient => "unauthorized_client",
148            AccessTokenErrorType::UnsupportedGrantType => "unsupported_grant_type",
149            AccessTokenErrorType::InvalidScope => "invalid_scope",
150        }
151    }
152}
153
154/// Represents parameters of an error in an [Issuing Error Response][Issuing Error].
155///
156/// This is used for both access token requests, and [token refresh requests] as they use the same
157/// internal error representations in the RFC as well.
158///
159/// [Issuing Error]: https://tools.ietf.org/html/rfc6749#section-5.2
160/// [token refresh requests]: https://tools.ietf.org/html/rfc6749#section-7
161#[derive(Clone, Debug)]
162pub struct AccessTokenError {
163    error: AccessTokenErrorType,
164    description: Option<Cow<'static, str>>,
165    uri: Option<Cow<'static, str>>,
166}
167
168impl AccessTokenError {
169    pub(crate) fn new(error: AccessTokenErrorType) -> Self {
170        AccessTokenError {
171            error,
172            description: None,
173            uri: None,
174        }
175    }
176
177    /// Set error type
178    pub fn set_type(&mut self, new_type: AccessTokenErrorType) {
179        self.error = new_type;
180    }
181
182    /// Get the formal kind of error.
183    ///
184    /// This can not currently be changed as to uphold the inner invariants for RFC compliance.
185    pub fn kind(&mut self) -> AccessTokenErrorType {
186        self.error
187    }
188
189    /// Provide a short text explanation for the error.
190    pub fn explain<D: Into<Cow<'static, str>>>(&mut self, description: D) {
191        self.description = Some(description.into())
192    }
193
194    /// A uri identifying a resource explaining the error in detail.
195    pub fn explain_uri(&mut self, uri: Url) {
196        self.uri = Some(String::from(uri).into())
197    }
198
199    /// Iterate over the key value pairs that describe this error.
200    ///
201    /// These pairs must be added to the detailed description of an error. The pairs will be
202    /// encoded in the json body of the Bad Request response.
203    pub fn iter(&self) -> <Self as IntoIterator>::IntoIter {
204        self.into_iter()
205    }
206}
207
208impl Default for AuthorizationError {
209    /// Construct a `AuthorizationError` with no extra information.
210    ///
211    /// Will produce a generic `InvalidRequest` error without any description or error uri which
212    /// would provide additional information for the client.
213    fn default() -> Self {
214        AuthorizationError {
215            error: AuthorizationErrorType::InvalidRequest,
216            description: None,
217            uri: None,
218        }
219    }
220}
221
222impl AsRef<str> for AuthorizationErrorType {
223    fn as_ref(&self) -> &str {
224        self.description()
225    }
226}
227
228impl fmt::Display for AuthorizationErrorType {
229    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230        write!(f, "{}", self.as_ref())
231    }
232}
233
234impl Default for AccessTokenError {
235    /// Construct a `AccessTokenError` with no extra information.
236    ///
237    /// Will produce a generic `InvalidRequest` error without any description or error uri which
238    /// would provide additional information for the client.
239    fn default() -> Self {
240        AccessTokenError {
241            error: AccessTokenErrorType::InvalidRequest,
242            description: None,
243            uri: None,
244        }
245    }
246}
247
248impl AsRef<str> for AccessTokenErrorType {
249    fn as_ref(&self) -> &str {
250        self.description()
251    }
252}
253
254impl fmt::Display for AccessTokenErrorType {
255    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256        write!(f, "{}", self.as_ref())
257    }
258}
259
260/// The error as key-value pairs.
261impl IntoIterator for AuthorizationError {
262    type Item = (&'static str, Cow<'static, str>);
263    type IntoIter = vec::IntoIter<(&'static str, Cow<'static, str>)>;
264
265    fn into_iter(self) -> Self::IntoIter {
266        let mut vec = vec![("error", Cow::Borrowed(self.error.description()))];
267        if let Some(description) = self.description {
268            vec.push(("description", description));
269        }
270        if let Some(uri) = self.uri {
271            vec.push(("uri", uri));
272        }
273        vec.into_iter()
274    }
275}
276
277impl IntoIterator for &'_ AuthorizationError {
278    type Item = (&'static str, Cow<'static, str>);
279    type IntoIter = vec::IntoIter<(&'static str, Cow<'static, str>)>;
280
281    fn into_iter(self) -> Self::IntoIter {
282        let mut vec = vec![("error", Cow::Borrowed(self.error.description()))];
283        if let Some(description) = &self.description {
284            vec.push(("description", description.clone().to_owned()));
285        }
286        if let Some(uri) = &self.uri {
287            vec.push(("uri", uri.clone().to_owned()));
288        }
289        vec.into_iter()
290    }
291}
292
293/// The error as key-value pairs.
294impl IntoIterator for AccessTokenError {
295    type Item = (&'static str, Cow<'static, str>);
296    type IntoIter = vec::IntoIter<(&'static str, Cow<'static, str>)>;
297
298    fn into_iter(self) -> Self::IntoIter {
299        let mut vec = vec![("error", Cow::Borrowed(self.error.description()))];
300        if let Some(description) = self.description {
301            vec.push(("description", description));
302        }
303        if let Some(uri) = self.uri {
304            vec.push(("uri", uri));
305        }
306        vec.into_iter()
307    }
308}
309
310impl IntoIterator for &'_ AccessTokenError {
311    type Item = (&'static str, Cow<'static, str>);
312    type IntoIter = vec::IntoIter<(&'static str, Cow<'static, str>)>;
313
314    fn into_iter(self) -> Self::IntoIter {
315        let mut vec = vec![("error", Cow::Borrowed(self.error.description()))];
316        if let Some(description) = &self.description {
317            vec.push(("description", description.clone().to_owned()));
318        }
319        if let Some(uri) = &self.uri {
320            vec.push(("uri", uri.clone().to_owned()));
321        }
322        vec.into_iter()
323    }
324}