mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2026-07-10 18:24:43 -06:00
feat(config): Add passkey_login_allowed config option
This commit is contained in:
parent
007ac4f992
commit
8cba57a1af
@ -316,6 +316,9 @@
|
||||
## unauthenticated access to potentially sensitive data.
|
||||
# SHOW_PASSWORD_HINT=false
|
||||
|
||||
## Controls whether users can setup and use passkeys to log in, which also bypasses 2FA. This setting applies globally to all users.
|
||||
# PASSKEY_LOGIN_ALLOWED=true
|
||||
|
||||
#########################
|
||||
### Advanced settings ###
|
||||
#########################
|
||||
|
||||
@ -4,6 +4,7 @@ use webauthn_rs::prelude::{Passkey, PasskeyRegistration};
|
||||
use webauthn_rs_proto::{COSEAlgorithm, UserVerificationPolicy, options::PubKeyCredParams};
|
||||
|
||||
use crate::{
|
||||
CONFIG,
|
||||
api::{
|
||||
ApiResult, JsonResult, PasswordOrOtpData,
|
||||
core::two_factor::webauthn::{RegisterPublicKeyCredentialCopy, WEBAUTHN},
|
||||
@ -20,7 +21,16 @@ pub fn routes() -> Vec<rocket::Route> {
|
||||
}
|
||||
|
||||
#[get("/webauthn")]
|
||||
async fn get_webauthn(headers: Headers, conn: DbConn) -> Json<Value> {
|
||||
async fn get_webauthn(headers: Headers, conn: DbConn) -> JsonResult {
|
||||
if !CONFIG.passkey_login_allowed() {
|
||||
// The web vault will query this endpoint weather passkey login is allowed, so we should return an empty list instead of an error.
|
||||
return Ok(Json(json!({
|
||||
"object": "list",
|
||||
"data": [],
|
||||
"continuationToken": null
|
||||
})))
|
||||
}
|
||||
|
||||
let user = headers.user;
|
||||
|
||||
let data: Vec<WebauthnCredential> = WebauthnCredential::find_all_by_user(&user.uuid, &conn).await;
|
||||
@ -38,11 +48,11 @@ async fn get_webauthn(headers: Headers, conn: DbConn) -> Json<Value> {
|
||||
})
|
||||
.collect::<Value>();
|
||||
|
||||
Json(json!({
|
||||
Ok(Json(json!({
|
||||
"object": "list",
|
||||
"data": data,
|
||||
"continuationToken": null
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
#[post("/webauthn/attestation-options", data = "<data>")]
|
||||
@ -51,6 +61,10 @@ async fn post_webauthn_attestation_options(
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
if !CONFIG.passkey_login_allowed() {
|
||||
err!("Passkey login is not allowed")
|
||||
}
|
||||
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
@ -165,6 +179,10 @@ async fn post_webauthn(
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> ApiResult<Status> {
|
||||
if !CONFIG.passkey_login_allowed() {
|
||||
err!("Passkey login is not allowed")
|
||||
}
|
||||
|
||||
let data: WebAuthnLoginCredentialCreateRequest = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
@ -201,6 +219,10 @@ async fn post_webauthn_delete(
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> ApiResult<Status> {
|
||||
if !CONFIG.passkey_login_allowed() {
|
||||
err!("Passkey login is not allowed")
|
||||
}
|
||||
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
|
||||
@ -114,7 +114,7 @@ async fn login(
|
||||
sso_login(data, &mut user_id, &conn, &client_header.ip, client_version.as_ref()).await
|
||||
}
|
||||
"authorization_code" => err!("SSO sign-in is not available"),
|
||||
"webauthn" => {
|
||||
"webauthn" if CONFIG.passkey_login_allowed() => {
|
||||
check_is_some(data.client_id.as_ref(), "client_id cannot be blank")?;
|
||||
check_is_some(data.scope.as_ref(), "scope cannot be blank")?;
|
||||
|
||||
@ -127,6 +127,7 @@ async fn login(
|
||||
|
||||
webauthn_login(data, &mut user_id, &conn, &client_header.ip).await
|
||||
}
|
||||
"webauthn" => err!("Passkey login is not allowed"),
|
||||
t => err!("Invalid type", t),
|
||||
};
|
||||
|
||||
@ -1490,6 +1491,10 @@ async fn authorize(data: AuthorizeData, cookies: &CookieJar<'_>, secure: Secure,
|
||||
|
||||
#[get("/accounts/webauthn/assertion-options")]
|
||||
fn get_webauthn_assertion_options() -> JsonResult {
|
||||
if !CONFIG.passkey_login_allowed() {
|
||||
err!("Passkey login is not allowed")
|
||||
}
|
||||
|
||||
let (mut response, state) = WEBAUTHN.start_passkey_authentication(&[])?;
|
||||
|
||||
// Allow any credential (discoverable) and require user verification
|
||||
|
||||
@ -78,6 +78,7 @@ fn vaultwarden_css() -> Cached<Css<String>> {
|
||||
"sso_only": CONFIG.sso_enabled() && CONFIG.sso_only(),
|
||||
"webauthn_2fa_supported": CONFIG.is_webauthn_2fa_supported(),
|
||||
"yubico_enabled": CONFIG._enable_yubico() && CONFIG.yubico_client_id().is_some() && CONFIG.yubico_secret_key().is_some(),
|
||||
"passkey_login_allowed": CONFIG.passkey_login_allowed(),
|
||||
});
|
||||
|
||||
let scss = match CONFIG.render_template("scss/vaultwarden.scss", &css_options) {
|
||||
|
||||
@ -649,6 +649,9 @@ make_config! {
|
||||
/// because this provides unauthenticated access to potentially sensitive data.
|
||||
show_password_hint: bool, true, def, false;
|
||||
|
||||
/// Allow passkey login |> Controls whether users can setup and use passkeys to log in, which also bypasses 2FA. This setting applies globally to all users.
|
||||
passkey_login_allowed: bool, true, def, true;
|
||||
|
||||
/// Admin token/Argon2 PHC |> The plain text token or Argon2 PHC string used to authenticate in this very same page. Changing it here will not deauthorize the current session!
|
||||
admin_token: Pass, true, option;
|
||||
|
||||
|
||||
@ -54,6 +54,23 @@ app-root ng-component > form > div:nth-child(1) > div > button[buttontype="secon
|
||||
}
|
||||
{{/if}}
|
||||
|
||||
{{#if (not passkey_login_allowed)}}
|
||||
/* Hide the `Log in with passkey` settings */
|
||||
app-user-layout app-password-settings app-webauthn-login-settings {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
/* Hide Log in with passkey on the login page */
|
||||
{{#if (webver ">=2025.5.1")}}
|
||||
.vw-passkey-login {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
{{else}}
|
||||
app-root ng-component > form > div:nth-child(1) > div > button[buttontype="secondary"].\!tw-text-primary-600:nth-child(3) {
|
||||
@extend %vw-hide;
|
||||
}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
/* Hide the or text followed by the two buttons hidden above */
|
||||
{{#if (webver ">=2025.5.1")}}
|
||||
{{#if (or (not sso_enabled) sso_only)}}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user