1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

Pass time and RNG in CSRF verification methods

This commit is contained in:
Quentin Gliech
2022-10-24 14:12:07 +02:00
parent f0d95a7613
commit b7c50b5403
26 changed files with 143 additions and 81 deletions

View File

@ -15,7 +15,7 @@
use axum_extra::extract::cookie::{Cookie, PrivateCookieJar}; use axum_extra::extract::cookie::{Cookie, PrivateCookieJar};
use chrono::{DateTime, Duration, Utc}; use chrono::{DateTime, Duration, Utc};
use data_encoding::{DecodeError, BASE64URL_NOPAD}; use data_encoding::{DecodeError, BASE64URL_NOPAD};
use rand::{thread_rng, Rng}; use rand::{Rng, RngCore};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{serde_as, TimestampSeconds}; use serde_with::{serde_as, TimestampSeconds};
use thiserror::Error; use thiserror::Error;
@ -108,23 +108,22 @@ pub struct ProtectedForm<T> {
} }
pub trait CsrfExt { pub trait CsrfExt {
fn csrf_token(self) -> (CsrfToken, Self); fn csrf_token<R>(self, now: DateTime<Utc>, rng: R) -> (CsrfToken, Self)
fn verify_form<T>(&self, form: ProtectedForm<T>) -> Result<T, CsrfError>; where
R: RngCore;
fn verify_form<T>(&self, now: DateTime<Utc>, form: ProtectedForm<T>) -> Result<T, CsrfError>;
} }
impl<K> CsrfExt for PrivateCookieJar<K> { impl<K> CsrfExt for PrivateCookieJar<K> {
fn csrf_token(self) -> (CsrfToken, Self) { fn csrf_token<R>(self, now: DateTime<Utc>, rng: R) -> (CsrfToken, Self)
where
R: RngCore,
{
let jar = self; let jar = self;
let mut cookie = jar.get("csrf").unwrap_or_else(|| Cookie::new("csrf", "")); let mut cookie = jar.get("csrf").unwrap_or_else(|| Cookie::new("csrf", ""));
cookie.set_path("/"); cookie.set_path("/");
cookie.set_http_only(true); cookie.set_http_only(true);
// XXX: the rng source and clock should come from somewhere else
#[allow(clippy::disallowed_methods)]
let now = Utc::now();
#[allow(clippy::disallowed_methods)]
let rng = thread_rng();
let new_token = cookie let new_token = cookie
.decode() .decode()
.ok() .ok()
@ -137,11 +136,7 @@ impl<K> CsrfExt for PrivateCookieJar<K> {
(new_token, jar) (new_token, jar)
} }
fn verify_form<T>(&self, form: ProtectedForm<T>) -> Result<T, CsrfError> { fn verify_form<T>(&self, now: DateTime<Utc>, form: ProtectedForm<T>) -> Result<T, CsrfError> {
// XXX: the clock should come from somewhere else
#[allow(clippy::disallowed_methods)]
let now = Utc::now();
let cookie = self.get("csrf").ok_or(CsrfError::Missing)?; let cookie = self.get("csrf").ok_or(CsrfError::Missing)?;
let token: CsrfToken = cookie.decode()?; let token: CsrfToken = cookie.decode()?;
let token = token.verify_expiration(now)?; let token = token.verify_expiration(now)?;

View File

@ -14,6 +14,7 @@
use clap::Parser; use clap::Parser;
use mas_config::{ConfigurationSection, RootConfig}; use mas_config::{ConfigurationSection, RootConfig};
use rand::SeedableRng;
use tracing::info; use tracing::info;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -51,7 +52,9 @@ impl Options {
Ok(()) Ok(())
} }
SC::Generate => { SC::Generate => {
let config = RootConfig::load_and_generate().await?; // XXX: we should disallow SeedableRng::from_entropy
let rng = rand_chacha::ChaChaRng::from_entropy();
let config = RootConfig::load_and_generate(rng).await?;
serde_yaml::to_writer(std::io::stdout(), &config)?; serde_yaml::to_writer(std::io::stdout(), &config)?;

View File

@ -17,6 +17,7 @@ use std::ops::{Deref, DerefMut};
use async_trait::async_trait; use async_trait::async_trait;
use mas_iana::oauth::OAuthClientAuthenticationMethod; use mas_iana::oauth::OAuthClientAuthenticationMethod;
use mas_jose::jwk::PublicJsonWebKeySet; use mas_jose::jwk::PublicJsonWebKeySet;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
@ -171,8 +172,10 @@ impl ConfigurationSection<'_> for ClientsConfig {
"clients" "clients"
} }
#[tracing::instrument] async fn generate<R>(_rng: R) -> anyhow::Result<Self>
async fn generate() -> anyhow::Result<Self> { where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -14,6 +14,7 @@
use async_trait::async_trait; use async_trait::async_trait;
use chrono::Duration; use chrono::Duration;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::serde_as; use serde_with::serde_as;
@ -47,7 +48,10 @@ impl ConfigurationSection<'_> for CsrfConfig {
"csrf" "csrf"
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(_rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -16,6 +16,7 @@ use std::{num::NonZeroU32, path::PathBuf, time::Duration};
use anyhow::Context; use anyhow::Context;
use async_trait::async_trait; use async_trait::async_trait;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{serde_as, skip_serializing_none}; use serde_with::{serde_as, skip_serializing_none};
@ -224,7 +225,10 @@ impl ConfigurationSection<'_> for DatabaseConfig {
"database" "database"
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(_rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -18,6 +18,7 @@ use anyhow::Context;
use async_trait::async_trait; use async_trait::async_trait;
use lettre::{message::Mailbox, Address}; use lettre::{message::Mailbox, Address};
use mas_email::MailTransport; use mas_email::MailTransport;
use rand::Rng;
use schemars::{ use schemars::{
gen::SchemaGenerator, gen::SchemaGenerator,
schema::{InstanceType, Schema, SchemaObject}, schema::{InstanceType, Schema, SchemaObject},
@ -160,7 +161,10 @@ impl ConfigurationSection<'_> for EmailConfig {
"email" "email"
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(_rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -17,6 +17,7 @@ use std::{borrow::Cow, io::Cursor, ops::Deref, path::PathBuf};
use anyhow::bail; use anyhow::bail;
use async_trait::async_trait; use async_trait::async_trait;
use mas_keystore::PrivateKey; use mas_keystore::PrivateKey;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
@ -328,7 +329,10 @@ impl ConfigurationSection<'_> for HttpConfig {
"http" "http"
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(_rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
use async_trait::async_trait; use async_trait::async_trait;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::serde_as; use serde_with::serde_as;
@ -46,7 +47,10 @@ impl ConfigurationSection<'_> for MatrixConfig {
"matrix" "matrix"
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(_rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
use async_trait::async_trait; use async_trait::async_trait;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -96,18 +97,21 @@ impl ConfigurationSection<'_> for RootConfig {
"" ""
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(mut rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self { Ok(Self {
clients: ClientsConfig::generate().await?, clients: ClientsConfig::generate(&mut rng).await?,
http: HttpConfig::generate().await?, http: HttpConfig::generate(&mut rng).await?,
database: DatabaseConfig::generate().await?, database: DatabaseConfig::generate(&mut rng).await?,
telemetry: TelemetryConfig::generate().await?, telemetry: TelemetryConfig::generate(&mut rng).await?,
templates: TemplatesConfig::generate().await?, templates: TemplatesConfig::generate(&mut rng).await?,
csrf: CsrfConfig::generate().await?, csrf: CsrfConfig::generate(&mut rng).await?,
email: EmailConfig::generate().await?, email: EmailConfig::generate(&mut rng).await?,
secrets: SecretsConfig::generate().await?, secrets: SecretsConfig::generate(&mut rng).await?,
matrix: MatrixConfig::generate().await?, matrix: MatrixConfig::generate(&mut rng).await?,
policy: PolicyConfig::generate().await?, policy: PolicyConfig::generate(&mut rng).await?,
}) })
} }

View File

@ -15,6 +15,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use async_trait::async_trait; use async_trait::async_trait;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::serde_as; use serde_with::serde_as;
@ -76,7 +77,10 @@ impl ConfigurationSection<'_> for PolicyConfig {
"policy" "policy"
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(_rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -20,7 +20,7 @@ use mas_jose::jwk::{JsonWebKey, JsonWebKeySet};
use mas_keystore::{Encrypter, Keystore, PrivateKey}; use mas_keystore::{Encrypter, Keystore, PrivateKey};
use rand::{ use rand::{
distributions::{Alphanumeric, DistString}, distributions::{Alphanumeric, DistString},
thread_rng, SeedableRng, Rng, SeedableRng,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -137,12 +137,11 @@ impl ConfigurationSection<'_> for SecretsConfig {
"secrets" "secrets"
} }
#[tracing::instrument] #[tracing::instrument(skip_all)]
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(mut rng: R) -> anyhow::Result<Self>
// XXX: that RNG should come from somewhere else where
#[allow(clippy::disallowed_methods)] R: Rng + Send,
let mut rng = rand_chacha::ChaChaRng::from_rng(thread_rng())?; {
info!("Generating keys..."); info!("Generating keys...");
let span = tracing::info_span!("rsa"); let span = tracing::info_span!("rsa");

View File

@ -15,6 +15,7 @@
use std::num::NonZeroU16; use std::num::NonZeroU16;
use async_trait::async_trait; use async_trait::async_trait;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
@ -274,7 +275,10 @@ impl ConfigurationSection<'_> for TelemetryConfig {
"telemetry" "telemetry"
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(_rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
use async_trait::async_trait; use async_trait::async_trait;
use rand::Rng;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -49,7 +50,10 @@ impl ConfigurationSection<'_> for TemplatesConfig {
"templates" "templates"
} }
async fn generate() -> anyhow::Result<Self> { async fn generate<R>(_rng: R) -> anyhow::Result<Self>
where
R: Rng + Send,
{
Ok(Self::default()) Ok(Self::default())
} }

View File

@ -21,6 +21,7 @@ use figment::{
providers::{Env, Format, Serialized, Yaml}, providers::{Env, Format, Serialized, Yaml},
Figment, Profile, Figment, Profile,
}; };
use rand::Rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[async_trait] #[async_trait]
@ -31,7 +32,9 @@ pub trait ConfigurationSection<'a>: Sized + Deserialize<'a> + Serialize {
fn path() -> &'static str; fn path() -> &'static str;
/// Generate a sample configuration for this section. /// Generate a sample configuration for this section.
async fn generate() -> anyhow::Result<Self>; async fn generate<R>(rng: R) -> anyhow::Result<Self>
where
R: Rng + Send;
/// Generate a sample configuration and override it with environment /// Generate a sample configuration and override it with environment
/// variables. /// variables.
@ -44,8 +47,11 @@ pub trait ConfigurationSection<'a>: Sized + Deserialize<'a> + Serialize {
/// export MAS_HTTP_ADDRESS=127.0.0.1:1234 /// export MAS_HTTP_ADDRESS=127.0.0.1:1234
/// matrix-authentication-service config generate /// matrix-authentication-service config generate
/// ``` /// ```
async fn load_and_generate() -> anyhow::Result<Self> { async fn load_and_generate<R>(rng: R) -> anyhow::Result<Self>
let base = Self::generate() where
R: Rng + Send,
{
let base = Self::generate(rng)
.await .await
.context("could not generate configuration")?; .context("could not generate configuration")?;

View File

@ -28,10 +28,7 @@ use mas_axum_utils::{
use mas_data_model::Device; use mas_data_model::Device;
use mas_keystore::Encrypter; use mas_keystore::Encrypter;
use mas_router::{CompatLoginSsoAction, PostAuthAction, Route}; use mas_router::{CompatLoginSsoAction, PostAuthAction, Route};
use mas_storage::{ use mas_storage::compat::{fullfill_compat_sso_login, get_compat_sso_login_by_id};
compat::{fullfill_compat_sso_login, get_compat_sso_login_by_id},
Clock,
};
use mas_templates::{CompatSsoContext, ErrorContext, TemplateContext, Templates}; use mas_templates::{CompatSsoContext, ErrorContext, TemplateContext, Templates};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::PgPool; use sqlx::PgPool;
@ -58,11 +55,11 @@ pub async fn get(
Path(id): Path<Ulid>, Path(id): Path<Ulid>,
Query(params): Query<Params>, Query(params): Query<Params>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let clock = Clock::default(); let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;
@ -128,7 +125,7 @@ pub async fn post(
let mut txn = pool.begin().await?; let mut txn = pool.begin().await?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
cookie_jar.verify_form(form)?; cookie_jar.verify_form(clock.now(), form)?;
let maybe_session = session_info.load_session(&mut txn).await?; let maybe_session = session_info.load_session(&mut txn).await?;

View File

@ -57,6 +57,7 @@ pub(crate) async fn get(
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
Path(grant_id): Path<Ulid>, Path(grant_id): Path<Ulid>,
) -> Result<Response, RouteError> { ) -> Result<Response, RouteError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool let mut conn = pool
.acquire() .acquire()
.await .await
@ -76,7 +77,7 @@ pub(crate) async fn get(
} }
if let Some(session) = maybe_session { if let Some(session) = maybe_session {
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let mut policy = policy_factory.instantiate().await?; let mut policy = policy_factory.instantiate().await?;
let res = policy let res = policy
@ -126,7 +127,7 @@ pub(crate) async fn post(
.context("failed to begin db transaction")?; .context("failed to begin db transaction")?;
cookie_jar cookie_jar
.verify_form(form) .verify_form(clock.now(), form)
.context("csrf verification failed")?; .context("csrf verification failed")?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();

View File

@ -42,9 +42,10 @@ pub(crate) async fn get(
State(pool): State<PgPool>, State(pool): State<PgPool>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.begin().await?; let mut conn = pool.begin().await?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;
@ -75,7 +76,7 @@ pub(crate) async fn post(
let (clock, mut rng) = crate::rng_and_clock()?; let (clock, mut rng) = crate::rng_and_clock()?;
let mut txn = pool.begin().await?; let mut txn = pool.begin().await?;
let form = cookie_jar.verify_form(form)?; let form = cookie_jar.verify_form(clock.now(), form)?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let maybe_session = session_info.load_session(&mut txn).await?; let maybe_session = session_info.load_session(&mut txn).await?;

View File

@ -57,6 +57,8 @@ pub(crate) async fn get(
State(pool): State<PgPool>, State(pool): State<PgPool>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
@ -64,7 +66,7 @@ pub(crate) async fn get(
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;
if let Some(session) = maybe_session { if let Some(session) = maybe_session {
render(templates, session, cookie_jar, &mut conn).await render(&mut rng, &clock, templates, session, cookie_jar, &mut conn).await
} else { } else {
let login = mas_router::Login::default(); let login = mas_router::Login::default();
Ok((cookie_jar, login.go()).into_response()) Ok((cookie_jar, login.go()).into_response())
@ -72,12 +74,14 @@ pub(crate) async fn get(
} }
async fn render( async fn render(
rng: impl Rng,
clock: &Clock,
templates: Templates, templates: Templates,
session: BrowserSession<PostgresqlBackend>, session: BrowserSession<PostgresqlBackend>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
executor: impl PgExecutor<'_>, executor: impl PgExecutor<'_>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), rng);
let emails = get_user_emails(executor, &session.user).await?; let emails = get_user_emails(executor, &session.user).await?;
@ -149,7 +153,7 @@ pub(crate) async fn post(
return Ok((cookie_jar, login.go()).into_response()); return Ok((cookie_jar, login.go()).into_response());
}; };
let form = cookie_jar.verify_form(form)?; let form = cookie_jar.verify_form(clock.now(), form)?;
match form { match form {
ManagementForm::Add { email } => { ManagementForm::Add { email } => {
@ -199,7 +203,15 @@ pub(crate) async fn post(
} }
}; };
let reply = render(templates.clone(), session, cookie_jar, &mut txn).await?; let reply = render(
&mut rng,
&clock,
templates.clone(),
session,
cookie_jar,
&mut txn,
)
.await?;
txn.commit().await?; txn.commit().await?;

View File

@ -49,9 +49,10 @@ pub(crate) async fn get(
Path(id): Path<Ulid>, Path(id): Path<Ulid>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;
@ -90,7 +91,7 @@ pub(crate) async fn post(
let clock = Clock::default(); let clock = Clock::default();
let mut txn = pool.begin().await?; let mut txn = pool.begin().await?;
let form = cookie_jar.verify_form(form)?; let form = cookie_jar.verify_form(clock.now(), form)?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let maybe_session = session_info.load_session(&mut txn).await?; let maybe_session = session_info.load_session(&mut txn).await?;

View File

@ -32,9 +32,10 @@ pub(crate) async fn get(
State(pool): State<PgPool>, State(pool): State<PgPool>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;

View File

@ -27,9 +27,10 @@ use mas_keystore::Encrypter;
use mas_router::Route; use mas_router::Route;
use mas_storage::{ use mas_storage::{
user::{authenticate_session, set_password}, user::{authenticate_session, set_password},
PostgresqlBackend, Clock, PostgresqlBackend,
}; };
use mas_templates::{EmptyContext, TemplateContext, Templates}; use mas_templates::{EmptyContext, TemplateContext, Templates};
use rand::Rng;
use serde::Deserialize; use serde::Deserialize;
use sqlx::PgPool; use sqlx::PgPool;
@ -45,6 +46,7 @@ pub(crate) async fn get(
State(pool): State<PgPool>, State(pool): State<PgPool>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
@ -52,7 +54,7 @@ pub(crate) async fn get(
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;
if let Some(session) = maybe_session { if let Some(session) = maybe_session {
render(templates, session, cookie_jar).await render(&mut rng, &clock, templates, session, cookie_jar).await
} else { } else {
let login = mas_router::Login::and_then(mas_router::PostAuthAction::ChangePassword); let login = mas_router::Login::and_then(mas_router::PostAuthAction::ChangePassword);
Ok((cookie_jar, login.go()).into_response()) Ok((cookie_jar, login.go()).into_response())
@ -60,11 +62,13 @@ pub(crate) async fn get(
} }
async fn render( async fn render(
rng: impl Rng,
clock: &Clock,
templates: Templates, templates: Templates,
session: BrowserSession<PostgresqlBackend>, session: BrowserSession<PostgresqlBackend>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), rng);
let ctx = EmptyContext let ctx = EmptyContext
.with_session(session) .with_session(session)
@ -84,7 +88,7 @@ pub(crate) async fn post(
let (clock, mut rng) = crate::rng_and_clock()?; let (clock, mut rng) = crate::rng_and_clock()?;
let mut txn = pool.begin().await?; let mut txn = pool.begin().await?;
let form = cookie_jar.verify_form(form)?; let form = cookie_jar.verify_form(clock.now(), form)?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
@ -122,7 +126,7 @@ pub(crate) async fn post(
) )
.await?; .await?;
let reply = render(templates.clone(), session, cookie_jar).await?; let reply = render(&mut rng, &clock, templates.clone(), session, cookie_jar).await?;
txn.commit().await?; txn.commit().await?;

View File

@ -29,9 +29,10 @@ pub async fn get(
State(pool): State<PgPool>, State(pool): State<PgPool>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<impl IntoResponse, FancyError> { ) -> Result<impl IntoResponse, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let session = session_info.load_session(&mut conn).await?; let session = session_info.load_session(&mut conn).await?;

View File

@ -42,16 +42,16 @@ impl ToFormState for LoginForm {
type Field = LoginFormField; type Field = LoginFormField;
} }
#[tracing::instrument(skip(templates, pool, cookie_jar))]
pub(crate) async fn get( pub(crate) async fn get(
State(templates): State<Templates>, State(templates): State<Templates>,
State(pool): State<PgPool>, State(pool): State<PgPool>,
Query(query): Query<OptionalPostAuthAction>, Query(query): Query<OptionalPostAuthAction>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;
@ -83,9 +83,9 @@ pub(crate) async fn post(
let (clock, mut rng) = crate::rng_and_clock()?; let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let form = cookie_jar.verify_form(form)?; let form = cookie_jar.verify_form(clock.now(), form)?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
// Validate the form // Validate the form
let state = { let state = {

View File

@ -34,7 +34,7 @@ pub(crate) async fn post(
let clock = Clock::default(); let clock = Clock::default();
let mut txn = pool.begin().await?; let mut txn = pool.begin().await?;
let form = cookie_jar.verify_form(form)?; let form = cookie_jar.verify_form(clock.now(), form)?;
let (session_info, mut cookie_jar) = cookie_jar.session_info(); let (session_info, mut cookie_jar) = cookie_jar.session_info();

View File

@ -41,9 +41,10 @@ pub(crate) async fn get(
Query(query): Query<OptionalPostAuthAction>, Query(query): Query<OptionalPostAuthAction>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;
@ -83,7 +84,7 @@ pub(crate) async fn post(
let (clock, mut rng) = crate::rng_and_clock()?; let (clock, mut rng) = crate::rng_and_clock()?;
let mut txn = pool.begin().await?; let mut txn = pool.begin().await?;
let form = cookie_jar.verify_form(form)?; let form = cookie_jar.verify_form(clock.now(), form)?;
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();

View File

@ -63,9 +63,10 @@ pub(crate) async fn get(
Query(query): Query<OptionalPostAuthAction>, Query(query): Query<OptionalPostAuthAction>,
cookie_jar: PrivateCookieJar<Encrypter>, cookie_jar: PrivateCookieJar<Encrypter>,
) -> Result<Response, FancyError> { ) -> Result<Response, FancyError> {
let (clock, mut rng) = crate::rng_and_clock()?;
let mut conn = pool.acquire().await?; let mut conn = pool.acquire().await?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
let (session_info, cookie_jar) = cookie_jar.session_info(); let (session_info, cookie_jar) = cookie_jar.session_info();
let maybe_session = session_info.load_session(&mut conn).await?; let maybe_session = session_info.load_session(&mut conn).await?;
@ -100,9 +101,9 @@ pub(crate) async fn post(
let (clock, mut rng) = crate::rng_and_clock()?; let (clock, mut rng) = crate::rng_and_clock()?;
let mut txn = pool.begin().await?; let mut txn = pool.begin().await?;
let form = cookie_jar.verify_form(form)?; let form = cookie_jar.verify_form(clock.now(), form)?;
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(); let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock.now(), &mut rng);
// Validate the form // Validate the form
let state = { let state = {