diff options
author | Vika <vika@fireburn.ru> | 2022-09-19 19:01:06 +0300 |
---|---|---|
committer | Vika <vika@fireburn.ru> | 2022-09-19 19:01:06 +0300 |
commit | 53c868691a09b84d71f724d23a09d1fb89368792 (patch) | |
tree | e5593882f3c9d4141a2cb796d48dc98b84411d20 /kittybox-rs | |
parent | de105ec7a56752c152e3020fa53a0e13206f4cb4 (diff) | |
download | kittybox-53c868691a09b84d71f724d23a09d1fb89368792.tar.zst |
Make webauthn and openssl optional
Diffstat (limited to 'kittybox-rs')
-rw-r--r-- | kittybox-rs/Cargo.toml | 5 | ||||
-rw-r--r-- | kittybox-rs/src/indieauth/backend.rs | 12 | ||||
-rw-r--r-- | kittybox-rs/src/indieauth/backend/fs.rs | 10 | ||||
-rw-r--r-- | kittybox-rs/src/indieauth/mod.rs | 19 |
4 files changed, 37 insertions, 9 deletions
diff --git a/kittybox-rs/Cargo.toml b/kittybox-rs/Cargo.toml index c9b98a2..6b0057f 100644 --- a/kittybox-rs/Cargo.toml +++ b/kittybox-rs/Cargo.toml @@ -7,9 +7,10 @@ default-run = "kittybox" autobins = false [features] -default = ["openssl"] +default = ["rustls"] #util = ["anyhow"] #migration = ["util"] +webauthn = ["openssl", "dep:webauthn"] openssl = ["reqwest/native-tls-vendored", "reqwest/native-tls-alpn"] rustls = ["reqwest/rustls-tls-webpki-roots"] cli = ["clap"] @@ -85,7 +86,7 @@ tracing-tree = "0.2.1" tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] } tower-http = { version = "0.3.3", features = ["trace", "cors", "catch-panic"] } tower = { version = "0.4.12", features = ["tracing"] } -webauthn = { version = "0.4.5", package = "webauthn-rs", features = ["danger-allow-state-serialisation"] } +webauthn = { version = "0.4.5", package = "webauthn-rs", features = ["danger-allow-state-serialisation"], optional = true } [dependencies.tokio] version = "^1.16.1" features = ["full", "tracing"] # TODO determine if my app doesn't need some features diff --git a/kittybox-rs/src/indieauth/backend.rs b/kittybox-rs/src/indieauth/backend.rs index 8b0c10a..534bcfb 100644 --- a/kittybox-rs/src/indieauth/backend.rs +++ b/kittybox-rs/src/indieauth/backend.rs @@ -9,7 +9,6 @@ type Result<T> = std::io::Result<T>; pub mod fs; pub use fs::FileBackend; - #[async_trait::async_trait] pub trait AuthBackend: Clone + Send + Sync + 'static { // Authorization code management. @@ -44,7 +43,10 @@ pub trait AuthBackend: Clone + Send + Sync + 'static { /// Enroll a password credential for a user. Only one password /// credential must exist for a given user. async fn enroll_password(&self, website: &url::Url, password: String) -> Result<()>; + /// List currently enrolled credential types for a given user. + async fn list_user_credential_types(&self, website: &url::Url) -> Result<Vec<EnrolledCredential>>; // WebAuthn credential management. + #[cfg(feature = "webauthn")] /// Enroll a WebAuthn authenticator public key for this user. /// Multiple public keys may be saved for one user, corresponding /// to different authenticators used by them. @@ -53,8 +55,10 @@ pub trait AuthBackend: Clone + Send + Sync + 'static { /// updated version after using /// [webauthn::prelude::Passkey::update_credential()]. async fn enroll_webauthn(&self, website: &url::Url, credential: webauthn::prelude::Passkey) -> Result<()>; + #[cfg(feature = "webauthn")] /// List currently enrolled WebAuthn authenticators for a given user. async fn list_webauthn_pubkeys(&self, website: &url::Url) -> Result<Vec<webauthn::prelude::Passkey>>; + #[cfg(feature = "webauthn")] /// Persist registration challenge state for a little while so it /// can be used later. /// @@ -65,6 +69,7 @@ pub trait AuthBackend: Clone + Send + Sync + 'static { website: &url::Url, state: webauthn::prelude::PasskeyRegistration ) -> Result<String>; + #[cfg(feature = "webauthn")] /// Retrieve a persisted registration challenge. /// /// The challenge should be deleted after retrieval. @@ -73,6 +78,7 @@ pub trait AuthBackend: Clone + Send + Sync + 'static { website: &url::Url, challenge_id: &str ) -> Result<webauthn::prelude::PasskeyRegistration>; + #[cfg(feature = "webauthn")] /// Persist authentication challenge state for a little while so /// it can be used later. /// @@ -86,6 +92,7 @@ pub trait AuthBackend: Clone + Send + Sync + 'static { website: &url::Url, state: webauthn::prelude::PasskeyAuthentication ) -> Result<String>; + #[cfg(feature = "webauthn")] /// Retrieve a persisted authentication challenge. /// /// The challenge should be deleted after retrieval. @@ -94,6 +101,5 @@ pub trait AuthBackend: Clone + Send + Sync + 'static { website: &url::Url, challenge_id: &str ) -> Result<webauthn::prelude::PasskeyAuthentication>; - /// List currently enrolled credential types for a given user. - async fn list_user_credential_types(&self, website: &url::Url) -> Result<Vec<EnrolledCredential>>; + } diff --git a/kittybox-rs/src/indieauth/backend/fs.rs b/kittybox-rs/src/indieauth/backend/fs.rs index fbfa0f7..57bc3bd 100644 --- a/kittybox-rs/src/indieauth/backend/fs.rs +++ b/kittybox-rs/src/indieauth/backend/fs.rs @@ -7,6 +7,7 @@ use kittybox_indieauth::{ }; use serde::de::DeserializeOwned; use tokio::{task::spawn_blocking, io::AsyncReadExt}; +#[cfg(feature = "webauthn")] use webauthn::prelude::{Passkey, PasskeyRegistration, PasskeyAuthentication}; const CODE_LENGTH: usize = 16; @@ -343,15 +344,18 @@ impl AuthBackend for FileBackend { } // WebAuthn credential management. + #[cfg(feature = "webauthn")] async fn enroll_webauthn(&self, website: &url::Url, credential: Passkey) -> Result<()> { todo!() } + #[cfg(feature = "webauthn")] async fn list_webauthn_pubkeys(&self, website: &url::Url) -> Result<Vec<Passkey>> { // TODO stub! Ok(vec![]) } + #[cfg(feature = "webauthn")] async fn persist_registration_challenge( &self, website: &url::Url, @@ -360,6 +364,7 @@ impl AuthBackend for FileBackend { todo!() } + #[cfg(feature = "webauthn")] async fn retrieve_registration_challenge( &self, website: &url::Url, @@ -368,6 +373,7 @@ impl AuthBackend for FileBackend { todo!() } + #[cfg(feature = "webauthn")] async fn persist_authentication_challenge( &self, website: &url::Url, @@ -376,6 +382,7 @@ impl AuthBackend for FileBackend { todo!() } + #[cfg(feature = "webauthn")] async fn retrieve_authentication_challenge( &self, website: &url::Url, @@ -392,12 +399,13 @@ impl AuthBackend for FileBackend { .join("password")) .await { - Ok(metadata) => creds.push(EnrolledCredential::Password), + Ok(_) => creds.push(EnrolledCredential::Password), Err(err) => if err.kind() != std::io::ErrorKind::NotFound { return Err(err) } } + #[cfg(feature = "webauthn")] if !self.list_webauthn_pubkeys(website).await?.is_empty() { creds.push(EnrolledCredential::WebAuthn); } diff --git a/kittybox-rs/src/indieauth/mod.rs b/kittybox-rs/src/indieauth/mod.rs index adf669e..67f4a43 100644 --- a/kittybox-rs/src/indieauth/mod.rs +++ b/kittybox-rs/src/indieauth/mod.rs @@ -17,6 +17,7 @@ use kittybox_indieauth::{ }; pub mod backend; +#[cfg(feature = "webauthn")] mod webauthn; use backend::AuthBackend; @@ -111,6 +112,7 @@ async fn authorization_endpoint_get<A: AuthBackend, D: Storage + 'static>( #[serde(untagged)] enum Credential { Password(String), + #[cfg(feature = "webauthn")] WebAuthn(::webauthn::prelude::PublicKeyCredential) } @@ -128,6 +130,7 @@ async fn verify_credential<A: AuthBackend>( ) -> std::io::Result<bool> { match credential { Credential::Password(password) => auth.verify_password(website, password).await, + #[cfg(feature = "webauthn")] Credential::WebAuthn(credential) => webauthn::verify( auth, website, @@ -145,8 +148,12 @@ async fn authorization_endpoint_confirm<A: AuthBackend>( cookies: CookieJar, ) -> Response { tracing::debug!("Received authorization confirmation from user"); + #[cfg(feature = "webauthn")] let challenge_id = cookies.get(webauthn::CHALLENGE_ID_COOKIE) .map(|cookie| cookie.value()); + #[cfg(not(feature = "webauthn"))] + let challenge_id = None; + let website = format!("https://{}/", host).parse().unwrap(); let AuthorizationConfirmation { authorization_method: credential, @@ -195,6 +202,7 @@ async fn authorization_endpoint_confirm<A: AuthBackend>( // opaque response instead that is completely useless (StatusCode::NO_CONTENT, [("Location", location.as_str())], + #[cfg(feature = "webauthn")] cookies.remove(Cookie::named(webauthn::CHALLENGE_ID_COOKIE)) ) .into_response() @@ -309,7 +317,7 @@ async fn token_endpoint_post<A: AuthBackend, D: Storage + 'static>( .unwrap() .as_secs() .into() - } + } } #[inline] @@ -521,7 +529,7 @@ async fn token_endpoint_post<A: AuthBackend, D: Storage + 'static>( tracing::error!("Error revoking refresh token: {}", err); return StatusCode::INTERNAL_SERVER_ERROR.into_response(); } - + GrantResponse::AccessToken { me: data.me, profile, @@ -695,8 +703,13 @@ pub fn router<A: AuthBackend, D: Storage + 'static>(backend: A, db: D) -> axum:: .route( "/userinfo", get(userinfo_endpoint_get::<A, D>)) + .route("/webauthn/pre_register", - get(webauthn::webauthn_pre_register::<A, D>)) + get( + #[cfg(feature = "webauthn")] webauthn::webauthn_pre_register::<A, D>, + #[cfg(not(feature = "webauthn"))] || async { axum::http::StatusCode::NOT_FOUND } + ) + ) .layer(tower_http::cors::CorsLayer::new() .allow_methods([ axum::http::Method::GET, |