diff --git a/lib/crates/fabro-server/src/auth/translate.rs b/lib/crates/fabro-server/src/auth/translate.rs index b8891483f..416607a33 100644 --- a/lib/crates/fabro-server/src/auth/translate.rs +++ b/lib/crates/fabro-server/src/auth/translate.rs @@ -35,30 +35,26 @@ pub(crate) async fn auth_translation_middleware( mut req: Request, next: Next, ) -> Response { - let auth_mode = req + let translated = match req .extensions() .get::() .expect("AuthMode extension must be added to the router") - .clone(); - - let AuthMode::Enabled(config) = auth_mode else { - return next.run(req).await; + { + AuthMode::Disabled => return next.run(req).await, + AuthMode::Enabled(config) => { + if let Some(auth_header) = req.headers().get(header::AUTHORIZATION) { + auth_header + .to_str() + .ok() + .and_then(|value| value.strip_prefix("Bearer ")) + .and_then(|token| translate_bearer_token(token, config)) + } else { + translate_session_cookie(req.headers(), state.as_ref(), config) + } + } }; - if let Some(auth_header) = req.headers().get(header::AUTHORIZATION) { - if let Some(token) = auth_header - .to_str() - .ok() - .and_then(|value| value.strip_prefix("Bearer ")) - .and_then(|token| translate_bearer_token(token, &config)) - { - req.headers_mut() - .insert(header::AUTHORIZATION, bearer_header_value(&token)); - } - return next.run(req).await; - } - - if let Some(token) = translate_session_cookie(req.headers(), state.as_ref(), &config) { + if let Some(token) = translated { req.headers_mut() .insert(header::AUTHORIZATION, bearer_header_value(&token)); } diff --git a/lib/crates/fabro-server/src/jwt_auth.rs b/lib/crates/fabro-server/src/jwt_auth.rs index 77b312041..951fecb0d 100644 --- a/lib/crates/fabro-server/src/jwt_auth.rs +++ b/lib/crates/fabro-server/src/jwt_auth.rs @@ -1,5 +1,6 @@ use anyhow::{Result, anyhow}; use axum::extract::FromRequestParts; +use axum::http::header; use axum::http::request::Parts; use fabro_types::settings::{ServerAuthMethod, ServerSettings as ResolvedServerSettings}; use fabro_types::{IdpIdentity, RunAuthMethod}; @@ -14,21 +15,15 @@ use crate::error::ApiError; type HmacSha256 = Hmac; const DEV_TOKEN_COMPARE_KEY: &[u8] = b"fabro-dev-token-compare-key"; -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum CredentialSource { - JwtAccessToken, -} - #[derive(Clone, Debug, PartialEq, Eq)] pub struct VerifiedAuth { - pub login: String, - pub name: String, - pub email: String, - pub avatar_url: String, - pub user_url: String, - pub auth_method: RunAuthMethod, - pub credential_source: CredentialSource, - pub identity: Option, + pub login: String, + pub name: String, + pub email: String, + pub avatar_url: String, + pub user_url: String, + pub auth_method: RunAuthMethod, + pub identity: Option, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -97,7 +92,7 @@ where anyhow!("Fabro server refuses to start: auth is configured but SESSION_SECRET is not set.") })?; if web_enabled { - auth::derive_cookie_key(secret.as_bytes()).map_err(|err| cookie_key_error(&err))?; + auth::derive_cookie_key(secret.as_bytes()).map_err(|err| session_secret_key_error(&err))?; } let dev_token = if methods.contains(&ServerAuthMethod::DevToken) { @@ -116,7 +111,9 @@ where None }; - let jwt_key = Some(auth::derive_jwt_key(secret.as_bytes()).map_err(|err| jwt_key_error(&err))?); + let jwt_key = Some( + auth::derive_jwt_key(secret.as_bytes()).map_err(|err| session_secret_key_error(&err))?, + ); let jwt_issuer = Some(resolve_jwt_issuer(settings, &lookup)); Ok(AuthMode::Enabled(ConfiguredAuth { @@ -150,23 +147,7 @@ where .unwrap_or_else(|| "fabro-server".to_string()) } -fn cookie_key_error(err: &KeyDeriveError) -> anyhow::Error { - match err { - KeyDeriveError::Empty => { - anyhow!( - "Fabro server refuses to start: auth is configured but SESSION_SECRET is not set." - ) - } - KeyDeriveError::TooShort { - got_bytes, - min_bytes, - } => anyhow!( - "Fabro server refuses to start: SESSION_SECRET must be at least {min_bytes} bytes (64 hex characters) when auth is configured. Current length: {got_bytes} bytes." - ), - } -} - -fn jwt_key_error(err: &KeyDeriveError) -> anyhow::Error { +fn session_secret_key_error(err: &KeyDeriveError) -> anyhow::Error { match err { KeyDeriveError::Empty => { anyhow!( @@ -205,12 +186,12 @@ fn config_allows_run_auth_method(config: &ConfiguredAuth, method: RunAuthMethod) } fn bearer_token(parts: &Parts) -> Option> { - let header = parts.headers.get("authorization")?; - let Ok(header) = header.to_str() else { + let value = parts.headers.get(header::AUTHORIZATION)?; + let Ok(value) = value.to_str() else { return Some(Err(ApiError::unauthorized())); }; Some( - header + value .strip_prefix("Bearer ") .ok_or_else(ApiError::unauthorized), ) @@ -255,14 +236,13 @@ fn authenticate_jwt_bearer(token: &str, config: &ConfiguredAuth) -> Result Result, ApiError> { .map(Some) } -/// Axum extractor that enforces authentication on a route. pub struct AuthenticatedService; pub fn authenticate_service_parts(parts: &Parts) -> Result<(), ApiError> { @@ -333,7 +312,6 @@ impl FromRequestParts for AuthenticatedService { } } -/// Axum extractor that authenticates and extracts the request subject. pub struct AuthenticatedSubject { pub login: Option, pub name: String, @@ -903,7 +881,6 @@ client_id = "Iv1.test" let auth = authenticate_parts(&parts).unwrap().unwrap(); assert_eq!(auth.login, "octocat"); assert_eq!(auth.auth_method, RunAuthMethod::Github); - assert_eq!(auth.credential_source, CredentialSource::JwtAccessToken); assert_eq!( auth.identity, Some(IdpIdentity::new("https://github.com", "12345").unwrap())