From 8826d9446e6c492db2243b9921e59ce496027bef Mon Sep 17 00:00:00 2001 From: Vika Date: Wed, 9 Apr 2025 23:31:02 +0300 Subject: cargo fmt Change-Id: I80e81ebba3f0cdf8c094451c9fe3ee4126b8c888 --- indieauth/src/lib.rs | 252 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 158 insertions(+), 94 deletions(-) (limited to 'indieauth/src/lib.rs') diff --git a/indieauth/src/lib.rs b/indieauth/src/lib.rs index 459d943..1582318 100644 --- a/indieauth/src/lib.rs +++ b/indieauth/src/lib.rs @@ -20,13 +20,13 @@ //! [`axum`]: https://github.com/tokio-rs/axum use std::borrow::Cow; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use url::Url; mod scopes; pub use self::scopes::{Scope, Scopes}; mod pkce; -pub use self::pkce::{PKCEMethod, PKCEVerifier, PKCEChallenge}; +pub use self::pkce::{PKCEChallenge, PKCEMethod, PKCEVerifier}; // Re-export rand crate just to be sure. pub use rand; @@ -48,7 +48,7 @@ pub enum IntrospectionEndpointAuthMethod { /// TLS client auth with a certificate signed by a valid CA. TlsClientAuth, /// TLS client auth with a self-signed certificate. - SelfSignedTlsClientAuth + SelfSignedTlsClientAuth, } /// Authentication methods supported by the revocation endpoint. @@ -64,7 +64,7 @@ pub enum IntrospectionEndpointAuthMethod { pub enum RevocationEndpointAuthMethod { /// No authentication is required to access an endpoint declaring /// this value. - None + None, } /// The response types supported by the authorization endpoint. @@ -80,7 +80,7 @@ pub enum ResponseType { /// This response type requires a valid access token. /// /// [AutoAuth spec]: https://github.com/sknebel/AutoAuth/blob/master/AutoAuth.md#allowing-external-clients-to-obtain-tokens - ExternalToken + ExternalToken, } // TODO serde_variant impl ResponseType { @@ -108,7 +108,7 @@ pub enum GrantType { /// The refresh token grant, allowing to exchange a refresh token /// for a fresh access token and a new refresh token, to /// facilitate long-term access. - RefreshToken + RefreshToken, } /// OAuth 2.0 Authorization Server Metadata in application to the IndieAuth protocol. @@ -220,7 +220,7 @@ pub struct Metadata { /// registration. #[serde(skip_serializing_if = "ref_identity")] #[serde(default = "Default::default")] - pub client_id_metadata_document_supported: bool + pub client_id_metadata_document_supported: bool, } impl std::fmt::Debug for Metadata { @@ -230,31 +230,59 @@ impl std::fmt::Debug for Metadata { .field("authorization_endpoint", &self.issuer.as_str()) .field("token_endpoint", &self.issuer.as_str()) .field("introspection_endpoint", &self.issuer.as_str()) - .field("introspection_endpoint_auth_methods_supported", &self.introspection_endpoint_auth_methods_supported) - .field("revocation_endpoint", &self.revocation_endpoint.as_ref().map(Url::as_str)) - .field("revocation_endpoint_auth_methods_supported", &self.revocation_endpoint_auth_methods_supported) + .field( + "introspection_endpoint_auth_methods_supported", + &self.introspection_endpoint_auth_methods_supported, + ) + .field( + "revocation_endpoint", + &self.revocation_endpoint.as_ref().map(Url::as_str), + ) + .field( + "revocation_endpoint_auth_methods_supported", + &self.revocation_endpoint_auth_methods_supported, + ) .field("scopes_supported", &self.scopes_supported) .field("response_types_supported", &self.response_types_supported) .field("grant_types_supported", &self.grant_types_supported) - .field("service_documentation", &self.service_documentation.as_ref().map(Url::as_str)) - .field("code_challenge_methods_supported", &self.code_challenge_methods_supported) - .field("authorization_response_iss_parameter_supported", &self.authorization_response_iss_parameter_supported) - .field("userinfo_endpoint", &self.userinfo_endpoint.as_ref().map(Url::as_str)) - .field("client_id_metadata_document_supported", &self.client_id_metadata_document_supported) + .field( + "service_documentation", + &self.service_documentation.as_ref().map(Url::as_str), + ) + .field( + "code_challenge_methods_supported", + &self.code_challenge_methods_supported, + ) + .field( + "authorization_response_iss_parameter_supported", + &self.authorization_response_iss_parameter_supported, + ) + .field( + "userinfo_endpoint", + &self.userinfo_endpoint.as_ref().map(Url::as_str), + ) + .field( + "client_id_metadata_document_supported", + &self.client_id_metadata_document_supported, + ) .finish() } } -fn ref_identity(v: &bool) -> bool { *v } +fn ref_identity(v: &bool) -> bool { + *v +} #[cfg(feature = "axum")] impl axum_core::response::IntoResponse for Metadata { fn into_response(self) -> axum_core::response::Response { use http::StatusCode; - (StatusCode::OK, - [("Content-Type", "application/json")], - serde_json::to_vec(&self).unwrap()) + ( + StatusCode::OK, + [("Content-Type", "application/json")], + serde_json::to_vec(&self).unwrap(), + ) .into_response() } } @@ -306,7 +334,7 @@ pub struct ClientMetadata { pub software_version: Option>, /// URI for the homepage of this client's owners #[serde(skip_serializing_if = "Option::is_none")] - pub homepage_uri: Option + pub homepage_uri: Option, } /// Error that occurs when creating [`ClientMetadata`] with mismatched `client_id` and `client_uri`. @@ -328,12 +356,15 @@ impl ClientMetadata { /// Returns `()` if the `client_uri` is not a prefix of `client_id` as required by the IndieAuth /// spec. pub fn new(client_id: url::Url, client_uri: url::Url) -> Result { - if client_id.as_str().as_bytes()[..client_uri.as_str().len()] != *client_uri.as_str().as_bytes() { + if client_id.as_str().as_bytes()[..client_uri.as_str().len()] + != *client_uri.as_str().as_bytes() + { return Err(ClientIdMismatch); } Ok(Self { - client_id, client_uri, + client_id, + client_uri, client_name: None, logo_uri: None, redirect_uris: None, @@ -363,14 +394,15 @@ impl axum_core::response::IntoResponse for ClientMetadata { fn into_response(self) -> axum_core::response::Response { use http::StatusCode; - (StatusCode::OK, - [("Content-Type", "application/json")], - serde_json::to_vec(&self).unwrap()) + ( + StatusCode::OK, + [("Content-Type", "application/json")], + serde_json::to_vec(&self).unwrap(), + ) .into_response() } } - /// User profile to be returned from the userinfo endpoint and when /// the `profile` scope was requested. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -387,7 +419,7 @@ pub struct Profile { /// User's email, if they've chosen to reveal it. This is guarded /// by the `email` scope. #[serde(skip_serializing_if = "Option::is_none")] - pub email: Option + pub email: Option, } #[cfg(feature = "axum")] @@ -395,9 +427,11 @@ impl axum_core::response::IntoResponse for Profile { fn into_response(self) -> axum_core::response::Response { use http::StatusCode; - (StatusCode::OK, - [("Content-Type", "application/json")], - serde_json::to_vec(&self).unwrap()) + ( + StatusCode::OK, + [("Content-Type", "application/json")], + serde_json::to_vec(&self).unwrap(), + ) .into_response() } } @@ -422,13 +456,13 @@ impl State { /// Generate a random state string of 128 bytes in length, using /// the provided random number generator. pub fn from_rng(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self { - use rand::{Rng, distributions::Alphanumeric}; + use rand::{distributions::Alphanumeric, Rng}; - let bytes = rng.sample_iter(&Alphanumeric) + let bytes = rng + .sample_iter(&Alphanumeric) .take(128) .collect::>(); Self(String::from_utf8(bytes).unwrap()) - } } impl AsRef for State { @@ -511,21 +545,23 @@ impl AuthorizationRequest { ("response_type", Cow::Borrowed(self.response_type.as_str())), ("client_id", Cow::Borrowed(self.client_id.as_str())), ("redirect_uri", Cow::Borrowed(self.redirect_uri.as_str())), - ("code_challenge", Cow::Borrowed(self.code_challenge.as_str())), - ("code_challenge_method", Cow::Borrowed(self.code_challenge.method().as_str())), - ("state", Cow::Borrowed(self.state.as_ref())) + ( + "code_challenge", + Cow::Borrowed(self.code_challenge.as_str()), + ), + ( + "code_challenge_method", + Cow::Borrowed(self.code_challenge.method().as_str()), + ), + ("state", Cow::Borrowed(self.state.as_ref())), ]; if let Some(ref scope) = self.scope { - v.push( - ("scope", Cow::Owned(scope.to_string())) - ); + v.push(("scope", Cow::Owned(scope.to_string()))); } if let Some(ref me) = self.me { - v.push( - ("me", Cow::Borrowed(me.as_str())) - ); + v.push(("me", Cow::Borrowed(me.as_str()))); } v @@ -558,17 +594,22 @@ pub struct AutoAuthRequest { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AutoAuthCallbackData { state: State, - callback_url: Url + callback_url: Url, } #[inline(always)] -fn deserialize_secs<'de, D: serde::de::Deserializer<'de>>(d: D) -> Result { +fn deserialize_secs<'de, D: serde::de::Deserializer<'de>>( + d: D, +) -> Result { use serde::Deserialize; Ok(std::time::Duration::from_secs(u64::deserialize(d)?)) } #[inline(always)] -fn serialize_secs(d: &std::time::Duration, s: S) -> Result { +fn serialize_secs( + d: &std::time::Duration, + s: S, +) -> Result { s.serialize_u64(std::time::Duration::as_secs(d)) } @@ -578,7 +619,7 @@ pub struct AutoAuthPollingResponse { request_id: State, #[serde(serialize_with = "serialize_secs")] #[serde(deserialize_with = "deserialize_secs")] - interval: std::time::Duration + interval: std::time::Duration, } /// The authorization response that must be appended to the @@ -610,10 +651,9 @@ pub struct AuthorizationResponse { /// authorization server. /// /// [oauth2-iss]: https://www.ietf.org/archive/id/draft-ietf-oauth-iss-auth-resp-02.html - pub iss: Url + pub iss: Url, } - /// A special grant request that is used in the AutoAuth ceremony. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AutoAuthCodeGrant { @@ -636,7 +676,7 @@ pub struct AutoAuthCodeGrant { callback_url: Url, /// The user's URL. Will be used to confirm the authorization /// endpoint's authority. - me: Url + me: Url, } /// A grant request that continues the IndieAuth ceremony. @@ -655,7 +695,7 @@ pub enum GrantRequest { redirect_uri: Url, /// The PKCE code verifier that was used to create the code /// challenge. - code_verifier: PKCEVerifier + code_verifier: PKCEVerifier, }, /// Use a refresh token to get a fresh access token and a new /// matching refresh token. @@ -670,8 +710,8 @@ pub enum GrantRequest { /// /// This cannot be used to gain new scopes -- you need to /// start over if you need new scopes from the user. - scope: Option - } + scope: Option, + }, } /// Token type, as described in [RFC6749][]. @@ -685,7 +725,7 @@ pub enum TokenType { /// IndieAuth uses. /// /// [RFC6750]: https://www.rfc-editor.org/rfc/rfc6750 - Bearer + Bearer, } /// The response to a successful [`GrantRequest`]. @@ -722,14 +762,14 @@ pub enum GrantResponse { profile: Option, /// The refresh token, if it was issued. #[serde(skip_serializing_if = "Option::is_none")] - refresh_token: Option + refresh_token: Option, }, /// A profile URL response, that only contains the profile URL and /// the profile, if it was requested. /// /// This is suitable for confirming the identity of the user, but /// no more than that. - ProfileUrl(ProfileUrl) + ProfileUrl(ProfileUrl), } /// The contents of a profile URL response. @@ -739,7 +779,7 @@ pub struct ProfileUrl { pub me: Url, /// The user's profile information, if it was requested. #[serde(skip_serializing_if = "Option::is_none")] - pub profile: Option + pub profile: Option, } #[cfg(feature = "axum")] @@ -747,12 +787,15 @@ impl axum_core::response::IntoResponse for GrantResponse { fn into_response(self) -> axum_core::response::Response { use http::StatusCode; - (StatusCode::OK, - [("Content-Type", "application/json"), - ("Cache-Control", "no-store"), - ("Pragma", "no-cache") - ], - serde_json::to_vec(&self).unwrap()) + ( + StatusCode::OK, + [ + ("Content-Type", "application/json"), + ("Cache-Control", "no-store"), + ("Pragma", "no-cache"), + ], + serde_json::to_vec(&self).unwrap(), + ) .into_response() } } @@ -766,7 +809,7 @@ impl axum_core::response::IntoResponse for GrantResponse { pub enum RequestMaybeAuthorizationEndpoint { Authorization(AuthorizationRequest), Grant(GrantRequest), - AutoAuth(AutoAuthCodeGrant) + AutoAuth(AutoAuthCodeGrant), } /// A token introspection request that can be handled by the token @@ -778,7 +821,7 @@ pub enum RequestMaybeAuthorizationEndpoint { #[derive(Debug, Serialize, Deserialize)] pub struct TokenIntrospectionRequest { /// The token for which data was requested. - pub token: String + pub token: String, } /// Data for a token that will be returned by the introspection @@ -800,7 +843,7 @@ pub struct TokenData { /// The issue date, represented in the same format as the /// [`exp`][TokenData::exp] field. #[serde(skip_serializing_if = "Option::is_none")] - pub iat: Option + pub iat: Option, } impl TokenData { @@ -809,24 +852,25 @@ impl TokenData { use std::time::{Duration, SystemTime, UNIX_EPOCH}; self.exp - .map(|exp| SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or(Duration::ZERO) - .as_secs() >= exp) + .map(|exp| { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or(Duration::ZERO) + .as_secs() + >= exp + }) .unwrap_or_default() } /// Return a timestamp at which the token is not considered valid anymore. pub fn expires_at(&self) -> Option { - self.exp.map(|time| { - std::time::UNIX_EPOCH + std::time::Duration::from_secs(time) - }) + self.exp + .map(|time| std::time::UNIX_EPOCH + std::time::Duration::from_secs(time)) } /// Return a timestamp describing when the token was issued. pub fn issued_at(&self) -> Option { - self.iat.map(|time| { - std::time::UNIX_EPOCH + std::time::Duration::from_secs(time) - }) + self.iat + .map(|time| std::time::UNIX_EPOCH + std::time::Duration::from_secs(time)) } /// Check if a certain scope is allowed for this token. @@ -849,18 +893,24 @@ pub struct TokenIntrospectionResponse { active: bool, #[serde(flatten)] #[serde(skip_serializing_if = "Option::is_none")] - data: Option + data: Option, } // These wrappers and impls should take care of making use of this // type as painless as possible. impl TokenIntrospectionResponse { /// Indicate that this token is not valid. pub fn inactive() -> Self { - Self { active: false, data: None } + Self { + active: false, + data: None, + } } /// Indicate that this token is valid, and provide data about it. pub fn active(data: TokenData) -> Self { - Self { active: true, data: Some(data) } + Self { + active: true, + data: Some(data), + } } /// Check if the endpoint reports this token as valid. pub fn is_active(&self) -> bool { @@ -870,7 +920,7 @@ impl TokenIntrospectionResponse { /// Get data contained in the response, if the token is valid. pub fn data(&self) -> Option<&TokenData> { if !self.active { - return None + return None; } self.data.as_ref() } @@ -882,7 +932,10 @@ impl Default for TokenIntrospectionResponse { } impl From> for TokenIntrospectionResponse { fn from(data: Option) -> Self { - Self { active: data.is_some(), data } + Self { + active: data.is_some(), + data, + } } } impl From for Option { @@ -896,9 +949,11 @@ impl axum_core::response::IntoResponse for TokenIntrospectionResponse { fn into_response(self) -> axum_core::response::Response { use http::StatusCode; - (StatusCode::OK, - [("Content-Type", "application/json")], - serde_json::to_vec(&self).unwrap()) + ( + StatusCode::OK, + [("Content-Type", "application/json")], + serde_json::to_vec(&self).unwrap(), + ) .into_response() } } @@ -908,7 +963,7 @@ impl axum_core::response::IntoResponse for TokenIntrospectionResponse { #[derive(Debug, Serialize, Deserialize)] pub struct TokenRevocationRequest { /// The token that needs to be revoked in case it is valid. - pub token: String + pub token: String, } /// Types of errors that a resource server (IndieAuth consumer) can @@ -969,7 +1024,6 @@ pub enum ErrorKind { /// AutoAuth/OAuth2 Device Flow: Access was denied by the /// authorization endpoint. AccessDenied, - } // TODO consider relying on serde_variant for these conversions impl AsRef for ErrorKind { @@ -1005,13 +1059,15 @@ pub struct Error { pub msg: Option, /// An URL to documentation describing what went wrong and how to /// fix it. - pub error_uri: Option + pub error_uri: Option, } impl From for Error { fn from(kind: ErrorKind) -> Error { Error { - kind, msg: None, error_uri: None + kind, + msg: None, + error_uri: None, } } } @@ -1037,9 +1093,11 @@ impl axum_core::response::IntoResponse for self::Error { fn into_response(self) -> axum_core::response::Response { use http::StatusCode; - (StatusCode::BAD_REQUEST, - [("Content-Type", "application/json")], - serde_json::to_vec(&self).unwrap()) + ( + StatusCode::BAD_REQUEST, + [("Content-Type", "application/json")], + serde_json::to_vec(&self).unwrap(), + ) .into_response() } } @@ -1052,17 +1110,23 @@ mod tests { fn test_serialize_deserialize_grant_request() { let authorization_code: GrantRequest = GrantRequest::AuthorizationCode { client_id: "https://kittybox.fireburn.ru/".parse().unwrap(), - redirect_uri: "https://kittybox.fireburn.ru/.kittybox/login/redirect".parse().unwrap(), + redirect_uri: "https://kittybox.fireburn.ru/.kittybox/login/redirect" + .parse() + .unwrap(), code_verifier: PKCEVerifier("helloworld".to_string()), - code: "hithere".to_owned() + code: "hithere".to_owned(), }; let serialized = serde_urlencoded::to_string([ ("grant_type", "authorization_code"), ("code", "hithere"), ("client_id", "https://kittybox.fireburn.ru/"), - ("redirect_uri", "https://kittybox.fireburn.ru/.kittybox/login/redirect"), + ( + "redirect_uri", + "https://kittybox.fireburn.ru/.kittybox/login/redirect", + ), ("code_verifier", "helloworld"), - ]).unwrap(); + ]) + .unwrap(); let deserialized = serde_urlencoded::from_str(&serialized).unwrap(); -- cgit 1.4.1