Finish OIDC login

This commit is contained in:
2025-03-18 19:37:46 +01:00
parent dbe1ec22e0
commit 1a022bd33e
6 changed files with 149 additions and 12 deletions

@ -1,7 +1,9 @@
use crate::app_config::AppConfig;
use crate::controllers::HttpResult;
use crate::controllers::{HttpFailure, HttpResult};
use crate::extractors::money_session::MoneySession;
use actix_web::HttpResponse;
use crate::services::users_service;
use actix_remote_ip::RemoteIP;
use actix_web::{HttpResponse, web};
use light_openid::primitives::OpenIDConfig;
#[derive(serde::Serialize)]
@ -10,7 +12,7 @@ struct StartOIDCResponse {
}
/// Start OIDC authentication
pub async fn start_oidc(session: MoneySession) -> HttpResult {
pub async fn start_oidc(session: MoneySession, remote_ip: RemoteIP) -> HttpResult {
let prov = AppConfig::get().openid_provider();
let conf = match OpenIDConfig::load_from_url(prov.configuration_url).await {
@ -22,7 +24,7 @@ pub async fn start_oidc(session: MoneySession) -> HttpResult {
}
};
let state = match session.gen_oidc_state() {
let state = match session.gen_oidc_state(remote_ip.0) {
Ok(s) => s,
Err(e) => {
log::error!("Failed to generate auth state! {e}");
@ -39,4 +41,66 @@ pub async fn start_oidc(session: MoneySession) -> HttpResult {
}))
}
// TODO : take from previous projects
#[derive(serde::Deserialize)]
pub struct FinishOpenIDLoginQuery {
code: String,
state: String,
}
/// Finish OIDC authentication
pub async fn finish_oidc(
session: MoneySession,
remote_ip: RemoteIP,
req: web::Json<FinishOpenIDLoginQuery>,
) -> HttpResult {
if let Err(e) = session.validate_state(&req.state, remote_ip.0) {
log::error!("Failed to validate OIDC CB state! {e}");
return Ok(HttpResponse::BadRequest().json("Invalid state!"));
}
let prov = AppConfig::get().openid_provider();
let conf = OpenIDConfig::load_from_url(prov.configuration_url)
.await
.map_err(HttpFailure::OpenID)?;
let (token, _) = conf
.request_token(
prov.client_id,
prov.client_secret,
&req.code,
&AppConfig::get().oidc_redirect_url(),
)
.await
.map_err(HttpFailure::OpenID)?;
let (user_info, _) = conf
.request_user_info(&token)
.await
.map_err(HttpFailure::OpenID)?;
if user_info.email_verified != Some(true) {
log::error!("Email is not verified!");
return Ok(HttpResponse::Unauthorized().json("Email unverified by IDP!"));
}
let mail = match user_info.email {
Some(m) => m,
None => {
return Ok(HttpResponse::Unauthorized().json("Email not provided by the IDP!"));
}
};
let user_name = user_info.name.unwrap_or_else(|| {
format!(
"{} {}",
user_info.given_name.as_deref().unwrap_or(""),
user_info.family_name.as_deref().unwrap_or("")
)
});
let user = users_service::create_or_update_user(&mail, &user_name).await?;
session.set_user(&user)?;
Ok(HttpResponse::Ok().finish())
}