Skip to main content

mas_oidc_client/
error.rs

1// Copyright 2022 Kévin Commaille.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The error types used in this crate.
16
17use std::{str::Utf8Error, sync::Arc};
18
19use headers::authorization::InvalidBearerToken;
20use http::{header::ToStrError, StatusCode};
21use mas_http::{catch_http_codes, form_urlencoded_request, json_request, json_response};
22use mas_jose::{
23    claims::ClaimError,
24    jwa::InvalidAlgorithm,
25    jwt::{JwtDecodeError, JwtSignatureError, NoKeyWorked},
26};
27use oauth2_types::{
28    errors::ClientErrorCode, oidc::ProviderMetadataVerificationError, pkce::CodeChallengeError,
29};
30use serde::{Deserialize, Serialize};
31use thiserror::Error;
32pub use tower::BoxError;
33
34/// All possible errors when using this crate.
35#[derive(Debug, Error)]
36#[error(transparent)]
37pub enum Error {
38    /// An error occurred fetching provider metadata.
39    Discovery(#[from] DiscoveryError),
40
41    /// An error occurred fetching the provider JWKS.
42    Jwks(#[from] JwksError),
43
44    /// An error occurred during client registration.
45    Registration(#[from] RegistrationError),
46
47    /// An error occurred building the authorization URL.
48    Authorization(#[from] AuthorizationError),
49
50    /// An error occurred exchanging an authorization code for an access token.
51    TokenAuthorizationCode(#[from] TokenAuthorizationCodeError),
52
53    /// An error occurred requesting an access token with client credentials.
54    TokenClientCredentials(#[from] TokenRequestError),
55
56    /// An error occurred refreshing an access token.
57    TokenRefresh(#[from] TokenRefreshError),
58
59    /// An error occurred revoking a token.
60    TokenRevoke(#[from] TokenRevokeError),
61
62    /// An error occurred requesting user info.
63    UserInfo(#[from] UserInfoError),
64
65    /// An error occurred introspecting a token.
66    Introspection(#[from] IntrospectionError),
67
68    /// An error occurred building the account management URL.
69    AccountManagement(#[from] AccountManagementError),
70}
71
72/// All possible errors when fetching provider metadata.
73#[derive(Debug, Error)]
74pub enum DiscoveryError {
75    /// An error occurred building the request's URL.
76    #[error(transparent)]
77    IntoUrl(#[from] url::ParseError),
78
79    /// An error occurred building the request.
80    #[error(transparent)]
81    IntoHttp(#[from] http::Error),
82
83    /// The server returned an HTTP error status code.
84    #[error(transparent)]
85    Http(#[from] HttpError),
86
87    /// An error occurred deserializing the response.
88    #[error(transparent)]
89    FromJson(#[from] serde_json::Error),
90
91    /// An error occurred validating the metadata.
92    #[error(transparent)]
93    Validation(#[from] ProviderMetadataVerificationError),
94
95    /// An error occurred sending the request.
96    #[error(transparent)]
97    Service(BoxError),
98
99    /// Discovery is disabled for this provider.
100    #[error("Discovery is disabled for this provider")]
101    Disabled,
102}
103
104impl<S> From<json_response::Error<S>> for DiscoveryError
105where
106    S: Into<DiscoveryError>,
107{
108    fn from(err: json_response::Error<S>) -> Self {
109        match err {
110            json_response::Error::Deserialize { inner } => inner.into(),
111            json_response::Error::Service { inner } => inner.into(),
112        }
113    }
114}
115
116impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for DiscoveryError
117where
118    S: Into<BoxError>,
119{
120    fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
121        match err {
122            catch_http_codes::Error::HttpError { status_code, inner } => {
123                Self::Http(HttpError::new(status_code, inner))
124            }
125            catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
126        }
127    }
128}
129
130/// All possible errors when registering the client.
131#[derive(Debug, Error)]
132pub enum RegistrationError {
133    /// An error occurred building the request.
134    #[error(transparent)]
135    IntoHttp(#[from] http::Error),
136
137    /// An error occurred serializing the request or deserializing the response.
138    #[error(transparent)]
139    Json(#[from] serde_json::Error),
140
141    /// The server returned an HTTP error status code.
142    #[error(transparent)]
143    Http(#[from] HttpError),
144
145    /// No client secret was received although one was expected because of the
146    /// authentication method.
147    #[error("missing client secret in response")]
148    MissingClientSecret,
149
150    /// An error occurred sending the request.
151    #[error(transparent)]
152    Service(BoxError),
153}
154
155impl<S> From<json_request::Error<S>> for RegistrationError
156where
157    S: Into<RegistrationError>,
158{
159    fn from(err: json_request::Error<S>) -> Self {
160        match err {
161            json_request::Error::Serialize { inner } => inner.into(),
162            json_request::Error::Service { inner } => inner.into(),
163        }
164    }
165}
166
167impl<S> From<json_response::Error<S>> for RegistrationError
168where
169    S: Into<RegistrationError>,
170{
171    fn from(err: json_response::Error<S>) -> Self {
172        match err {
173            json_response::Error::Deserialize { inner } => inner.into(),
174            json_response::Error::Service { inner } => inner.into(),
175        }
176    }
177}
178
179impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for RegistrationError
180where
181    S: Into<BoxError>,
182{
183    fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
184        match err {
185            catch_http_codes::Error::HttpError { status_code, inner } => {
186                HttpError::new(status_code, inner).into()
187            }
188            catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
189        }
190    }
191}
192
193/// All possible errors when making a pushed authorization request.
194#[derive(Debug, Error)]
195pub enum PushedAuthorizationError {
196    /// An error occurred serializing the request.
197    #[error(transparent)]
198    UrlEncoded(#[from] serde_urlencoded::ser::Error),
199
200    /// An error occurred building the request.
201    #[error(transparent)]
202    IntoHttp(#[from] http::Error),
203
204    /// An error occurred adding the client credentials to the request.
205    #[error(transparent)]
206    Credentials(#[from] CredentialsError),
207
208    /// The server returned an HTTP error status code.
209    #[error(transparent)]
210    Http(#[from] HttpError),
211
212    /// An error occurred deserializing the response.
213    #[error(transparent)]
214    Json(#[from] serde_json::Error),
215
216    /// An error occurred sending the request.
217    #[error(transparent)]
218    Service(BoxError),
219}
220
221impl<S> From<form_urlencoded_request::Error<S>> for PushedAuthorizationError
222where
223    S: Into<PushedAuthorizationError>,
224{
225    fn from(err: form_urlencoded_request::Error<S>) -> Self {
226        match err {
227            form_urlencoded_request::Error::Serialize { inner } => inner.into(),
228            form_urlencoded_request::Error::Service { inner } => inner.into(),
229        }
230    }
231}
232
233impl<S> From<json_response::Error<S>> for PushedAuthorizationError
234where
235    S: Into<PushedAuthorizationError>,
236{
237    fn from(err: json_response::Error<S>) -> Self {
238        match err {
239            json_response::Error::Deserialize { inner } => inner.into(),
240            json_response::Error::Service { inner } => inner.into(),
241        }
242    }
243}
244
245impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for PushedAuthorizationError
246where
247    S: Into<BoxError>,
248{
249    fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
250        match err {
251            catch_http_codes::Error::HttpError { status_code, inner } => {
252                HttpError::new(status_code, inner).into()
253            }
254            catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
255        }
256    }
257}
258
259/// All possible errors when authorizing the client.
260#[derive(Debug, Error)]
261pub enum AuthorizationError {
262    /// An error occurred constructing the PKCE code challenge.
263    #[error(transparent)]
264    Pkce(#[from] CodeChallengeError),
265
266    /// An error occurred serializing the request.
267    #[error(transparent)]
268    UrlEncoded(#[from] serde_urlencoded::ser::Error),
269
270    /// An error occurred making the PAR request.
271    #[error(transparent)]
272    PushedAuthorization(#[from] PushedAuthorizationError),
273}
274
275/// All possible errors when requesting an access token.
276#[derive(Debug, Error)]
277pub enum TokenRequestError {
278    /// An error occurred building the request.
279    #[error(transparent)]
280    IntoHttp(#[from] http::Error),
281
282    /// An error occurred adding the client credentials to the request.
283    #[error(transparent)]
284    Credentials(#[from] CredentialsError),
285
286    /// An error occurred serializing the request.
287    #[error(transparent)]
288    UrlEncoded(#[from] serde_urlencoded::ser::Error),
289
290    /// The server returned an HTTP error status code.
291    #[error(transparent)]
292    Http(#[from] HttpError),
293
294    /// An error occurred deserializing the response.
295    #[error(transparent)]
296    Json(#[from] serde_json::Error),
297
298    /// An error occurred sending the request.
299    #[error(transparent)]
300    Service(BoxError),
301}
302
303impl<S> From<form_urlencoded_request::Error<S>> for TokenRequestError
304where
305    S: Into<TokenRequestError>,
306{
307    fn from(err: form_urlencoded_request::Error<S>) -> Self {
308        match err {
309            form_urlencoded_request::Error::Serialize { inner } => inner.into(),
310            form_urlencoded_request::Error::Service { inner } => inner.into(),
311        }
312    }
313}
314
315impl<S> From<json_response::Error<S>> for TokenRequestError
316where
317    S: Into<TokenRequestError>,
318{
319    fn from(err: json_response::Error<S>) -> Self {
320        match err {
321            json_response::Error::Deserialize { inner } => inner.into(),
322            json_response::Error::Service { inner } => inner.into(),
323        }
324    }
325}
326
327impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for TokenRequestError
328where
329    S: Into<BoxError>,
330{
331    fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
332        match err {
333            catch_http_codes::Error::HttpError { status_code, inner } => {
334                HttpError::new(status_code, inner).into()
335            }
336            catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
337        }
338    }
339}
340
341/// All possible errors when exchanging a code for an access token.
342#[derive(Debug, Error)]
343pub enum TokenAuthorizationCodeError {
344    /// An error occurred requesting the access token.
345    #[error(transparent)]
346    Token(#[from] TokenRequestError),
347
348    /// An error occurred validating the ID Token.
349    #[error(transparent)]
350    IdToken(#[from] IdTokenError),
351}
352
353/// All possible errors when refreshing an access token.
354#[derive(Debug, Error)]
355pub enum TokenRefreshError {
356    /// An error occurred requesting the access token.
357    #[error(transparent)]
358    Token(#[from] TokenRequestError),
359
360    /// An error occurred validating the ID Token.
361    #[error(transparent)]
362    IdToken(#[from] IdTokenError),
363}
364
365/// All possible errors when revoking a token.
366#[derive(Debug, Error)]
367pub enum TokenRevokeError {
368    /// An error occurred building the request.
369    #[error(transparent)]
370    IntoHttp(#[from] http::Error),
371
372    /// An error occurred adding the client credentials to the request.
373    #[error(transparent)]
374    Credentials(#[from] CredentialsError),
375
376    /// An error occurred serializing the request.
377    #[error(transparent)]
378    UrlEncoded(#[from] serde_urlencoded::ser::Error),
379
380    /// An error occurred deserializing the error response.
381    #[error(transparent)]
382    Json(#[from] serde_json::Error),
383
384    /// The server returned an HTTP error status code.
385    #[error(transparent)]
386    Http(#[from] HttpError),
387
388    /// An error occurred sending the request.
389    #[error(transparent)]
390    Service(BoxError),
391}
392
393impl<S> From<form_urlencoded_request::Error<S>> for TokenRevokeError
394where
395    S: Into<TokenRevokeError>,
396{
397    fn from(err: form_urlencoded_request::Error<S>) -> Self {
398        match err {
399            form_urlencoded_request::Error::Serialize { inner } => inner.into(),
400            form_urlencoded_request::Error::Service { inner } => inner.into(),
401        }
402    }
403}
404
405impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for TokenRevokeError
406where
407    S: Into<BoxError>,
408{
409    fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
410        match err {
411            catch_http_codes::Error::HttpError { status_code, inner } => {
412                HttpError::new(status_code, inner).into()
413            }
414            catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
415        }
416    }
417}
418
419/// All possible errors when requesting user info.
420#[derive(Debug, Error)]
421pub enum UserInfoError {
422    /// An error occurred getting the provider metadata.
423    #[error(transparent)]
424    Discovery(#[from] Arc<DiscoveryError>),
425
426    /// The provider doesn't support requesting user info.
427    #[error("missing UserInfo support")]
428    MissingUserInfoSupport,
429
430    /// No token is available to get info from.
431    #[error("missing token")]
432    MissingToken,
433
434    /// No client metadata is available.
435    #[error("missing client metadata")]
436    MissingClientMetadata,
437
438    /// The access token is invalid.
439    #[error(transparent)]
440    Token(#[from] InvalidBearerToken),
441
442    /// An error occurred building the request.
443    #[error(transparent)]
444    IntoHttp(#[from] http::Error),
445
446    /// The content-type header is missing from the response.
447    #[error("missing response content-type")]
448    MissingResponseContentType,
449
450    /// The content-type header could not be decoded.
451    #[error("could not decoded response content-type: {0}")]
452    DecodeResponseContentType(#[from] ToStrError),
453
454    /// The content-type is not valid.
455    #[error("invalid response content-type")]
456    InvalidResponseContentTypeValue,
457
458    /// The content-type is not the one that was expected.
459    #[error("unexpected response content-type {got:?}, expected {expected:?}")]
460    UnexpectedResponseContentType {
461        /// The expected content-type.
462        expected: String,
463        /// The returned content-type.
464        got: String,
465    },
466
467    /// An error occurred reading the response.
468    #[error(transparent)]
469    FromUtf8(#[from] Utf8Error),
470
471    /// An error occurred deserializing the JSON or error response.
472    #[error(transparent)]
473    Json(#[from] serde_json::Error),
474
475    /// An error occurred verifying the Id Token.
476    #[error(transparent)]
477    IdToken(#[from] IdTokenError),
478
479    /// The server returned an HTTP error status code.
480    #[error(transparent)]
481    Http(#[from] HttpError),
482
483    /// An error occurred sending the request.
484    #[error(transparent)]
485    Service(BoxError),
486}
487
488impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for UserInfoError
489where
490    S: Into<BoxError>,
491{
492    fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
493        match err {
494            catch_http_codes::Error::HttpError { status_code, inner } => {
495                HttpError::new(status_code, inner).into()
496            }
497            catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
498        }
499    }
500}
501
502/// All possible errors when introspecting a token.
503#[derive(Debug, Error)]
504pub enum IntrospectionError {
505    /// An error occurred building the request.
506    #[error(transparent)]
507    IntoHttp(#[from] http::Error),
508
509    /// An error occurred adding the client credentials to the request.
510    #[error(transparent)]
511    Credentials(#[from] CredentialsError),
512
513    /// The access token is invalid.
514    #[error(transparent)]
515    Token(#[from] InvalidBearerToken),
516
517    /// An error occurred serializing the request.
518    #[error(transparent)]
519    UrlEncoded(#[from] serde_urlencoded::ser::Error),
520
521    /// An error occurred deserializing the JSON or error response.
522    #[error(transparent)]
523    Json(#[from] serde_json::Error),
524
525    /// The server returned an HTTP error status code.
526    #[error(transparent)]
527    Http(#[from] HttpError),
528
529    /// An error occurred sending the request.
530    #[error(transparent)]
531    Service(BoxError),
532}
533
534impl<S> From<form_urlencoded_request::Error<S>> for IntrospectionError
535where
536    S: Into<IntrospectionError>,
537{
538    fn from(err: form_urlencoded_request::Error<S>) -> Self {
539        match err {
540            form_urlencoded_request::Error::Serialize { inner } => inner.into(),
541            form_urlencoded_request::Error::Service { inner } => inner.into(),
542        }
543    }
544}
545
546impl<S> From<json_response::Error<S>> for IntrospectionError
547where
548    S: Into<IntrospectionError>,
549{
550    fn from(err: json_response::Error<S>) -> Self {
551        match err {
552            json_response::Error::Deserialize { inner } => inner.into(),
553            json_response::Error::Service { inner } => inner.into(),
554        }
555    }
556}
557
558impl<S> From<catch_http_codes::Error<S, Option<ErrorBody>>> for IntrospectionError
559where
560    S: Into<BoxError>,
561{
562    fn from(err: catch_http_codes::Error<S, Option<ErrorBody>>) -> Self {
563        match err {
564            catch_http_codes::Error::HttpError { status_code, inner } => {
565                HttpError::new(status_code, inner).into()
566            }
567            catch_http_codes::Error::Service { inner } => Self::Service(inner.into()),
568        }
569    }
570}
571
572/// All possible errors when requesting a JWKS.
573#[derive(Debug, Error)]
574pub enum JwksError {
575    /// An error occurred building the request.
576    #[error(transparent)]
577    IntoHttp(#[from] http::Error),
578
579    /// An error occurred deserializing the response.
580    #[error(transparent)]
581    Json(#[from] serde_json::Error),
582
583    /// An error occurred sending the request.
584    #[error(transparent)]
585    Service(BoxError),
586}
587
588impl<S> From<json_response::Error<S>> for JwksError
589where
590    S: Into<BoxError>,
591{
592    fn from(err: json_response::Error<S>) -> Self {
593        match err {
594            json_response::Error::Service { inner } => Self::Service(inner.into()),
595            json_response::Error::Deserialize { inner } => Self::Json(inner),
596        }
597    }
598}
599
600/// All possible errors when verifying a JWT.
601#[derive(Debug, Error)]
602pub enum JwtVerificationError {
603    /// An error occured decoding the JWT.
604    #[error(transparent)]
605    JwtDecode(#[from] JwtDecodeError),
606
607    /// No key worked for verifying the JWT's signature.
608    #[error(transparent)]
609    JwtSignature(#[from] NoKeyWorked),
610
611    /// An error occurred extracting a claim.
612    #[error(transparent)]
613    Claim(#[from] ClaimError),
614
615    /// The algorithm used for signing the JWT is not the one that was
616    /// requested.
617    #[error("wrong signature alg")]
618    WrongSignatureAlg,
619}
620
621/// All possible errors when verifying an ID token.
622#[derive(Debug, Error)]
623pub enum IdTokenError {
624    /// No ID Token was found in the response although one was expected.
625    #[error("ID token is missing")]
626    MissingIdToken,
627
628    /// The ID Token from the latest Authorization was not provided although
629    /// this request expects to be verified against one.
630    #[error("Authorization ID token is missing")]
631    MissingAuthIdToken,
632
633    /// An error occurred validating the ID Token's signature and basic claims.
634    #[error(transparent)]
635    Jwt(#[from] JwtVerificationError),
636
637    /// An error occurred extracting a claim.
638    #[error(transparent)]
639    Claim(#[from] ClaimError),
640
641    /// The subject identifier returned by the issuer is not the same as the one
642    /// we got before.
643    #[error("wrong subject identifier")]
644    WrongSubjectIdentifier,
645
646    /// The authentication time returned by the issuer is not the same as the
647    /// one we got before.
648    #[error("wrong authentication time")]
649    WrongAuthTime,
650}
651
652/// An error that can be returned by an OpenID Provider.
653#[derive(Debug, Clone, Error)]
654#[error("{status}: {body:?}")]
655pub struct HttpError {
656    /// The status code of the error.
657    pub status: StatusCode,
658
659    /// The body of the error, if any.
660    pub body: Option<ErrorBody>,
661}
662
663impl HttpError {
664    /// Creates a new `HttpError` with the given status code and optional body.
665    #[must_use]
666    pub fn new(status: StatusCode, body: Option<ErrorBody>) -> Self {
667        Self { status, body }
668    }
669}
670
671/// The body of an error that can be returned by an OpenID Provider.
672#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct ErrorBody {
674    /// The error code.
675    pub error: ClientErrorCode,
676
677    /// Additional text description of the error for debugging.
678    pub error_description: Option<String>,
679}
680
681/// All errors that can occur when adding client credentials to the request.
682#[derive(Debug, Error)]
683pub enum CredentialsError {
684    /// Trying to use an unsupported authentication method.
685    #[error("unsupported authentication method")]
686    UnsupportedMethod,
687
688    /// When authenticationg with `private_key_jwt`, no private key was found
689    /// for the given algorithm.
690    #[error("no private key was found for the given algorithm")]
691    NoPrivateKeyFound,
692
693    /// The signing algorithm is invalid for this authentication method.
694    #[error("invalid algorithm: {0}")]
695    InvalidSigningAlgorithm(#[from] InvalidAlgorithm),
696
697    /// An error occurred when building the claims of the JWT.
698    #[error(transparent)]
699    JwtClaims(#[from] ClaimError),
700
701    /// The key found cannot be used with the algorithm.
702    #[error("Wrong algorithm for key")]
703    JwtWrongAlgorithm,
704
705    /// An error occurred when signing the JWT.
706    #[error(transparent)]
707    JwtSignature(#[from] JwtSignatureError),
708
709    /// An error occurred with a custom signing method.
710    #[error(transparent)]
711    Custom(BoxError),
712}
713
714/// All errors that can occur when building the account management URL.
715#[derive(Debug, Error)]
716pub enum AccountManagementError {
717    /// An error occurred serializing the parameters.
718    #[error(transparent)]
719    UrlEncoded(#[from] serde_urlencoded::ser::Error),
720}