diff --git a/Cargo.lock b/Cargo.lock index 926b3031..c5f37f57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3869,7 +3869,6 @@ dependencies = [ "language-tags", "mas-iana", "mas-jose", - "parse-display", "serde", "serde_json", "serde_with", @@ -4156,31 +4155,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "parse-display" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06af5f9333eb47bd9ba8462d612e37a8328a5cb80b13f0af4de4c3b89f52dee5" -dependencies = [ - "parse-display-derive", - "regex", - "regex-syntax 0.8.2", -] - -[[package]] -name = "parse-display-derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc9252f259500ee570c75adcc4e317fa6f57a1e47747d622e0bf838002a7b790" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "regex-syntax 0.8.2", - "structmeta", - "syn 2.0.53", -] - [[package]] name = "parse-size" version = "1.0.0" @@ -5857,29 +5831,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" -[[package]] -name = "structmeta" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" -dependencies = [ - "proc-macro2", - "quote", - "structmeta-derive", - "syn 2.0.53", -] - -[[package]] -name = "structmeta-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.53", -] - [[package]] name = "strum" version = "0.25.0" diff --git a/crates/graphql/src/model/oauth.rs b/crates/graphql/src/model/oauth.rs index 6cb9a2a3..3307e16a 100644 --- a/crates/graphql/src/model/oauth.rs +++ b/crates/graphql/src/model/oauth.rs @@ -197,9 +197,10 @@ impl OAuth2Client { /// The application type advertised by the client. pub async fn application_type(&self) -> Option { - match self.0.application_type? { + match self.0.application_type.as_ref()? { ApplicationType::Web => Some(OAuth2ApplicationType::Web), ApplicationType::Native => Some(OAuth2ApplicationType::Native), + ApplicationType::Unknown(_) => None, } } } diff --git a/crates/handlers/src/oauth2/registration.rs b/crates/handlers/src/oauth2/registration.rs index 3598bab8..5b493b14 100644 --- a/crates/handlers/src/oauth2/registration.rs +++ b/crates/handlers/src/oauth2/registration.rs @@ -268,7 +268,7 @@ pub(crate) async fn post( &clock, metadata.redirect_uris().to_vec(), encrypted_client_secret, - metadata.application_type, + metadata.application_type.clone(), //&metadata.response_types(), metadata.grant_types().to_vec(), metadata.contacts.clone().unwrap_or_default(), diff --git a/crates/oauth2-types/Cargo.toml b/crates/oauth2-types/Cargo.toml index 5b674d93..6893018d 100644 --- a/crates/oauth2-types/Cargo.toml +++ b/crates/oauth2-types/Cargo.toml @@ -17,7 +17,6 @@ serde.workspace = true serde_json.workspace = true language-tags = { version = "0.3.2", features = ["serde"] } url.workspace = true -parse-display = "0.9.0" serde_with = { version = "3.7.0", features = ["chrono"] } chrono.workspace = true sha2 = "0.10.8" diff --git a/crates/oauth2-types/src/errors.rs b/crates/oauth2-types/src/errors.rs index 2b04c7e1..af6135a5 100644 --- a/crates/oauth2-types/src/errors.rs +++ b/crates/oauth2-types/src/errors.rs @@ -16,7 +16,6 @@ use std::borrow::Cow; -use parse_display::{Display, FromStr}; use serde::{Deserialize, Serialize}; use serde_with::{DeserializeFromStr, SerializeDisplay}; @@ -60,8 +59,7 @@ impl From for ClientError { } /// Client error codes defined in OAuth2.0, OpenID Connect and their extensions. -#[derive(Debug, Clone, PartialEq, Eq, Display, FromStr, SerializeDisplay, DeserializeFromStr)] -#[display(style = "snake_case")] +#[derive(Debug, Clone, PartialEq, Eq, SerializeDisplay, DeserializeFromStr)] pub enum ClientErrorCode { /// `invalid_request` /// @@ -276,10 +274,77 @@ pub enum ClientErrorCode { UnsupportedTokenType, /// Another error code. - #[display("{0}")] Unknown(String), } +impl core::fmt::Display for ClientErrorCode { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ClientErrorCode::InvalidRequest => f.write_str("invalid_request"), + ClientErrorCode::InvalidClient => f.write_str("invalid_client"), + ClientErrorCode::InvalidGrant => f.write_str("invalid_grant"), + ClientErrorCode::UnauthorizedClient => f.write_str("unauthorized_client"), + ClientErrorCode::UnsupportedGrantType => f.write_str("unsupported_grant_type"), + ClientErrorCode::AccessDenied => f.write_str("access_denied"), + ClientErrorCode::UnsupportedResponseType => f.write_str("unsupported_response_type"), + ClientErrorCode::InvalidScope => f.write_str("invalid_scope"), + ClientErrorCode::ServerError => f.write_str("server_error"), + ClientErrorCode::TemporarilyUnavailable => f.write_str("temporarily_unavailable"), + ClientErrorCode::InteractionRequired => f.write_str("interaction_required"), + ClientErrorCode::LoginRequired => f.write_str("login_required"), + ClientErrorCode::AccountSelectionRequired => f.write_str("account_selection_required"), + ClientErrorCode::ConsentRequired => f.write_str("consent_required"), + ClientErrorCode::InvalidRequestUri => f.write_str("invalid_request_uri"), + ClientErrorCode::InvalidRequestObject => f.write_str("invalid_request_object"), + ClientErrorCode::RequestNotSupported => f.write_str("request_not_supported"), + ClientErrorCode::RequestUriNotSupported => f.write_str("request_uri_not_supported"), + ClientErrorCode::RegistrationNotSupported => f.write_str("registration_not_supported"), + ClientErrorCode::InvalidRedirectUri => f.write_str("invalid_redirect_uri"), + ClientErrorCode::InvalidClientMetadata => f.write_str("invalid_client_metadata"), + ClientErrorCode::AuthorizationPending => f.write_str("authorization_pending"), + ClientErrorCode::SlowDown => f.write_str("slow_down"), + ClientErrorCode::ExpiredToken => f.write_str("expired_token"), + ClientErrorCode::UnsupportedTokenType => f.write_str("unsupported_token_type"), + ClientErrorCode::Unknown(value) => f.write_str(value), + } + } +} + +impl core::str::FromStr for ClientErrorCode { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "invalid_request" => Ok(ClientErrorCode::InvalidRequest), + "invalid_client" => Ok(ClientErrorCode::InvalidClient), + "invalid_grant" => Ok(ClientErrorCode::InvalidGrant), + "unauthorized_client" => Ok(ClientErrorCode::UnauthorizedClient), + "unsupported_grant_type" => Ok(ClientErrorCode::UnsupportedGrantType), + "access_denied" => Ok(ClientErrorCode::AccessDenied), + "unsupported_response_type" => Ok(ClientErrorCode::UnsupportedResponseType), + "invalid_scope" => Ok(ClientErrorCode::InvalidScope), + "server_error" => Ok(ClientErrorCode::ServerError), + "temporarily_unavailable" => Ok(ClientErrorCode::TemporarilyUnavailable), + "interaction_required" => Ok(ClientErrorCode::InteractionRequired), + "login_required" => Ok(ClientErrorCode::LoginRequired), + "account_selection_required" => Ok(ClientErrorCode::AccountSelectionRequired), + "consent_required" => Ok(ClientErrorCode::ConsentRequired), + "invalid_request_uri" => Ok(ClientErrorCode::InvalidRequestUri), + "invalid_request_object" => Ok(ClientErrorCode::InvalidRequestObject), + "request_not_supported" => Ok(ClientErrorCode::RequestNotSupported), + "request_uri_not_supported" => Ok(ClientErrorCode::RequestUriNotSupported), + "registration_not_supported" => Ok(ClientErrorCode::RegistrationNotSupported), + "invalid_redirect_uri" => Ok(ClientErrorCode::InvalidRedirectUri), + "invalid_client_metadata" => Ok(ClientErrorCode::InvalidClientMetadata), + "authorization_pending" => Ok(ClientErrorCode::AuthorizationPending), + "slow_down" => Ok(ClientErrorCode::SlowDown), + "expired_token" => Ok(ClientErrorCode::ExpiredToken), + "unsupported_token_type" => Ok(ClientErrorCode::UnsupportedTokenType), + _ => Ok(ClientErrorCode::Unknown(s.to_owned())), + } + } +} + impl ClientErrorCode { /// Get the default description for this `ClientErrorCode`. /// diff --git a/crates/oauth2-types/src/oidc.rs b/crates/oauth2-types/src/oidc.rs index 01bd24f3..75b95ed3 100644 --- a/crates/oauth2-types/src/oidc.rs +++ b/crates/oauth2-types/src/oidc.rs @@ -16,14 +16,13 @@ //! //! [OpenID Connect]: https://openid.net/connect/ -use std::{fmt, ops::Deref, str::FromStr}; +use std::{fmt, ops::Deref}; use language_tags::LanguageTag; use mas_iana::{ jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg}, oauth::{OAuthAccessTokenType, OAuthClientAuthenticationMethod, PkceCodeChallengeMethod}, }; -use parse_display::{Display, FromStr, ParseError}; use serde::{Deserialize, Serialize}; use serde_with::{ formats::SpaceSeparator, serde_as, skip_serializing_none, DeserializeFromStr, SerializeDisplay, @@ -39,14 +38,12 @@ use crate::{ /// An enum for types that accept either an [`OAuthClientAuthenticationMethod`] /// or an [`OAuthAccessTokenType`]. -#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug, Display)] +#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)] pub enum AuthenticationMethodOrAccessTokenType { /// An authentication method. - #[display("{0}")] AuthenticationMethod(OAuthClientAuthenticationMethod), /// An access token type. - #[display("{0}")] AccessTokenType(OAuthAccessTokenType), /// An unknown value. @@ -54,10 +51,37 @@ pub enum AuthenticationMethodOrAccessTokenType { /// Note that this variant should only be used as the result parsing a /// string of unknown type. To build a custom variant, first parse a /// string with the wanted type then use `.into()`. - #[display("{0}")] Unknown(String), } +impl core::fmt::Display for AuthenticationMethodOrAccessTokenType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::AuthenticationMethod(m) => m.fmt(f), + Self::AccessTokenType(t) => t.fmt(f), + Self::Unknown(s) => s.fmt(f), + } + } +} + +impl core::str::FromStr for AuthenticationMethodOrAccessTokenType { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match OAuthClientAuthenticationMethod::from_str(s) { + Ok(OAuthClientAuthenticationMethod::Unknown(_)) | Err(_) => {} + Ok(m) => return Ok(m.into()), + } + + match OAuthAccessTokenType::from_str(s) { + Ok(OAuthAccessTokenType::Unknown(_)) | Err(_) => {} + Ok(m) => return Ok(m.into()), + } + + Ok(Self::Unknown(s.to_owned())) + } +} + impl AuthenticationMethodOrAccessTokenType { /// Get the authentication method of this /// `AuthenticationMethodOrAccessTokenType`. @@ -80,28 +104,6 @@ impl AuthenticationMethodOrAccessTokenType { } } -impl FromStr for AuthenticationMethodOrAccessTokenType { - type Err = ParseError; - - fn from_str(s: &str) -> Result { - if let Ok(m) = OAuthClientAuthenticationMethod::from_str(s) { - match m { - OAuthClientAuthenticationMethod::Unknown(_) => {} - m => return Ok(m.into()), - } - } - - if let Ok(t) = OAuthAccessTokenType::from_str(s) { - match t { - OAuthAccessTokenType::Unknown(_) => {} - t => return Ok(t.into()), - } - } - - Ok(Self::Unknown(s.to_owned())) - } -} - impl From for AuthenticationMethodOrAccessTokenType { fn from(t: OAuthClientAuthenticationMethod) -> Self { Self::AuthenticationMethod(t) @@ -115,16 +117,38 @@ impl From for AuthenticationMethodOrAccessTokenType { } /// The kind of an application. -#[derive( - SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr, -)] -#[display(style = "lowercase")] +#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)] pub enum ApplicationType { /// A web application. Web, /// A native application. Native, + + /// An unknown value. + Unknown(String), +} + +impl core::fmt::Display for ApplicationType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Web => f.write_str("web"), + Self::Native => f.write_str("native"), + Self::Unknown(s) => f.write_str(s), + } + } +} + +impl core::str::FromStr for ApplicationType { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "web" => Ok(Self::Web), + "native" => Ok(Self::Native), + s => Ok(Self::Unknown(s.to_owned())), + } + } } /// Subject Identifier types. @@ -132,10 +156,7 @@ pub enum ApplicationType { /// A Subject Identifier is a locally unique and never reassigned identifier /// within the Issuer for the End-User, which is intended to be consumed by the /// Client. -#[derive( - SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr, -)] -#[display(style = "lowercase")] +#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)] pub enum SubjectType { /// This provides the same `sub` (subject) value to all Clients. Public, @@ -144,13 +165,35 @@ pub enum SubjectType { /// enable Clients to correlate the End-User's activities without /// permission. Pairwise, + + /// An unknown value. + Unknown(String), +} + +impl core::fmt::Display for SubjectType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Public => f.write_str("public"), + Self::Pairwise => f.write_str("pairwise"), + Self::Unknown(s) => f.write_str(s), + } + } +} + +impl core::str::FromStr for SubjectType { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "public" => Ok(Self::Public), + "pairwise" => Ok(Self::Pairwise), + s => Ok(Self::Unknown(s.to_owned())), + } + } } /// Claim types. -#[derive( - SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr, -)] -#[display(style = "lowercase")] +#[derive(SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug)] pub enum ClaimType { /// Claims that are directly asserted by the OpenID Provider. Normal, @@ -162,6 +205,33 @@ pub enum ClaimType { /// Claims that are asserted by a Claims Provider other than the OpenID /// Provider but are returned as references by the OpenID Provider. Distributed, + + /// An unknown value. + Unknown(String), +} + +impl core::fmt::Display for ClaimType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Normal => f.write_str("normal"), + Self::Aggregated => f.write_str("aggregated"), + Self::Distributed => f.write_str("distributed"), + Self::Unknown(s) => f.write_str(s), + } + } +} + +impl core::str::FromStr for ClaimType { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "normal" => Ok(Self::Normal), + "aggregated" => Ok(Self::Aggregated), + "distributed" => Ok(Self::Distributed), + s => Ok(Self::Unknown(s.to_owned())), + } + } } /// An account management action that a user can take. diff --git a/crates/oauth2-types/src/registration/mod.rs b/crates/oauth2-types/src/registration/mod.rs index 66f11d4f..18af0d85 100644 --- a/crates/oauth2-types/src/registration/mod.rs +++ b/crates/oauth2-types/src/registration/mod.rs @@ -613,7 +613,9 @@ impl ClientMetadata { /// Defaults to [`DEFAULT_APPLICATION_TYPE`]. #[must_use] pub fn application_type(&self) -> ApplicationType { - self.application_type.unwrap_or(DEFAULT_APPLICATION_TYPE) + self.application_type + .clone() + .unwrap_or(DEFAULT_APPLICATION_TYPE) } /// Requested client authentication method for the [token endpoint]. diff --git a/crates/oauth2-types/src/requests.rs b/crates/oauth2-types/src/requests.rs index 0558d805..1374c575 100644 --- a/crates/oauth2-types/src/requests.rs +++ b/crates/oauth2-types/src/requests.rs @@ -21,7 +21,6 @@ use std::{collections::HashSet, fmt, hash::Hash, num::NonZeroU32}; use chrono::{DateTime, Duration, Utc}; use language_tags::LanguageTag; use mas_iana::oauth::{OAuthAccessTokenType, OAuthTokenTypeHint}; -use parse_display::{Display, FromStr}; use serde::{Deserialize, Serialize}; use serde_with::{ formats::SpaceSeparator, serde_as, skip_serializing_none, DeserializeFromStr, DisplayFromStr, @@ -38,19 +37,8 @@ use crate::{response_type::ResponseType, scope::Scope}; /// /// Defined in [OAuth 2.0 Multiple Response Type Encoding Practices](https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes). #[derive( - Debug, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Clone, - Display, - FromStr, - SerializeDisplay, - DeserializeFromStr, + Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, SerializeDisplay, DeserializeFromStr, )] -#[display(style = "snake_case")] #[non_exhaustive] pub enum ResponseMode { /// Authorization Response parameters are encoded in the query string added @@ -71,28 +59,40 @@ pub enum ResponseMode { FormPost, /// An unknown value. - #[display("{0}")] Unknown(String), } +impl core::fmt::Display for ResponseMode { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ResponseMode::Query => f.write_str("query"), + ResponseMode::Fragment => f.write_str("fragment"), + ResponseMode::FormPost => f.write_str("form_post"), + ResponseMode::Unknown(s) => f.write_str(s), + } + } +} + +impl core::str::FromStr for ResponseMode { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "query" => Ok(ResponseMode::Query), + "fragment" => Ok(ResponseMode::Fragment), + "form_post" => Ok(ResponseMode::FormPost), + s => Ok(ResponseMode::Unknown(s.to_owned())), + } + } +} + /// Value that specifies how the Authorization Server displays the /// authentication and consent user interface pages to the End-User. /// /// Defined in [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). #[derive( - Debug, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Clone, - Display, - FromStr, - SerializeDisplay, - DeserializeFromStr, + Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, SerializeDisplay, DeserializeFromStr, )] -#[display(style = "snake_case")] #[non_exhaustive] pub enum Display { /// The Authorization Server should display the authentication and consent @@ -114,10 +114,35 @@ pub enum Display { Wap, /// An unknown value. - #[display("{0}")] Unknown(String), } +impl core::fmt::Display for Display { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Display::Page => f.write_str("page"), + Display::Popup => f.write_str("popup"), + Display::Touch => f.write_str("touch"), + Display::Wap => f.write_str("wap"), + Display::Unknown(s) => f.write_str(s), + } + } +} + +impl core::str::FromStr for Display { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "page" => Ok(Display::Page), + "popup" => Ok(Display::Popup), + "touch" => Ok(Display::Touch), + "wap" => Ok(Display::Wap), + s => Ok(Display::Unknown(s.to_owned())), + } + } +} + impl Default for Display { fn default() -> Self { Self::Page @@ -129,19 +154,8 @@ impl Default for Display { /// /// Defined in [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). #[derive( - Debug, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Clone, - Display, - FromStr, - SerializeDisplay, - DeserializeFromStr, + Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, SerializeDisplay, DeserializeFromStr, )] -#[display(style = "snake_case")] #[non_exhaustive] pub enum Prompt { /// The Authorization Server must not display any authentication or consent @@ -171,10 +185,37 @@ pub enum Prompt { Create, /// An unknown value. - #[display("{0}")] Unknown(String), } +impl core::fmt::Display for Prompt { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Prompt::None => f.write_str("none"), + Prompt::Login => f.write_str("login"), + Prompt::Consent => f.write_str("consent"), + Prompt::SelectAccount => f.write_str("select_account"), + Prompt::Create => f.write_str("create"), + Prompt::Unknown(s) => f.write_str(s), + } + } +} + +impl core::str::FromStr for Prompt { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "none" => Ok(Prompt::None), + "login" => Ok(Prompt::Login), + "consent" => Ok(Prompt::Consent), + "select_account" => Ok(Prompt::SelectAccount), + "create" => Ok(Prompt::Create), + s => Ok(Prompt::Unknown(s.to_owned())), + } + } +} + /// The body of a request to the [Authorization Endpoint]. /// /// [Authorization Endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1 @@ -511,20 +552,8 @@ impl fmt::Debug for DeviceCodeGrant { /// All possible values for the `grant_type` parameter. #[derive( - Debug, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Clone, - Copy, - Display, - FromStr, - SerializeDisplay, - DeserializeFromStr, + Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, SerializeDisplay, DeserializeFromStr, )] -#[display(style = "snake_case")] pub enum GrantType { /// [`authorization_code`](https://www.rfc-editor.org/rfc/rfc6749#section-4.1) AuthorizationCode, @@ -542,16 +571,54 @@ pub enum GrantType { Password, /// [`urn:ietf:params:oauth:grant-type:device_code`](https://www.rfc-editor.org/rfc/rfc8628) - #[display("urn:ietf:params:oauth:grant-type:device_code")] DeviceCode, /// [`https://datatracker.ietf.org/doc/html/rfc7523#section-2.1`](https://www.rfc-editor.org/rfc/rfc7523#section-2.1) - #[display("urn:ietf:params:oauth:grant-type:jwt-bearer")] JwtBearer, /// [`urn:openid:params:grant-type:ciba`](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) - #[display("urn:openid:params:grant-type:ciba")] ClientInitiatedBackchannelAuthentication, + + /// An unknown value. + Unknown(String), +} + +impl core::fmt::Display for GrantType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + GrantType::AuthorizationCode => f.write_str("authorization_code"), + GrantType::RefreshToken => f.write_str("refresh_token"), + GrantType::Implicit => f.write_str("implicit"), + GrantType::ClientCredentials => f.write_str("client_credentials"), + GrantType::Password => f.write_str("password"), + GrantType::DeviceCode => f.write_str("urn:ietf:params:oauth:grant-type:device_code"), + GrantType::JwtBearer => f.write_str("urn:ietf:params:oauth:grant-type:jwt-bearer"), + GrantType::ClientInitiatedBackchannelAuthentication => { + f.write_str("urn:openid:params:grant-type:ciba") + } + GrantType::Unknown(s) => f.write_str(s), + } + } +} + +impl core::str::FromStr for GrantType { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "authorization_code" => Ok(GrantType::AuthorizationCode), + "refresh_token" => Ok(GrantType::RefreshToken), + "implicit" => Ok(GrantType::Implicit), + "client_credentials" => Ok(GrantType::ClientCredentials), + "password" => Ok(GrantType::Password), + "urn:ietf:params:oauth:grant-type:device_code" => Ok(GrantType::DeviceCode), + "urn:ietf:params:oauth:grant-type:jwt-bearer" => Ok(GrantType::JwtBearer), + "urn:openid:params:grant-type:ciba" => { + Ok(GrantType::ClientInitiatedBackchannelAuthentication) + } + s => Ok(GrantType::Unknown(s.to_owned())), + } + } } /// An enum representing the possible requests to the [Token Endpoint]. diff --git a/crates/oauth2-types/src/response_type.rs b/crates/oauth2-types/src/response_type.rs index 1a7dc00a..5f8e8516 100644 --- a/crates/oauth2-types/src/response_type.rs +++ b/crates/oauth2-types/src/response_type.rs @@ -21,7 +21,6 @@ use std::{collections::BTreeSet, fmt, iter::FromIterator, str::FromStr}; use mas_iana::oauth::OAuthAuthorizationEndpointResponseType; -use parse_display::{Display, FromStr}; use serde_with::{DeserializeFromStr, SerializeDisplay}; use thiserror::Error; @@ -38,19 +37,8 @@ pub struct InvalidResponseType; /// This type also accepts unknown tokens that can be constructed via it's /// `FromStr` implementation or used via its `Display` implementation. #[derive( - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Display, - FromStr, - SerializeDisplay, - DeserializeFromStr, + Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, SerializeDisplay, DeserializeFromStr, )] -#[display(style = "snake_case")] #[non_exhaustive] pub enum ResponseTypeToken { /// `code` @@ -63,10 +51,33 @@ pub enum ResponseTypeToken { Token, /// Unknown token. - #[display("{0}")] Unknown(String), } +impl core::fmt::Display for ResponseTypeToken { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ResponseTypeToken::Code => f.write_str("code"), + ResponseTypeToken::IdToken => f.write_str("id_token"), + ResponseTypeToken::Token => f.write_str("token"), + ResponseTypeToken::Unknown(s) => f.write_str(s), + } + } +} + +impl core::str::FromStr for ResponseTypeToken { + type Err = core::convert::Infallible; + + fn from_str(s: &str) -> Result { + match s { + "code" => Ok(Self::Code), + "id_token" => Ok(Self::IdToken), + "token" => Ok(Self::Token), + s => Ok(Self::Unknown(s.to_owned())), + } + } +} + /// An [OAuth 2.0 `response_type` value] that the client can use /// at the [authorization endpoint]. /// diff --git a/crates/storage-pg/src/oauth2/client.rs b/crates/storage-pg/src/oauth2/client.rs index 28eff034..c0da7974 100644 --- a/crates/storage-pg/src/oauth2/client.rs +++ b/crates/storage-pg/src/oauth2/client.rs @@ -463,7 +463,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { "#, Uuid::from(id), encrypted_client_secret, - application_type.map(|a| a.to_string()), + application_type.as_ref().map(ToString::to_string), &redirect_uris_array, grant_types.contains(&GrantType::AuthorizationCode), grant_types.contains(&GrantType::RefreshToken),