mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2026-03-22 01:39:21 -07:00
SSO using OpenID Connect (#3899)
* Add SSO functionality using OpenID Connect Co-authored-by: Pablo Ovelleiro Corral <mail@pablo.tools> Co-authored-by: Stuart Heap <sheap13@gmail.com> Co-authored-by: Alex Moore <skiepp@my-dockerfarm.cloud> Co-authored-by: Brian Munro <brian.alexander.munro@gmail.com> Co-authored-by: Jacques B. <timshel@github.com> * Improvements and error handling * Stop rolling device token * Add playwright tests * Activate PKCE by default * Ensure result order when searching for sso_user * add SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION * Toggle SSO button in scss * Base64 encode state before sending it to providers * Prevent disabled User from SSO login * Review fixes * Remove unused UserOrganization.invited_by_email * Split SsoUser::find_by_identifier_or_email * api::Accounts::verify_password add the policy even if it's ignored * Disable signups if SSO_ONLY is activated * Add verifiedDate to organizations::get_org_domain_sso_details * Review fixes * Remove OrganizationId guard from get_master_password_policy * Add wrapper type OIDCCode OIDCState OIDCIdentifier * Membership::confirm_user_invitations fix and tests * Allow set-password only if account is unitialized * Review fixes * Prevent accepting another user invitation * Log password change event on SSO account creation * Unify master password policy resolution * Upgrade openidconnect to 4.0.0 * Revert "Remove unused UserOrganization.invited_by_email" This reverts commit 548e19995e141314af98a10d170ea7371f02fab4. * Process org enrollment in accounts::post_set_password * Improve tests * Pass the claim invited_by_email in case it was not in db * Add Slack configuration hints * Fix playwright tests * Skip broken tests * Add sso identifier in admin user panel * Remove duplicate expiration check, add a log * Augment mobile refresh_token validity * Rauthy configuration hints * Fix playwright tests * Playwright upgrade and conf improvement * Playwright tests improvements * 2FA email and device creation change * Fix and improve Playwright tests * Minor improvements * Fix enforceOnLogin org policies * Run playwright sso tests against correct db * PKCE should now work with Zitadel * Playwright upgrade maildev to use MailBuffer.expect * Upgrades playwright tests deps * Check email_verified in id_token and user_info * Add sso verified endpoint for v2025.6.0 * Fix playwright tests * Create a separate sso_client * Upgrade openidconnect to 4.0.1 * Server settings for login fields toggle * Use only css for login fields * Fix playwright test * Review fix * More review fix * Perform same checks when setting kdf --------- Co-authored-by: Felix Eckhofer <felix@eckhofer.com> Co-authored-by: Pablo Ovelleiro Corral <mail@pablo.tools> Co-authored-by: Stuart Heap <sheap13@gmail.com> Co-authored-by: Alex Moore <skiepp@my-dockerfarm.cloud> Co-authored-by: Brian Munro <brian.alexander.munro@gmail.com> Co-authored-by: Jacques B. <timshel@github.com> Co-authored-by: Timshel <timshel@480s>
This commit is contained in:
@@ -46,6 +46,7 @@ pub fn routes() -> Vec<Route> {
|
||||
invite_user,
|
||||
logout,
|
||||
delete_user,
|
||||
delete_sso_user,
|
||||
deauth_user,
|
||||
disable_user,
|
||||
enable_user,
|
||||
@@ -239,6 +240,7 @@ struct AdminTemplateData {
|
||||
page_data: Option<Value>,
|
||||
logged_in: bool,
|
||||
urlpath: String,
|
||||
sso_enabled: bool,
|
||||
}
|
||||
|
||||
impl AdminTemplateData {
|
||||
@@ -248,6 +250,7 @@ impl AdminTemplateData {
|
||||
page_data: Some(page_data),
|
||||
logged_in: true,
|
||||
urlpath: CONFIG.domain_path(),
|
||||
sso_enabled: CONFIG.sso_enabled(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +299,7 @@ async fn invite_user(data: Json<InviteData>, _token: AdminToken, mut conn: DbCon
|
||||
err_code!("User already exists", Status::Conflict.code)
|
||||
}
|
||||
|
||||
let mut user = User::new(data.email);
|
||||
let mut user = User::new(data.email, None);
|
||||
|
||||
async fn _generate_invite(user: &User, conn: &mut DbConn) -> EmptyResult {
|
||||
if CONFIG.mail_enabled() {
|
||||
@@ -336,7 +339,7 @@ fn logout(cookies: &CookieJar<'_>) -> Redirect {
|
||||
async fn get_users_json(_token: AdminToken, mut conn: DbConn) -> Json<Value> {
|
||||
let users = User::get_all(&mut conn).await;
|
||||
let mut users_json = Vec::with_capacity(users.len());
|
||||
for u in users {
|
||||
for (u, _) in users {
|
||||
let mut usr = u.to_json(&mut conn).await;
|
||||
usr["userEnabled"] = json!(u.enabled);
|
||||
usr["createdAt"] = json!(format_naive_datetime_local(&u.created_at, DT_FMT));
|
||||
@@ -354,7 +357,7 @@ async fn get_users_json(_token: AdminToken, mut conn: DbConn) -> Json<Value> {
|
||||
async fn users_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult<Html<String>> {
|
||||
let users = User::get_all(&mut conn).await;
|
||||
let mut users_json = Vec::with_capacity(users.len());
|
||||
for u in users {
|
||||
for (u, sso_u) in users {
|
||||
let mut usr = u.to_json(&mut conn).await;
|
||||
usr["cipher_count"] = json!(Cipher::count_owned_by_user(&u.uuid, &mut conn).await);
|
||||
usr["attachment_count"] = json!(Attachment::count_by_user(&u.uuid, &mut conn).await);
|
||||
@@ -365,6 +368,9 @@ async fn users_overview(_token: AdminToken, mut conn: DbConn) -> ApiResult<Html<
|
||||
Some(dt) => json!(format_naive_datetime_local(&dt, DT_FMT)),
|
||||
None => json!("Never"),
|
||||
};
|
||||
|
||||
usr["sso_identifier"] = json!(sso_u.map(|u| u.identifier.to_string()).unwrap_or(String::new()));
|
||||
|
||||
users_json.push(usr);
|
||||
}
|
||||
|
||||
@@ -417,6 +423,27 @@ async fn delete_user(user_id: UserId, token: AdminToken, mut conn: DbConn) -> Em
|
||||
res
|
||||
}
|
||||
|
||||
#[delete("/users/<user_id>/sso", format = "application/json")]
|
||||
async fn delete_sso_user(user_id: UserId, token: AdminToken, mut conn: DbConn) -> EmptyResult {
|
||||
let memberships = Membership::find_any_state_by_user(&user_id, &mut conn).await;
|
||||
let res = SsoUser::delete(&user_id, &mut conn).await;
|
||||
|
||||
for membership in memberships {
|
||||
log_event(
|
||||
EventType::OrganizationUserUnlinkedSso as i32,
|
||||
&membership.uuid,
|
||||
&membership.org_uuid,
|
||||
&ACTING_ADMIN_USER.into(),
|
||||
14, // Use UnknownBrowser type
|
||||
&token.ip.ip,
|
||||
&mut conn,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/deauth", format = "application/json")]
|
||||
async fn deauth_user(user_id: UserId, _token: AdminToken, mut conn: DbConn, nt: Notify<'_>) -> EmptyResult {
|
||||
let mut user = get_user_or_404(&user_id, &mut conn).await?;
|
||||
|
||||
Reference in New Issue
Block a user