Use our own session-bound XSRF tokens for the consent screen

This commit is contained in:
Olivier 'reivilibre' 2024-01-20 17:50:13 +00:00
parent 2d23eae641
commit 3584302bee

View File

@ -6,8 +6,8 @@ use axum::{
response::{Html, IntoResponse, Response},
Extension, Form,
};
use axum_csrf::CsrfToken;
use chrono::Utc;
use eyre::{Context, ContextCompat};
use serde::{Deserialize, Serialize};
@ -59,7 +59,6 @@ pub async fn oidc_authorisation(
login_session: Option<LoginSession>,
Extension(config): Extension<Arc<Configuration>>,
Extension(code_store): Extension<VolatileCodeStore>,
csrf: CsrfToken,
OriginalUri(uri): OriginalUri,
) -> Response {
let Query(query) = match query {
@ -87,7 +86,7 @@ pub async fn oidc_authorisation(
// If the application requires consent, then we should ask for that.
if !client_config.skip_consent {
return show_consent_page(login_session, client_config, &config, csrf).await;
return show_consent_page(login_session, client_config, &config).await;
}
// No consent needed: process the authorisation.
@ -107,6 +106,7 @@ pub async fn oidc_authorisation(
#[derive(Deserialize)]
pub struct PostConsentForm {
action: String,
csrf: String,
}
pub async fn post_oidc_authorisation_consent(
@ -114,7 +114,6 @@ pub async fn post_oidc_authorisation_consent(
login_session: Option<LoginSession>,
Extension(config): Extension<Arc<Configuration>>,
Extension(code_store): Extension<VolatileCodeStore>,
_csrf: CsrfToken,
OriginalUri(uri): OriginalUri,
Form(form): Form<PostConsentForm>,
) -> Response {
@ -128,6 +127,14 @@ pub async fn post_oidc_authorisation_consent(
return make_login_redirect(uri);
};
if login_session
.validate_csrf_token(&form.csrf, Utc::now())
.is_err()
{
// XSRF token is not valid, so show the consent form again...
return show_consent_page(login_session, client_config, &config).await;
}
match form.action.as_str() {
"accept" => {
// TODO CORS not ok: send strict prevention headers and frame headers
@ -204,18 +211,14 @@ async fn show_consent_page(
login_session: LoginSession,
client_config: &OidcClientConfiguration,
_config: &Configuration,
csrf: CsrfToken,
) -> Response {
let csrf_token = csrf
.authenticity_token()
let csrf_token = login_session
.generate_csrf_token(Utc::now())
.expect("must be able to create a CSRF token");
(
csrf,
Html(format!(
"hi <u>{}</u>, consent to <u>{}</u>? <form method='POST'><input type='hidden' name='csrf' value='{}'><button type='submit' name='action' value='accept'>Accept</button> <button type='submit' name='action' value='deny'>Deny</button></form>",
login_session.user_name, client_config.name, csrf_token
))
)
Html(format!(
"hi <u>{}</u>, consent to <u>{}</u>? <form method='POST'><input type='hidden' name='csrf' value='{}'><button type='submit' name='action' value='accept'>Accept</button> <button type='submit' name='action' value='deny'>Deny</button></form>",
login_session.user_name, client_config.name, csrf_token
))
.into_response()
}