1
0
Fork 0
mirror of https://github.com/dani-garcia/vaultwarden.git synced 2025-07-31 08:09:07 +00:00

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>
This commit is contained in:
Felix Eckhofer 2023-08-30 16:13:09 +02:00 committed by Timshel
commit 671f7a1d31
24 changed files with 700 additions and 13 deletions

View file

@ -33,6 +33,7 @@ pub fn routes() -> Vec<rocket::Route> {
get_public_keys,
post_keys,
post_password,
post_set_password,
post_kdf,
post_rotatekey,
post_sstamp,
@ -82,6 +83,21 @@ pub struct RegisterData {
organization_user_id: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetPasswordData {
kdf: Option<i32>,
kdf_iterations: Option<i32>,
kdf_memory: Option<i32>,
kdf_parallelism: Option<i32>,
key: String,
keys: Option<KeysData>,
master_password_hash: String,
master_password_hint: Option<String>,
#[allow(dead_code)]
org_identifier: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct KeysData {
@ -89,6 +105,13 @@ struct KeysData {
public_key: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct TokenPayload {
exp: i64,
email: String,
nonce: String,
}
/// Trims whitespace from password hints, and converts blank password hints to `None`.
fn clean_password_hint(password_hint: &Option<String>) -> Option<String> {
match password_hint {
@ -244,6 +267,50 @@ pub async fn _register(data: Json<RegisterData>, mut conn: DbConn) -> JsonResult
})))
}
#[post("/accounts/set-password", data = "<data>")]
async fn post_set_password(data: Json<SetPasswordData>, headers: Headers, mut conn: DbConn) -> JsonResult {
let data: SetPasswordData = data.into_inner();
let mut user = headers.user;
// Check against the password hint setting here so if it fails, the user
// can retry without losing their invitation below.
let password_hint = clean_password_hint(&data.master_password_hash);
enforce_password_hint_setting(&password_hint)?;
if let Some(client_kdf_iter) = data.kdf_iterations {
user.client_kdf_iter = client_kdf_iter;
}
if let Some(client_kdf_type) = data.kdf {
user.client_kdf_type = client_kdf_type;
}
// We need to allow revision-date to use the old security_timestamp
let routes = ["revision_date"];
let routes: Option<Vec<String>> = Some(routes.iter().map(ToString::to_string).collect());
user.client_kdf_memory = data.kdf_memory;
user.client_kdf_parallelism = data.kdf_parallelism;
user.set_password(&data.master_password_hash, Some(data.key), false, routes);
user.password_hint = password_hint;
if let Some(keys) = data.keys {
user.private_key = Some(keys.encrypted_private_key);
user.public_key = Some(keys.public_key);
}
if CONFIG.mail_enabled() {
mail::send_set_password(&user.email.to_lowercase(), &user.name).await?;
}
user.save(&mut conn).await?;
Ok(Json(json!({
"Object": "set-password",
"CaptchaBypassToken": "",
})))
}
#[get("/accounts/profile")]
async fn profile(headers: Headers, mut conn: DbConn) -> Json<Value> {
Json(headers.user.to_json(&mut conn).await)
@ -980,7 +1047,7 @@ struct SecretVerificationRequest {
}
#[post("/accounts/verify-password", data = "<data>")]
fn verify_password(data: Json<SecretVerificationRequest>, headers: Headers) -> EmptyResult {
fn verify_password(data: Json<SecretVerificationRequest>, headers: Headers) -> JsonResult {
let data: SecretVerificationRequest = data.into_inner();
let user = headers.user;
@ -988,7 +1055,9 @@ fn verify_password(data: Json<SecretVerificationRequest>, headers: Headers) -> E
err!("Invalid password")
}
Ok(())
Ok(Json(json!({
"MasterPasswordPolicy": {}, // Required for SSO login with mobile apps
})))
}
async fn _api_key(data: Json<PasswordOrOtpData>, rotate: bool, headers: Headers, mut conn: DbConn) -> JsonResult {