You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2026-01-03 17:02:28 +03:00
better CSRF form handling
This commit is contained in:
@@ -1,34 +0,0 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::filters::CsrfToken;
|
||||
|
||||
/// A CSRF-protected form
|
||||
#[derive(Deserialize)]
|
||||
pub struct CsrfForm<T> {
|
||||
csrf: String,
|
||||
|
||||
#[serde(flatten)]
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T> CsrfForm<T> {
|
||||
pub fn verify_csrf(self, token: &CsrfToken) -> anyhow::Result<T> {
|
||||
// Verify CSRF from request
|
||||
token.verify_form_value(&self.csrf)?;
|
||||
Ok(self.inner)
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use data_encoding::BASE64URL_NOPAD;
|
||||
use headers::SetCookie;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_with::{serde_as, TimestampSeconds};
|
||||
use warp::{filters::BoxedFilter, Filter, Rejection, Reply};
|
||||
|
||||
@@ -78,6 +78,23 @@ impl CsrfToken {
|
||||
}
|
||||
}
|
||||
|
||||
/// A CSRF-protected form
|
||||
#[derive(Deserialize)]
|
||||
struct CsrfForm<T> {
|
||||
csrf: String,
|
||||
|
||||
#[serde(flatten)]
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T> CsrfForm<T> {
|
||||
fn verify_csrf(self, token: &CsrfToken) -> anyhow::Result<T> {
|
||||
// Verify CSRF from request
|
||||
token.verify_form_value(&self.csrf)?;
|
||||
Ok(self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn csrf_token(cookies_config: &CookiesConfig) -> BoxedFilter<(CsrfToken,)> {
|
||||
super::cookies::encrypted("csrf", cookies_config)
|
||||
.and_then(move |token: CsrfToken| async move {
|
||||
@@ -118,3 +135,18 @@ where
|
||||
{
|
||||
save_encrypted("csrf", cookies_config)
|
||||
}
|
||||
|
||||
pub fn protected_form<T>(cookies_config: &CookiesConfig) -> BoxedFilter<(T,)>
|
||||
where
|
||||
T: DeserializeOwned + Send + 'static,
|
||||
{
|
||||
csrf_token(cookies_config)
|
||||
.and(warp::body::form())
|
||||
.and_then(
|
||||
|csrf_token: CsrfToken, protected_form: CsrfForm<T>| async move {
|
||||
let form = protected_form.verify_csrf(&csrf_token).wrap_error()?;
|
||||
Ok::<_, Rejection>(form)
|
||||
},
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ use warp::{
|
||||
|
||||
use crate::{
|
||||
config::{CookiesConfig, CsrfConfig},
|
||||
csrf::CsrfForm,
|
||||
errors::WrapError,
|
||||
filters::{
|
||||
csrf::{csrf_token, save_csrf_token, updated_csrf_token},
|
||||
csrf::{protected_form, save_csrf_token, updated_csrf_token},
|
||||
session::{save_session, with_optional_session},
|
||||
with_pool, with_templates, CsrfToken,
|
||||
},
|
||||
@@ -52,9 +51,8 @@ pub(super) fn filter(
|
||||
.with(wrap_fn(save_csrf_token(cookies_config)));
|
||||
|
||||
let post = warp::post()
|
||||
.and(csrf_token(cookies_config))
|
||||
.and(with_pool(pool))
|
||||
.and(warp::body::form())
|
||||
.and(protected_form(cookies_config))
|
||||
.and_then(post)
|
||||
.untuple_one()
|
||||
.with(wrap_fn(save_session(cookies_config)));
|
||||
@@ -81,13 +79,7 @@ async fn get(
|
||||
))
|
||||
}
|
||||
|
||||
async fn post(
|
||||
csrf_token: CsrfToken,
|
||||
db: PgPool,
|
||||
form: CsrfForm<LoginForm>,
|
||||
) -> Result<(SessionInfo, impl Reply), Rejection> {
|
||||
let form = form.verify_csrf(&csrf_token).wrap_error()?;
|
||||
|
||||
async fn post(db: PgPool, form: LoginForm) -> Result<(SessionInfo, impl Reply), Rejection> {
|
||||
let session_info = login(&db, &form.username, &form.password)
|
||||
.await
|
||||
.wrap_error()?;
|
||||
|
||||
@@ -13,40 +13,26 @@
|
||||
// limitations under the License.
|
||||
|
||||
use sqlx::PgPool;
|
||||
use warp::{filters::BoxedFilter, hyper::Uri, wrap_fn, Filter, Rejection, Reply};
|
||||
use warp::{filters::BoxedFilter, hyper::Uri, Filter, Rejection, Reply};
|
||||
|
||||
use crate::{
|
||||
config::CookiesConfig,
|
||||
csrf::CsrfForm,
|
||||
errors::WrapError,
|
||||
filters::{
|
||||
csrf::{csrf_token, save_csrf_token},
|
||||
session::with_session,
|
||||
with_pool, CsrfToken,
|
||||
},
|
||||
filters::{csrf::protected_form, session::with_session, with_pool},
|
||||
storage::SessionInfo,
|
||||
};
|
||||
|
||||
pub(super) fn filter(pool: &PgPool, cookies_config: &CookiesConfig) -> BoxedFilter<(impl Reply,)> {
|
||||
warp::post()
|
||||
.and(warp::path("logout"))
|
||||
.and(csrf_token(cookies_config))
|
||||
.and(with_session(pool, cookies_config))
|
||||
.and(with_pool(pool))
|
||||
.and(warp::body::form())
|
||||
.and(protected_form(cookies_config))
|
||||
.and_then(post)
|
||||
.untuple_one()
|
||||
.with(wrap_fn(save_csrf_token(cookies_config)))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
async fn post(
|
||||
token: CsrfToken,
|
||||
session: SessionInfo,
|
||||
pool: PgPool,
|
||||
form: CsrfForm<()>,
|
||||
) -> Result<(CsrfToken, impl Reply), Rejection> {
|
||||
form.verify_csrf(&token).wrap_error()?;
|
||||
async fn post(session: SessionInfo, pool: PgPool, _form: ()) -> Result<impl Reply, Rejection> {
|
||||
session.end(&pool).await.wrap_error()?;
|
||||
Ok::<_, Rejection>((token, warp::redirect(Uri::from_static("/login"))))
|
||||
Ok::<_, Rejection>(warp::redirect(Uri::from_static("/login")))
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ use warp::{
|
||||
|
||||
use crate::{
|
||||
config::{CookiesConfig, CsrfConfig},
|
||||
csrf::CsrfForm,
|
||||
errors::WrapError,
|
||||
filters::{
|
||||
csrf::{csrf_token, save_csrf_token, updated_csrf_token},
|
||||
csrf::{protected_form, save_csrf_token, updated_csrf_token},
|
||||
session::with_session,
|
||||
with_pool, with_templates, CsrfToken,
|
||||
},
|
||||
@@ -51,13 +50,10 @@ pub(super) fn filter(
|
||||
.with(wrap_fn(save_csrf_token(cookies_config)));
|
||||
|
||||
let post = warp::post()
|
||||
.and(csrf_token(cookies_config))
|
||||
.and(with_session(pool, cookies_config))
|
||||
.and(with_pool(pool))
|
||||
.and(warp::body::form())
|
||||
.and_then(post)
|
||||
.untuple_one()
|
||||
.with(wrap_fn(save_csrf_token(cookies_config)));
|
||||
.and(protected_form(cookies_config))
|
||||
.and_then(post);
|
||||
|
||||
warp::path("reauth").and(get.or(post)).boxed()
|
||||
}
|
||||
@@ -81,14 +77,11 @@ async fn get(
|
||||
}
|
||||
|
||||
async fn post(
|
||||
csrf_token: CsrfToken,
|
||||
session: SessionInfo,
|
||||
pool: PgPool,
|
||||
form: CsrfForm<ReauthForm>,
|
||||
) -> Result<(CsrfToken, impl Reply), Rejection> {
|
||||
let form = form.verify_csrf(&csrf_token).wrap_error()?;
|
||||
|
||||
form: ReauthForm,
|
||||
) -> Result<impl Reply, Rejection> {
|
||||
let _session = session.reauth(&pool, &form.password).await.wrap_error()?;
|
||||
|
||||
Ok((csrf_token, warp::redirect(Uri::from_static("/"))))
|
||||
Ok(warp::redirect(Uri::from_static("/")))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilte
|
||||
|
||||
mod cli;
|
||||
mod config;
|
||||
mod csrf;
|
||||
mod errors;
|
||||
mod filters;
|
||||
mod handlers;
|
||||
|
||||
Reference in New Issue
Block a user