diff --git a/warpgate-common/src/auth/policy.rs b/warpgate-common/src/auth/policy.rs index 9870bb8..b3ac40c 100644 --- a/warpgate-common/src/auth/policy.rs +++ b/warpgate-common/src/auth/policy.rs @@ -5,6 +5,7 @@ use crate::UserRequireCredentialsPolicy; pub enum CredentialPolicyResponse { Ok, + NeedMoreCredentials, Need(CredentialKind), } @@ -38,9 +39,16 @@ impl CredentialPolicy for UserRequireCredentialsPolicy { } if let Some(kind) = remaining_required_kinds.into_iter().next() { - return CredentialPolicyResponse::Need(kind); + CredentialPolicyResponse::Need(kind) + } else { + CredentialPolicyResponse::Ok + } + } else { + if valid_credentials.is_empty() { + CredentialPolicyResponse::NeedMoreCredentials + } else { + CredentialPolicyResponse::Ok } } - CredentialPolicyResponse::Ok } } diff --git a/warpgate-common/src/auth/state.rs b/warpgate-common/src/auth/state.rs index de4992b..2ee9ea4 100644 --- a/warpgate-common/src/auth/state.rs +++ b/warpgate-common/src/auth/state.rs @@ -1,7 +1,7 @@ use std::time::{Duration, Instant}; use once_cell::sync::Lazy; -use tracing::*; +use tracing::warn; use super::{AuthCredential, CredentialPolicy, CredentialPolicyResponse}; use crate::AuthResult; @@ -59,6 +59,9 @@ impl AuthState { CredentialPolicyResponse::Need(kind) => { return AuthResult::Need(kind); } + CredentialPolicyResponse::NeedMoreCredentials => { + return AuthResult::Rejected; + } } } AuthResult::Accepted { diff --git a/warpgate-common/src/config_providers/mod.rs b/warpgate-common/src/config_providers/mod.rs index 05133bf..3209d3f 100644 --- a/warpgate-common/src/config_providers/mod.rs +++ b/warpgate-common/src/config_providers/mod.rs @@ -16,6 +16,7 @@ use crate::{Secret, Target, UserSnapshot, WarpgateError}; pub enum AuthResult { Accepted { username: String }, Need(CredentialKind), + NeedMoreCredentials, Rejected, } diff --git a/warpgate-protocol-http/src/api/auth.rs b/warpgate-protocol-http/src/api/auth.rs index 130c919..9a9e019 100644 --- a/warpgate-protocol-http/src/api/auth.rs +++ b/warpgate-protocol-http/src/api/auth.rs @@ -75,6 +75,7 @@ impl From for ApiAuthState { AuthResult::Need(CredentialKind::Otp) => ApiAuthState::OtpNeeded, AuthResult::Need(CredentialKind::Sso) => ApiAuthState::SsoNeeded, AuthResult::Need(CredentialKind::PublicKey) => ApiAuthState::Failed, + AuthResult::NeedMoreCredentials => ApiAuthState::Failed, AuthResult::Accepted { .. } => ApiAuthState::Success, } } diff --git a/warpgate-protocol-mysql/src/session.rs b/warpgate-protocol-mysql/src/session.rs index 5ce1f59..54a4090 100644 --- a/warpgate-protocol-mysql/src/session.rs +++ b/warpgate-protocol-mysql/src/session.rs @@ -216,7 +216,9 @@ impl MySqlSession { } self.run_authorized(handshake, username, target_name).await } - AuthResult::Rejected | AuthResult::Need(_) => fail(&mut self).await, // TODO SSO + AuthResult::Rejected + | AuthResult::Need(_) + | AuthResult::NeedMoreCredentials => fail(&mut self).await, // TODO SSO } } AuthSelector::Ticket { secret } => { diff --git a/warpgate-protocol-ssh/src/server/session.rs b/warpgate-protocol-ssh/src/server/session.rs index 7845129..0a27f03 100644 --- a/warpgate-protocol-ssh/src/server/session.rs +++ b/warpgate-protocol-ssh/src/server/session.rs @@ -930,16 +930,18 @@ impl ServerSession { match self .try_auth( &selector, - AuthCredential::PublicKey { + Some(AuthCredential::PublicKey { kind: key.name().to_string(), public_key_bytes: Bytes::from(key.public_key_bytes()), - }, + }), ) .await { Ok(AuthResult::Accepted { .. }) => russh::server::Auth::Accept, Ok(AuthResult::Rejected) => russh::server::Auth::Reject, - Ok(AuthResult::Need(_)) => russh::server::Auth::Reject, + Ok(AuthResult::Need(_) | AuthResult::NeedMoreCredentials) => { + russh::server::Auth::Reject + } Err(error) => { error!(?error, "Failed to verify credentials"); russh::server::Auth::Reject @@ -956,12 +958,14 @@ impl ServerSession { info!("Password key auth as {:?}", selector); match self - .try_auth(&selector, AuthCredential::Password(password)) + .try_auth(&selector, Some(AuthCredential::Password(password))) .await { Ok(AuthResult::Accepted { .. }) => russh::server::Auth::Accept, Ok(AuthResult::Rejected) => russh::server::Auth::Reject, - Ok(AuthResult::Need(_)) => russh::server::Auth::Reject, + Ok(AuthResult::Need(_) | AuthResult::NeedMoreCredentials) => { + russh::server::Auth::Reject + } Err(error) => { error!(?error, "Failed to verify credentials"); russh::server::Auth::Reject @@ -977,14 +981,15 @@ impl ServerSession { let selector: AuthSelector = ssh_username.expose_secret().into(); info!("Keyboard-interactive auth as {:?}", selector); - let Some(otp) = response else { - return russh::server::Auth::Reject - }; + let cred = response.map(|otp| AuthCredential::Otp(otp)); - match self.try_auth(&selector, AuthCredential::Otp(otp)).await { + match self.try_auth(&selector, cred).await { Ok(AuthResult::Accepted { .. }) => russh::server::Auth::Accept, - Ok(AuthResult::Rejected) => russh::server::Auth::Reject, - Ok(AuthResult::Need(CredentialKind::Otp)) => russh::server::Auth::Partial { + Ok( + AuthResult::Rejected + | AuthResult::NeedMoreCredentials + | AuthResult::Need(CredentialKind::Otp), + ) => russh::server::Auth::Partial { name: Cow::Borrowed("Two-factor authentication"), instructions: Cow::Borrowed(""), prompts: Cow::Owned(vec![(Cow::Borrowed("One-time password: "), true)]), @@ -1000,7 +1005,7 @@ impl ServerSession { async fn try_auth( &mut self, selector: &AuthSelector, - credential: AuthCredential, + credential: Option, ) -> Result { match selector { AuthSelector::User { @@ -1009,13 +1014,16 @@ impl ServerSession { } => { let cp = self.services.config_provider.clone(); let state = self.get_auth_state(username).await?; - if cp - .lock() - .await - .validate_credential(username, &credential) - .await? - { - state.add_valid_credential(credential); + + if let Some(credential) = credential { + if cp + .lock() + .await + .validate_credential(username, &credential) + .await? + { + state.add_valid_credential(credential); + } } let user_auth_result = state.verify(); diff --git a/warpgate-web/src/gateway/Login.svelte b/warpgate-web/src/gateway/Login.svelte index e87403f..b7e57be 100644 --- a/warpgate-web/src/gateway/Login.svelte +++ b/warpgate-web/src/gateway/Login.svelte @@ -187,7 +187,7 @@ async function startSSO (provider: SsoProviderDescription) { {#if ssoProvider.kind === SsoProviderKind.Google} {/if} - {#if ssoProvider.kind === SsoProviderKind.Microsoft || ssoProvider.kind === SsoProviderKind.Azure} + {#if ssoProvider.kind === SsoProviderKind.Azure} {/if} {#if ssoProvider.kind === SsoProviderKind.Apple} diff --git a/warpgate-web/src/gateway/lib/openapi-schema.json b/warpgate-web/src/gateway/lib/openapi-schema.json index d12db63..32b25ef 100644 --- a/warpgate-web/src/gateway/lib/openapi-schema.json +++ b/warpgate-web/src/gateway/lib/openapi-schema.json @@ -338,7 +338,6 @@ "enum": [ "Google", "Apple", - "Microsoft", "Azure", "Custom" ]