Files
continuwuity/src/api/client/oidc/login.rs
T
lafleur a586ea390c feat(oidc_provider) use askama templates
Implements a custom OidcResponse with CSP headers and oxide-auth processing
compatibility.
2025-11-11 11:34:26 -05:00

74 lines
2.2 KiB
Rust

use conduwuit_web::oidc::{
oidc_consent_form,
AuthorizationQuery,
LoginError,
LoginQuery,
OidcRequest,
OidcResponse,
};
use axum::extract::State;
use conduwuit::{
Result,
err,
utils::hash::verify_password,
};
use ruma::user_id::UserId;
//#[axum::debug_handler]
/// # `POST /_matrix/client/unstable/org.matrix.msc2964/login`
///
/// Display a login UI to the user and return an authorization code on success.
/// We presume that the OAuth2 query parameters are provided in the form.
/// With the code, the client may then access stage two,
/// [super::authorize::authorize_consent].
pub(crate) async fn oidc_login(
State(services): State<crate::State>,
request: OidcRequest,
) -> Result<OidcResponse> {
let query: LoginQuery = request.clone().try_into().map_err(|LoginError(err)|
err!(Request(InvalidParam("Cannot process login form. {err}")))
)?;
// Only accept local usernames. Mostly to simplify things at first.
let user_id = UserId::parse_with_server_name(
query.username.clone(),
&services.config.server_name
)
.map_err(|e| err!(Request(InvalidUsername("Username is invalid: {e}"))))?;
if ! services.users.exists(&user_id).await {
return Err(err!(Request(Unknown("unknown username"))));
}
tracing::info!("logging in: {user_id:?}");
let valid_hash = services
.users
.password_hash(&user_id)
.await
.inspect_err(|e| tracing::info!("could not get user's hash: {e:?}"))?;
if valid_hash.is_empty() {
return Err(err!(Request(UserDeactivated("the user's hash was not found"))));
}
if let Err(_) = verify_password(&query.password, &valid_hash) {
return Err(err!(Request(InvalidParam("password does not match"))));
}
tracing::info!("{user_id:?} passed, forwarding to consent page");
let hostname = services
.config
.well_known
.client
.as_ref()
.map(|s| s.domain().expect("well-known client should have a domain"));
let hostname = hostname.unwrap_or("Continuwuity");
let authorization_query: AuthorizationQuery = query.into();
services
.oidc
.endpoint()
.with_solicitor(oidc_consent_form(hostname, &authorization_query))
.authorization_flow()
.execute(request)
.map_err(|err| err!(Request(Unknown("authorization failed: {err:?}"))))
}