You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-07 17:03:01 +03:00
Use an enum for client error codes
Replace the ClientError constants with From<ClientErrorCode>.
This commit is contained in:
committed by
Quentin Gliech
parent
348044afdc
commit
ee47c821e3
73
Cargo.lock
generated
73
Cargo.lock
generated
@@ -1176,14 +1176,37 @@ dependencies = [
|
|||||||
"cipher 0.3.0",
|
"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]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.14.1",
|
||||||
"darling_macro",
|
"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]]
|
[[package]]
|
||||||
@@ -1200,13 +1223,24 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.14.1",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
@@ -2846,6 +2880,7 @@ dependencies = [
|
|||||||
"mas-jose",
|
"mas-jose",
|
||||||
"parse-display",
|
"parse-display",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde-enum-str",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"sha2 0.10.2",
|
"sha2 0.10.2",
|
||||||
@@ -3957,6 +3992,36 @@ dependencies = [
|
|||||||
"serde_derive",
|
"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]]
|
[[package]]
|
||||||
name = "serde_bser"
|
name = "serde_bser"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -4037,7 +4102,7 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de337f322382fcdfbb21a014f7c224ee041a23785651db67b9827403178f698f"
|
checksum = "de337f322382fcdfbb21a014f7c224ee041a23785651db67b9827403178f698f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.14.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
@@ -33,11 +33,7 @@ use mas_storage::oauth2::{
|
|||||||
};
|
};
|
||||||
use mas_templates::Templates;
|
use mas_templates::Templates;
|
||||||
use oauth2_types::{
|
use oauth2_types::{
|
||||||
errors::{
|
errors::{ClientError, ClientErrorCode},
|
||||||
ACCESS_DENIED, CONSENT_REQUIRED, INTERACTION_REQUIRED, INVALID_REQUEST, LOGIN_REQUIRED,
|
|
||||||
REGISTRATION_NOT_SUPPORTED, REQUEST_NOT_SUPPORTED, REQUEST_URI_NOT_SUPPORTED, SERVER_ERROR,
|
|
||||||
UNAUTHORIZED_CLIENT,
|
|
||||||
},
|
|
||||||
pkce,
|
pkce,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
requests::{AuthorizationRequest, GrantType, Prompt, ResponseMode},
|
requests::{AuthorizationRequest, GrantType, Prompt, ResponseMode},
|
||||||
@@ -202,32 +198,49 @@ pub(crate) async fn get(
|
|||||||
// with the right error since we don't support them.
|
// with the right error since we don't support them.
|
||||||
if params.auth.request.is_some() {
|
if params.auth.request.is_some() {
|
||||||
return Ok(callback_destination
|
return Ok(callback_destination
|
||||||
.go(&templates, REQUEST_NOT_SUPPORTED)
|
.go(
|
||||||
|
&templates,
|
||||||
|
ClientError::from(ClientErrorCode::RequestNotSupported),
|
||||||
|
)
|
||||||
.await?);
|
.await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.auth.request_uri.is_some() {
|
if params.auth.request_uri.is_some() {
|
||||||
return Ok(callback_destination
|
return Ok(callback_destination
|
||||||
.go(&templates, REQUEST_URI_NOT_SUPPORTED)
|
.go(
|
||||||
|
&templates,
|
||||||
|
ClientError::from(ClientErrorCode::RequestUriNotSupported),
|
||||||
|
)
|
||||||
.await?);
|
.await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.auth.registration.is_some() {
|
if params.auth.registration.is_some() {
|
||||||
return Ok(callback_destination
|
return Ok(callback_destination
|
||||||
.go(&templates, REGISTRATION_NOT_SUPPORTED)
|
.go(
|
||||||
|
&templates,
|
||||||
|
ClientError::from(ClientErrorCode::RegistrationNotSupported),
|
||||||
|
)
|
||||||
.await?);
|
.await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it is allowed to use this grant type
|
// Check if it is allowed to use this grant type
|
||||||
if !client.grant_types.contains(&GrantType::AuthorizationCode) {
|
if !client.grant_types.contains(&GrantType::AuthorizationCode) {
|
||||||
return Ok(callback_destination
|
return Ok(callback_destination
|
||||||
.go(&templates, UNAUTHORIZED_CLIENT)
|
.go(
|
||||||
|
&templates,
|
||||||
|
ClientError::from(ClientErrorCode::UnauthorizedClient),
|
||||||
|
)
|
||||||
.await?);
|
.await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail early if prompt=none and there is no active session
|
// Fail early if prompt=none and there is no active session
|
||||||
if params.auth.prompt == Some(Prompt::None) && maybe_session.is_none() {
|
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<AuthorizationCode> = if response_type.has_code() {
|
let code: Option<AuthorizationCode> = 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
|
// If the request had PKCE params but no code asked, it should get back with an
|
||||||
// error
|
// error
|
||||||
if params.pkce.is_some() {
|
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
|
None
|
||||||
@@ -315,16 +333,24 @@ pub(crate) async fn get(
|
|||||||
Ok(params) => callback_destination.go(&templates, params).await?,
|
Ok(params) => callback_destination.go(&templates, params).await?,
|
||||||
Err(GrantCompletionError::RequiresConsent) => {
|
Err(GrantCompletionError::RequiresConsent) => {
|
||||||
callback_destination
|
callback_destination
|
||||||
.go(&templates, CONSENT_REQUIRED)
|
.go(
|
||||||
|
&templates,
|
||||||
|
ClientError::from(ClientErrorCode::ConsentRequired),
|
||||||
|
)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
Err(GrantCompletionError::RequiresReauth) => {
|
Err(GrantCompletionError::RequiresReauth) => {
|
||||||
callback_destination
|
callback_destination
|
||||||
.go(&templates, INTERACTION_REQUIRED)
|
.go(
|
||||||
|
&templates,
|
||||||
|
ClientError::from(ClientErrorCode::InteractionRequired),
|
||||||
|
)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
Err(GrantCompletionError::PolicyViolation) => {
|
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::Anyhow(a)) => return Err(RouteError::Anyhow(a)),
|
||||||
Err(GrantCompletionError::Internal(e)) => {
|
Err(GrantCompletionError::Internal(e)) => {
|
||||||
@@ -378,7 +404,9 @@ pub(crate) async fn get(
|
|||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(%err);
|
tracing::error!(%err);
|
||||||
callback_destination.go(&templates, SERVER_ERROR).await?
|
callback_destination
|
||||||
|
.go(&templates, ClientError::from(ClientErrorCode::ServerError))
|
||||||
|
.await?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ use hyper::StatusCode;
|
|||||||
use mas_policy::{PolicyFactory, Violation};
|
use mas_policy::{PolicyFactory, Violation};
|
||||||
use mas_storage::oauth2::client::insert_client;
|
use mas_storage::oauth2::client::insert_client;
|
||||||
use oauth2_types::{
|
use oauth2_types::{
|
||||||
errors::{INVALID_CLIENT_METADATA, INVALID_REDIRECT_URI, SERVER_ERROR},
|
errors::{ClientError, ClientErrorCode},
|
||||||
registration::{
|
registration::{
|
||||||
ClientMetadata, ClientMetadataVerificationError, ClientRegistrationResponse, Localized,
|
ClientMetadata, ClientMetadataVerificationError, ClientRegistrationResponse, Localized,
|
||||||
},
|
},
|
||||||
@@ -65,36 +65,24 @@ impl From<ClientMetadataVerificationError> 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 {
|
impl IntoResponse for RouteError {
|
||||||
fn into_response(self) -> axum::response::Response {
|
fn into_response(self) -> axum::response::Response {
|
||||||
match self {
|
match self {
|
||||||
Self::Internal(_) | Self::Anyhow(_) => {
|
Self::Internal(_) | Self::Anyhow(_) => (
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, Json(SERVER_ERROR)).into_response()
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
Json(ClientError::from(ClientErrorCode::ServerError)),
|
||||||
Self::InvalidRedirectUri => {
|
)
|
||||||
(StatusCode::BAD_REQUEST, Json(INVALID_REDIRECT_URI)).into_response()
|
.into_response(),
|
||||||
}
|
Self::InvalidRedirectUri => (
|
||||||
Self::InvalidClientMetadata => {
|
StatusCode::BAD_REQUEST,
|
||||||
(StatusCode::BAD_REQUEST, Json(INVALID_CLIENT_METADATA)).into_response()
|
Json(ClientError::from(ClientErrorCode::InvalidRedirectUri)),
|
||||||
}
|
)
|
||||||
|
.into_response(),
|
||||||
|
Self::InvalidClientMetadata => (
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Json(ClientError::from(ClientErrorCode::InvalidClientMetadata)),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
Self::PolicyDenied(violations) => {
|
Self::PolicyDenied(violations) => {
|
||||||
let collected = &violations
|
let collected = &violations
|
||||||
.iter()
|
.iter()
|
||||||
@@ -104,10 +92,10 @@ impl IntoResponse for RouteError {
|
|||||||
|
|
||||||
(
|
(
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
Json(PolicyError::new(
|
Json(
|
||||||
"invalid_client_metadata".to_owned(),
|
ClientError::from(ClientErrorCode::InvalidClientMetadata)
|
||||||
joined,
|
.with_description(joined),
|
||||||
)),
|
),
|
||||||
)
|
)
|
||||||
.into_response()
|
.into_response()
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@ use mas_storage::{
|
|||||||
DatabaseInconsistencyError, PostgresqlBackend,
|
DatabaseInconsistencyError, PostgresqlBackend,
|
||||||
};
|
};
|
||||||
use oauth2_types::{
|
use oauth2_types::{
|
||||||
errors::{INVALID_CLIENT, INVALID_GRANT, INVALID_REQUEST, SERVER_ERROR, UNAUTHORIZED_CLIENT},
|
errors::{ClientError, ClientErrorCode},
|
||||||
pkce::CodeChallengeError,
|
pkce::CodeChallengeError,
|
||||||
requests::{
|
requests::{
|
||||||
AccessTokenRequest, AccessTokenResponse, AuthorizationCodeGrant, RefreshTokenGrant,
|
AccessTokenRequest, AccessTokenResponse, AuthorizationCodeGrant, RefreshTokenGrant,
|
||||||
@@ -129,21 +129,33 @@ impl From<RefreshTokenLookupError> for RouteError {
|
|||||||
impl IntoResponse for RouteError {
|
impl IntoResponse for RouteError {
|
||||||
fn into_response(self) -> axum::response::Response {
|
fn into_response(self) -> axum::response::Response {
|
||||||
match self {
|
match self {
|
||||||
Self::Internal(_) | Self::Anyhow(_) => {
|
Self::Internal(_) | Self::Anyhow(_) => (
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, Json(SERVER_ERROR))
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
Json(ClientError::from(ClientErrorCode::ServerError)),
|
||||||
Self::BadRequest => (StatusCode::BAD_REQUEST, Json(INVALID_REQUEST)),
|
),
|
||||||
|
Self::BadRequest => (
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Json(ClientError::from(ClientErrorCode::InvalidRequest)),
|
||||||
|
),
|
||||||
Self::PkceVerification(err) => (
|
Self::PkceVerification(err) => (
|
||||||
StatusCode::BAD_REQUEST,
|
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()
|
.into_response()
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,7 @@ sha2 = "0.10.2"
|
|||||||
data-encoding = "2.3.2"
|
data-encoding = "2.3.2"
|
||||||
thiserror = "1.0.32"
|
thiserror = "1.0.32"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.3"
|
||||||
|
serde-enum-str = "0.2.5"
|
||||||
|
|
||||||
mas-iana = { path = "../iana" }
|
mas-iana = { path = "../iana" }
|
||||||
mas-jose = { path = "../jose" }
|
mas-jose = { path = "../jose" }
|
||||||
|
@@ -14,150 +14,296 @@
|
|||||||
|
|
||||||
use std::borrow::Cow;
|
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<ClientErrorCode>` implementation.
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ClientError {
|
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>,
|
pub error_description: Cow<'static, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientError {
|
impl ClientError {
|
||||||
|
/// Creates a new `ClientError` with the given error code and description.
|
||||||
#[must_use]
|
#[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 {
|
Self {
|
||||||
error,
|
error,
|
||||||
error_description: Cow::Borrowed(error_description),
|
error_description: Cow::Borrowed(error_description),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Changes the description of this `ClientError` with the given `String`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_description(&self, description: String) -> Self {
|
pub fn with_description(mut self, description: String) -> Self {
|
||||||
Self {
|
self.error_description = Cow::Owned(description);
|
||||||
error: self.error,
|
self
|
||||||
error_description: Cow::Owned(description),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod rfc6749 {
|
impl From<ClientErrorCode> for ClientError {
|
||||||
use super::ClientError;
|
fn from(error: ClientErrorCode) -> Self {
|
||||||
|
let desc = error.default_description();
|
||||||
|
Self::new(error, desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const INVALID_REQUEST: ClientError = ClientError::new(
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize_enum_str, Deserialize_enum_str)]
|
||||||
"invalid_request",
|
#[serde(rename_all = "snake_case")]
|
||||||
"The request is missing a required parameter, \
|
pub enum ClientErrorCode {
|
||||||
includes an invalid parameter value, \
|
/// `invalid_request`
|
||||||
includes a parameter more than once, \
|
///
|
||||||
or is otherwise malformed.",
|
/// 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 INVALID_CLIENT: ClientError =
|
/// `invalid_client`
|
||||||
ClientError::new("invalid_client", "Client authentication failed.");
|
///
|
||||||
|
/// 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 INVALID_GRANT: ClientError = ClientError::new(
|
/// `invalid_grant`
|
||||||
"invalid_grant",
|
///
|
||||||
"The provided access grant is invalid, expired, or revoked.",
|
/// 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 UNAUTHORIZED_CLIENT: ClientError = ClientError::new(
|
/// `unauthorized_client`
|
||||||
"unauthorized_client",
|
///
|
||||||
"The client is not authorized to request an access token using this method.",
|
/// 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 UNSUPPORTED_GRANT_TYPE: ClientError = ClientError::new(
|
/// `unsupported_grant_type`
|
||||||
"unsupported_grant_type",
|
///
|
||||||
"The authorization grant type is not supported by the authorization server.",
|
/// 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 ACCESS_DENIED: ClientError = ClientError::new(
|
/// `access_denied`
|
||||||
"access_denied",
|
///
|
||||||
"The resource owner or authorization server denied the request.",
|
/// 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 UNSUPPORTED_RESPONSE_TYPE: ClientError = ClientError::new(
|
/// `unsupported_response_type`
|
||||||
"unsupported_response_type",
|
///
|
||||||
"The authorization server does not support obtaining an access token using this method.",
|
/// 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 INVALID_SCOPE: ClientError = ClientError::new(
|
/// `invalid_scope`
|
||||||
"invalid_scope",
|
///
|
||||||
"The requested scope is invalid, unknown, or malformed.",
|
/// 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 SERVER_ERROR: ClientError = ClientError::new(
|
/// `server_error`
|
||||||
"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,
|
||||||
|
|
||||||
|
/// `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),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 \
|
"The authorization server encountered an unexpected condition \
|
||||||
that prevented it from fulfilling the request.",
|
that prevented it from fulfilling the request."
|
||||||
);
|
}
|
||||||
|
ClientErrorCode::TemporarilyUnavailable => {
|
||||||
pub const TEMPORARILY_UNAVAILABLE: ClientError = ClientError::new(
|
|
||||||
"temporarily_unavailable",
|
|
||||||
"The authorization server is currently unable to handle the request \
|
"The authorization server is currently unable to handle the request \
|
||||||
due to a temporary overloading or maintenance of the server.",
|
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 mod oidc_core {
|
|
||||||
use super::ClientError;
|
|
||||||
|
|
||||||
pub const INTERACTION_REQUIRED: ClientError = ClientError::new(
|
|
||||||
"interaction_required",
|
|
||||||
"The Authorization Server requires End-User interaction of some form to proceed.",
|
|
||||||
);
|
|
||||||
|
|
||||||
pub const LOGIN_REQUIRED: ClientError = ClientError::new(
|
|
||||||
"login_required",
|
|
||||||
"The Authorization Server requires End-User authentication.",
|
|
||||||
);
|
|
||||||
|
|
||||||
pub const ACCOUNT_SELECTION_REQUIRED: ClientError = ClientError::new(
|
|
||||||
"account_selection_required",
|
|
||||||
"The End-User is REQUIRED to select a session at the Authorization Server.",
|
|
||||||
);
|
|
||||||
|
|
||||||
pub const CONSENT_REQUIRED: ClientError = ClientError::new(
|
|
||||||
"consent_required",
|
|
||||||
"The Authorization Server requires End-User consent.",
|
|
||||||
);
|
|
||||||
|
|
||||||
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. ",
|
|
||||||
);
|
|
||||||
|
|
||||||
pub const INVALID_REQUEST_OBJECT: ClientError = ClientError::new(
|
|
||||||
"invalid_request_object",
|
|
||||||
"The request parameter contains an invalid Request Object.",
|
|
||||||
);
|
|
||||||
|
|
||||||
pub const REQUEST_NOT_SUPPORTED: ClientError = ClientError::new(
|
|
||||||
"request_not_supported",
|
|
||||||
"The provider does not support use of the request parameter.",
|
|
||||||
);
|
|
||||||
|
|
||||||
pub const REQUEST_URI_NOT_SUPPORTED: ClientError = ClientError::new(
|
|
||||||
"request_uri_not_supported",
|
|
||||||
"The provider does not support use of the request_uri parameter.",
|
|
||||||
);
|
|
||||||
|
|
||||||
pub const REGISTRATION_NOT_SUPPORTED: ClientError = ClientError::new(
|
|
||||||
"registration_not_supported",
|
|
||||||
"The provider does not support use of the registration parameter.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use oidc_core::*;
|
|
||||||
pub use rfc6749::*;
|
|
||||||
pub use rfc7591::*;
|
|
||||||
|
Reference in New Issue
Block a user