From ee47c821e3f844a4ccd817866e2e2d5b1728b152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Wed, 24 Aug 2022 11:24:57 +0200 Subject: [PATCH] Use an enum for client error codes Replace the ClientError constants with From. --- Cargo.lock | 73 +++- .../handlers/src/oauth2/authorization/mod.rs | 58 ++- crates/handlers/src/oauth2/registration.rs | 52 +-- crates/handlers/src/oauth2/token.rs | 38 +- crates/oauth2-types/Cargo.toml | 1 + crates/oauth2-types/src/errors.rs | 380 ++++++++++++------ 6 files changed, 421 insertions(+), 181 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe3df88c..4d1f06bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1176,14 +1176,37 @@ dependencies = [ "cipher 0.3.0", ] +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + [[package]] name = "darling" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.1", + "darling_macro 0.14.1", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1200,13 +1223,24 @@ dependencies = [ "syn", ] +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn", +] + [[package]] name = "darling_macro" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" dependencies = [ - "darling_core", + "darling_core 0.14.1", "quote", "syn", ] @@ -2846,6 +2880,7 @@ dependencies = [ "mas-jose", "parse-display", "serde", + "serde-enum-str", "serde_json", "serde_with", "sha2 0.10.2", @@ -3957,6 +3992,36 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-attributes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aba2af3c3b9cd6f3a919056dac6005b71fceecc1cdfa65c4df3912f64e07e60" +dependencies = [ + "darling_core 0.13.4", + "serde-rename-rule", + "syn", +] + +[[package]] +name = "serde-enum-str" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a41bf2fc78a58589b9a6948bfc918c9b2dc918732f2ac14eed982ffb876b39" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "serde-attributes", + "syn", +] + +[[package]] +name = "serde-rename-rule" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd2930103714ccef4f1fe5b6a5f2b6fdcfe462a6c802464714bd41e5b5097c33" + [[package]] name = "serde_bser" version = "0.3.1" @@ -4037,7 +4102,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de337f322382fcdfbb21a014f7c224ee041a23785651db67b9827403178f698f" dependencies = [ - "darling", + "darling 0.14.1", "proc-macro2", "quote", "syn", diff --git a/crates/handlers/src/oauth2/authorization/mod.rs b/crates/handlers/src/oauth2/authorization/mod.rs index 27529682..15fe6afe 100644 --- a/crates/handlers/src/oauth2/authorization/mod.rs +++ b/crates/handlers/src/oauth2/authorization/mod.rs @@ -33,11 +33,7 @@ use mas_storage::oauth2::{ }; use mas_templates::Templates; use oauth2_types::{ - errors::{ - ACCESS_DENIED, CONSENT_REQUIRED, INTERACTION_REQUIRED, INVALID_REQUEST, LOGIN_REQUIRED, - REGISTRATION_NOT_SUPPORTED, REQUEST_NOT_SUPPORTED, REQUEST_URI_NOT_SUPPORTED, SERVER_ERROR, - UNAUTHORIZED_CLIENT, - }, + errors::{ClientError, ClientErrorCode}, pkce, prelude::*, requests::{AuthorizationRequest, GrantType, Prompt, ResponseMode}, @@ -202,32 +198,49 @@ pub(crate) async fn get( // with the right error since we don't support them. if params.auth.request.is_some() { return Ok(callback_destination - .go(&templates, REQUEST_NOT_SUPPORTED) + .go( + &templates, + ClientError::from(ClientErrorCode::RequestNotSupported), + ) .await?); } if params.auth.request_uri.is_some() { return Ok(callback_destination - .go(&templates, REQUEST_URI_NOT_SUPPORTED) + .go( + &templates, + ClientError::from(ClientErrorCode::RequestUriNotSupported), + ) .await?); } if params.auth.registration.is_some() { return Ok(callback_destination - .go(&templates, REGISTRATION_NOT_SUPPORTED) + .go( + &templates, + ClientError::from(ClientErrorCode::RegistrationNotSupported), + ) .await?); } // Check if it is allowed to use this grant type if !client.grant_types.contains(&GrantType::AuthorizationCode) { return Ok(callback_destination - .go(&templates, UNAUTHORIZED_CLIENT) + .go( + &templates, + ClientError::from(ClientErrorCode::UnauthorizedClient), + ) .await?); } // Fail early if prompt=none and there is no active session if params.auth.prompt == Some(Prompt::None) && maybe_session.is_none() { - return Ok(callback_destination.go(&templates, LOGIN_REQUIRED).await?); + return Ok(callback_destination + .go( + &templates, + ClientError::from(ClientErrorCode::LoginRequired), + ) + .await?); } let code: Option = if response_type.has_code() { @@ -248,7 +261,12 @@ pub(crate) async fn get( // If the request had PKCE params but no code asked, it should get back with an // error if params.pkce.is_some() { - return Ok(callback_destination.go(&templates, INVALID_REQUEST).await?); + return Ok(callback_destination + .go( + &templates, + ClientError::from(ClientErrorCode::InvalidRequest), + ) + .await?); } None @@ -315,16 +333,24 @@ pub(crate) async fn get( Ok(params) => callback_destination.go(&templates, params).await?, Err(GrantCompletionError::RequiresConsent) => { callback_destination - .go(&templates, CONSENT_REQUIRED) + .go( + &templates, + ClientError::from(ClientErrorCode::ConsentRequired), + ) .await? } Err(GrantCompletionError::RequiresReauth) => { callback_destination - .go(&templates, INTERACTION_REQUIRED) + .go( + &templates, + ClientError::from(ClientErrorCode::InteractionRequired), + ) .await? } Err(GrantCompletionError::PolicyViolation) => { - callback_destination.go(&templates, ACCESS_DENIED).await? + callback_destination + .go(&templates, ClientError::from(ClientErrorCode::AccessDenied)) + .await? } Err(GrantCompletionError::Anyhow(a)) => return Err(RouteError::Anyhow(a)), Err(GrantCompletionError::Internal(e)) => { @@ -378,7 +404,9 @@ pub(crate) async fn get( Ok(r) => r, Err(err) => { tracing::error!(%err); - callback_destination.go(&templates, SERVER_ERROR).await? + callback_destination + .go(&templates, ClientError::from(ClientErrorCode::ServerError)) + .await? } }; diff --git a/crates/handlers/src/oauth2/registration.rs b/crates/handlers/src/oauth2/registration.rs index c46fab1e..302dfcbc 100644 --- a/crates/handlers/src/oauth2/registration.rs +++ b/crates/handlers/src/oauth2/registration.rs @@ -19,7 +19,7 @@ use hyper::StatusCode; use mas_policy::{PolicyFactory, Violation}; use mas_storage::oauth2::client::insert_client; use oauth2_types::{ - errors::{INVALID_CLIENT_METADATA, INVALID_REDIRECT_URI, SERVER_ERROR}, + errors::{ClientError, ClientErrorCode}, registration::{ ClientMetadata, ClientMetadataVerificationError, ClientRegistrationResponse, Localized, }, @@ -65,36 +65,24 @@ impl From for RouteError { } } -// TODO: there is probably a better way to do achieve this. ClientError only -// works for static strings -#[derive(serde::Serialize)] -struct PolicyError { - error: String, - error_description: String, -} - -impl PolicyError { - #[must_use] - pub const fn new(error: String, error_description: String) -> Self { - Self { - error, - error_description, - } - } -} - impl IntoResponse for RouteError { fn into_response(self) -> axum::response::Response { match self { - Self::Internal(_) | Self::Anyhow(_) => { - (StatusCode::INTERNAL_SERVER_ERROR, Json(SERVER_ERROR)).into_response() - } - Self::InvalidRedirectUri => { - (StatusCode::BAD_REQUEST, Json(INVALID_REDIRECT_URI)).into_response() - } - Self::InvalidClientMetadata => { - (StatusCode::BAD_REQUEST, Json(INVALID_CLIENT_METADATA)).into_response() - } + Self::Internal(_) | Self::Anyhow(_) => ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ClientError::from(ClientErrorCode::ServerError)), + ) + .into_response(), + Self::InvalidRedirectUri => ( + StatusCode::BAD_REQUEST, + Json(ClientError::from(ClientErrorCode::InvalidRedirectUri)), + ) + .into_response(), + Self::InvalidClientMetadata => ( + StatusCode::BAD_REQUEST, + Json(ClientError::from(ClientErrorCode::InvalidClientMetadata)), + ) + .into_response(), Self::PolicyDenied(violations) => { let collected = &violations .iter() @@ -104,10 +92,10 @@ impl IntoResponse for RouteError { ( StatusCode::UNAUTHORIZED, - Json(PolicyError::new( - "invalid_client_metadata".to_owned(), - joined, - )), + Json( + ClientError::from(ClientErrorCode::InvalidClientMetadata) + .with_description(joined), + ), ) .into_response() } diff --git a/crates/handlers/src/oauth2/token.rs b/crates/handlers/src/oauth2/token.rs index 8cc8d965..e42d9232 100644 --- a/crates/handlers/src/oauth2/token.rs +++ b/crates/handlers/src/oauth2/token.rs @@ -43,7 +43,7 @@ use mas_storage::{ DatabaseInconsistencyError, PostgresqlBackend, }; use oauth2_types::{ - errors::{INVALID_CLIENT, INVALID_GRANT, INVALID_REQUEST, SERVER_ERROR, UNAUTHORIZED_CLIENT}, + errors::{ClientError, ClientErrorCode}, pkce::CodeChallengeError, requests::{ AccessTokenRequest, AccessTokenResponse, AuthorizationCodeGrant, RefreshTokenGrant, @@ -129,21 +129,33 @@ impl From for RouteError { impl IntoResponse for RouteError { fn into_response(self) -> axum::response::Response { match self { - Self::Internal(_) | Self::Anyhow(_) => { - (StatusCode::INTERNAL_SERVER_ERROR, Json(SERVER_ERROR)) - } - Self::BadRequest => (StatusCode::BAD_REQUEST, Json(INVALID_REQUEST)), + Self::Internal(_) | Self::Anyhow(_) => ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(ClientError::from(ClientErrorCode::ServerError)), + ), + Self::BadRequest => ( + StatusCode::BAD_REQUEST, + Json(ClientError::from(ClientErrorCode::InvalidRequest)), + ), Self::PkceVerification(err) => ( StatusCode::BAD_REQUEST, - Json(INVALID_GRANT.with_description(format!("PKCE verification failed: {err}"))), + Json( + ClientError::from(ClientErrorCode::InvalidGrant) + .with_description(format!("PKCE verification failed: {err}")), + ), + ), + Self::ClientNotFound | Self::ClientCredentialsVerification(_) => ( + StatusCode::UNAUTHORIZED, + Json(ClientError::from(ClientErrorCode::InvalidClient)), + ), + Self::ClientNotAllowed | Self::UnauthorizedClient => ( + StatusCode::UNAUTHORIZED, + Json(ClientError::from(ClientErrorCode::UnauthorizedClient)), + ), + Self::InvalidGrant => ( + StatusCode::BAD_REQUEST, + Json(ClientError::from(ClientErrorCode::InvalidGrant)), ), - Self::ClientNotFound | Self::ClientCredentialsVerification(_) => { - (StatusCode::UNAUTHORIZED, Json(INVALID_CLIENT)) - } - Self::ClientNotAllowed | Self::UnauthorizedClient => { - (StatusCode::UNAUTHORIZED, Json(UNAUTHORIZED_CLIENT)) - } - Self::InvalidGrant => (StatusCode::BAD_REQUEST, Json(INVALID_GRANT)), } .into_response() } diff --git a/crates/oauth2-types/Cargo.toml b/crates/oauth2-types/Cargo.toml index 2fdfc27c..ac471b2d 100644 --- a/crates/oauth2-types/Cargo.toml +++ b/crates/oauth2-types/Cargo.toml @@ -19,6 +19,7 @@ sha2 = "0.10.2" data-encoding = "2.3.2" thiserror = "1.0.32" itertools = "0.10.3" +serde-enum-str = "0.2.5" mas-iana = { path = "../iana" } mas-jose = { path = "../jose" } diff --git a/crates/oauth2-types/src/errors.rs b/crates/oauth2-types/src/errors.rs index 2920e894..0d39daa7 100644 --- a/crates/oauth2-types/src/errors.rs +++ b/crates/oauth2-types/src/errors.rs @@ -14,150 +14,296 @@ use std::borrow::Cow; -#[derive(serde::Serialize, Clone)] +use serde::{Deserialize, Serialize}; +use serde_enum_str::{Deserialize_enum_str, Serialize_enum_str}; + +/// A client error returned by an authorization server. +/// +/// To construct this with a default description for the error code, use its +/// `From` implementation. +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct ClientError { - pub error: &'static str, + /// The error code. + pub error: ClientErrorCode, + + /// A human-readable description of the error. pub error_description: Cow<'static, str>, } impl ClientError { + /// Creates a new `ClientError` with the given error code and description. #[must_use] - pub const fn new(error: &'static str, error_description: &'static str) -> Self { + pub const fn new(error: ClientErrorCode, error_description: &'static str) -> Self { Self { error, error_description: Cow::Borrowed(error_description), } } + /// Changes the description of this `ClientError` with the given `String`. #[must_use] - pub const fn with_description(&self, description: String) -> Self { - Self { - error: self.error, - error_description: Cow::Owned(description), - } + pub fn with_description(mut self, description: String) -> Self { + self.error_description = Cow::Owned(description); + self } } -pub mod rfc6749 { - use super::ClientError; - - pub const INVALID_REQUEST: ClientError = ClientError::new( - "invalid_request", - "The request is missing a required parameter, \ - includes an invalid parameter value, \ - includes a parameter more than once, \ - or is otherwise malformed.", - ); - - pub const INVALID_CLIENT: ClientError = - ClientError::new("invalid_client", "Client authentication failed."); - - pub const INVALID_GRANT: ClientError = ClientError::new( - "invalid_grant", - "The provided access grant is invalid, expired, or revoked.", - ); - - pub const UNAUTHORIZED_CLIENT: ClientError = ClientError::new( - "unauthorized_client", - "The client is not authorized to request an access token using this method.", - ); - - pub const UNSUPPORTED_GRANT_TYPE: ClientError = ClientError::new( - "unsupported_grant_type", - "The authorization grant type is not supported by the authorization server.", - ); - - pub const ACCESS_DENIED: ClientError = ClientError::new( - "access_denied", - "The resource owner or authorization server denied the request.", - ); - - pub const UNSUPPORTED_RESPONSE_TYPE: ClientError = ClientError::new( - "unsupported_response_type", - "The authorization server does not support obtaining an access token using this method.", - ); - - pub const INVALID_SCOPE: ClientError = ClientError::new( - "invalid_scope", - "The requested scope is invalid, unknown, or malformed.", - ); - - pub const SERVER_ERROR: ClientError = ClientError::new( - "server_error", - "The authorization server encountered an unexpected condition \ - that prevented it from fulfilling the request.", - ); - - pub const TEMPORARILY_UNAVAILABLE: ClientError = ClientError::new( - "temporarily_unavailable", - "The authorization server is currently unable to handle the request \ - due to a temporary overloading or maintenance of the server.", - ); +impl From for ClientError { + fn from(error: ClientErrorCode) -> Self { + let desc = error.default_description(); + Self::new(error, desc) + } } -pub mod oidc_core { - use super::ClientError; +#[derive(Debug, Clone, PartialEq, Eq, Serialize_enum_str, Deserialize_enum_str)] +#[serde(rename_all = "snake_case")] +pub enum ClientErrorCode { + /// `invalid_request` + /// + /// The request is missing a required parameter, includes an invalid + /// parameter value, includes a parameter more than once, or is otherwise + /// malformed. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-5.2). + InvalidRequest, - pub const INTERACTION_REQUIRED: ClientError = ClientError::new( - "interaction_required", - "The Authorization Server requires End-User interaction of some form to proceed.", - ); + /// `invalid_client` + /// + /// Client authentication failed (e.g., unknown client, no client + /// authentication included, or unsupported authentication method). + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-5.2). + InvalidClient, - pub const LOGIN_REQUIRED: ClientError = ClientError::new( - "login_required", - "The Authorization Server requires End-User authentication.", - ); + /// `invalid_grant` + /// + /// The provided authorization grant (e.g., authorization code, resource + /// owner credentials) or refresh token is invalid, expired, revoked, does + /// not match the redirection URI used in the authorization request, or was + /// issued to another client. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-5.2). + InvalidGrant, - pub const ACCOUNT_SELECTION_REQUIRED: ClientError = ClientError::new( - "account_selection_required", - "The End-User is REQUIRED to select a session at the Authorization Server.", - ); + /// `unauthorized_client` + /// + /// The authenticated client is not authorized to use this authorization + /// grant type. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-5.2). + UnauthorizedClient, - pub const CONSENT_REQUIRED: ClientError = ClientError::new( - "consent_required", - "The Authorization Server requires End-User consent.", - ); + /// `unsupported_grant_type` + /// + /// The authorization grant type is not supported by the authorization + /// server. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-5.2). + UnsupportedGrantType, - pub const INVALID_REQUEST_URI: ClientError = ClientError::new( - "invalid_request_uri", - "The request_uri in the Authorization Request returns an error or contains invalid data. ", - ); + /// `access_denied` + /// + /// The resource owner or authorization server denied the request. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1). + AccessDenied, - pub const INVALID_REQUEST_OBJECT: ClientError = ClientError::new( - "invalid_request_object", - "The request parameter contains an invalid Request Object.", - ); + /// `unsupported_response_type` + /// + /// The authorization server does not support obtaining an authorization + /// code using this method. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1). + UnsupportedResponseType, - pub const REQUEST_NOT_SUPPORTED: ClientError = ClientError::new( - "request_not_supported", - "The provider does not support use of the request parameter.", - ); + /// `invalid_scope` + /// + /// The requested scope is invalid, unknown, malformed, or exceeds the scope + /// granted by the resource owner. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1). + InvalidScope, - pub const REQUEST_URI_NOT_SUPPORTED: ClientError = ClientError::new( - "request_uri_not_supported", - "The provider does not support use of the request_uri parameter.", - ); + /// `server_error` + /// + /// The authorization server encountered an unexpected condition that + /// prevented it from fulfilling the request. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1). + ServerError, - pub const REGISTRATION_NOT_SUPPORTED: ClientError = ClientError::new( - "registration_not_supported", - "The provider does not support use of the registration parameter.", - ); + /// `temporarily_unavailable` + /// + /// The authorization server is currently unable to handle the request due + /// to a temporary overloading or maintenance of the server. + /// + /// From [RFC6749](https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1). + TemporarilyUnavailable, + + /// `interaction_required` + /// + /// The authorization server requires end-user interaction of some form to + /// proceed. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + InteractionRequired, + + /// `login_required` + /// + /// The authorization server requires end-user authentication. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + LoginRequired, + + /// `account_selection_required` + /// + /// The end-user is required to select a session at the authorization + /// server. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + AccountSelectionRequired, + + /// `consent_required` + /// + /// The authorization server requires end-user consent. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + ConsentRequired, + + /// `invalid_request_uri` + /// + /// The `request_uri` in the authorization request returns an error or + /// contains invalid data. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + InvalidRequestUri, + + /// `invalid_request_object` + /// + /// The request parameter contains an invalid request object. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + InvalidRequestObject, + + /// `request_not_supported` + /// + /// The authorization server does not support use of the `request` + /// parameter. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + RequestNotSupported, + + /// `request_uri_not_supported` + /// + /// The authorization server does not support use of the `request_uri` + /// parameter. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + RequestUriNotSupported, + + /// `registration_not_supported` + /// + /// The authorization server does not support use of the `registration` + /// parameter. + /// + /// From [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthError). + RegistrationNotSupported, + + /// `invalid_redirect_uri` + /// + /// The value of one or more redirection URIs is invalid. + /// + /// From [RFC7591](https://www.rfc-editor.org/rfc/rfc7591#section-3.2.2). + InvalidRedirectUri, + + /// `invalid_client_metadata` + /// + /// The value of one of the client metadata fields is invalid and the server + /// has rejected this request. + /// + /// From [RFC7591](https://www.rfc-editor.org/rfc/rfc7591#section-3.2.2). + InvalidClientMetadata, + + /// Another error code. + #[serde(other)] + Unknown(String), } -mod rfc7591 { - use super::ClientError; - - pub const INVALID_REDIRECT_URI: ClientError = ClientError::new( - "invalid_redirect_uri", - "The value of one or more redirection URIs is invalid.", - ); - - pub const INVALID_CLIENT_METADATA: ClientError = ClientError::new( - "invalid_client_metadata", - "The value of one of the client metadata fields is invalid", - ); +impl ClientErrorCode { + /// Get the default description for this `ClientErrorCode`. + /// + /// Note that [`ClientErrorCode::Unknown`] returns an empty string. + #[must_use] + pub fn default_description(&self) -> &'static str { + match self { + ClientErrorCode::InvalidRequest => { + "The request is missing a required parameter, includes an \ + invalid parameter value, includes a parameter more than once, \ + or is otherwise malformed." + } + ClientErrorCode::InvalidClient => "Client authentication failed.", + ClientErrorCode::InvalidGrant => { + "The provided access grant is invalid, expired, or revoked." + } + ClientErrorCode::UnauthorizedClient => { + "The client is not authorized to request an access token using this method." + } + ClientErrorCode::UnsupportedGrantType => { + "The authorization grant type is not supported by the authorization server." + } + ClientErrorCode::AccessDenied => { + "The resource owner or authorization server denied the request." + } + ClientErrorCode::UnsupportedResponseType => { + "The authorization server does not support obtaining an access \ + token using this method." + } + ClientErrorCode::InvalidScope => { + "The requested scope is invalid, unknown, or malformed." + } + ClientErrorCode::ServerError => { + "The authorization server encountered an unexpected condition \ + that prevented it from fulfilling the request." + } + ClientErrorCode::TemporarilyUnavailable => { + "The authorization server is currently unable to handle the request \ + due to a temporary overloading or maintenance of the server." + } + ClientErrorCode::InteractionRequired => { + "The Authorization Server requires End-User interaction of some form to proceed." + } + ClientErrorCode::LoginRequired => { + "The Authorization Server requires End-User authentication." + } + ClientErrorCode::AccountSelectionRequired => { + "The End-User is required to select a session at the Authorization Server." + } + ClientErrorCode::ConsentRequired => { + "The Authorization Server requires End-User consent." + } + ClientErrorCode::InvalidRequestUri => { + "The request_uri in the Authorization Request returns an error \ + or contains invalid data." + } + ClientErrorCode::InvalidRequestObject => { + "The request parameter contains an invalid Request Object." + } + ClientErrorCode::RequestNotSupported => { + "The provider does not support use of the request parameter." + } + ClientErrorCode::RequestUriNotSupported => { + "The provider does not support use of the request_uri parameter." + } + ClientErrorCode::RegistrationNotSupported => { + "The provider does not support use of the registration parameter." + } + ClientErrorCode::InvalidRedirectUri => { + "The value of one or more redirection URIs is invalid." + } + ClientErrorCode::InvalidClientMetadata => { + "The value of one of the client metadata fields is invalid" + } + ClientErrorCode::Unknown(_) => "", + } + } } - -pub use oidc_core::*; -pub use rfc6749::*; -pub use rfc7591::*;