You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-07 17:03:01 +03:00
Handle cookies better by setting the right flags & expiration
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2909,7 +2909,6 @@ dependencies = [
|
|||||||
"base64ct",
|
"base64ct",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
"const-oid",
|
"const-oid",
|
||||||
"cookie",
|
|
||||||
"der",
|
"der",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
|
@@ -8,7 +8,7 @@ license = "Apache-2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1.73"
|
async-trait = "0.1.73"
|
||||||
axum = { version = "0.6.20", features = ["headers"] }
|
axum = { version = "0.6.20", features = ["headers"] }
|
||||||
axum-extra = { version = "0.7.7", features = ["cookie-private"] }
|
axum-extra = { version = "0.7.7", features = ["cookie-private", "cookie-key-expansion"] }
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
data-encoding = "2.4.0"
|
data-encoding = "2.4.0"
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.28"
|
||||||
|
@@ -14,8 +14,18 @@
|
|||||||
|
|
||||||
//! Private (encrypted) cookie jar, based on axum-extra's cookie jar
|
//! Private (encrypted) cookie jar, based on axum-extra's cookie jar
|
||||||
|
|
||||||
|
use std::convert::Infallible;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::{
|
||||||
|
extract::{FromRef, FromRequestParts},
|
||||||
|
response::{IntoResponseParts, ResponseParts},
|
||||||
|
};
|
||||||
|
use axum_extra::extract::cookie::{Cookie, Key, PrivateCookieJar, SameSite};
|
||||||
|
use http::request::Parts;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[error("could not decode cookie")]
|
#[error("could not decode cookie")]
|
||||||
@@ -23,32 +33,113 @@ pub enum CookieDecodeError {
|
|||||||
Deserialize(#[from] serde_json::Error),
|
Deserialize(#[from] serde_json::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CookieExt {
|
/// Manages cookie options and encryption key
|
||||||
fn decode<T>(&self) -> Result<T, CookieDecodeError>
|
///
|
||||||
where
|
/// This is meant to be accessible through axum's state via the [`FromRef`]
|
||||||
T: DeserializeOwned;
|
/// trait
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CookieManager {
|
||||||
|
options: CookieOption,
|
||||||
|
key: Key,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CookieManager {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn new(base_url: Url, key: Key) -> Self {
|
||||||
|
let options = CookieOption::new(base_url);
|
||||||
|
Self { options, key }
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn encode<T>(self, t: &T) -> Self
|
pub fn derive_from(base_url: Url, key: &[u8]) -> Self {
|
||||||
where
|
let key = Key::derive_from(key);
|
||||||
T: Serialize;
|
Self::new(base_url, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CookieExt for axum_extra::extract::cookie::Cookie<'a> {
|
#[async_trait]
|
||||||
fn decode<T>(&self) -> Result<T, CookieDecodeError>
|
impl<S> FromRequestParts<S> for CookieJar
|
||||||
where
|
where
|
||||||
T: DeserializeOwned,
|
CookieManager: FromRef<S>,
|
||||||
{
|
S: Send + Sync,
|
||||||
let decoded = serde_json::from_str(self.value())?;
|
{
|
||||||
Ok(decoded)
|
type Rejection = Infallible;
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
let cookie_manager = CookieManager::from_ref(state);
|
||||||
|
let inner = PrivateCookieJar::from_headers(&parts.headers, cookie_manager.key.clone());
|
||||||
|
let options = cookie_manager.options.clone();
|
||||||
|
|
||||||
|
Ok(CookieJar { inner, options })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct CookieOption {
|
||||||
|
base_url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CookieOption {
|
||||||
|
const fn new(base_url: Url) -> Self {
|
||||||
|
Self { base_url }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode<T>(mut self, t: &T) -> Self
|
fn secure(&self) -> bool {
|
||||||
where
|
self.base_url.scheme() == "https"
|
||||||
T: Serialize,
|
}
|
||||||
{
|
|
||||||
let encoded = serde_json::to_string(t).unwrap();
|
fn path(&self) -> &str {
|
||||||
self.set_value(encoded);
|
self.base_url.path()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply<'a>(&self, mut cookie: Cookie<'a>) -> Cookie<'a> {
|
||||||
|
cookie.set_http_only(true);
|
||||||
|
cookie.set_secure(self.secure());
|
||||||
|
cookie.set_path(self.path().to_owned());
|
||||||
|
cookie.set_same_site(SameSite::Lax);
|
||||||
|
cookie
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A cookie jar which encrypts cookies & sets secure options
|
||||||
|
pub struct CookieJar {
|
||||||
|
inner: PrivateCookieJar<Key>,
|
||||||
|
options: CookieOption,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CookieJar {
|
||||||
|
#[must_use]
|
||||||
|
pub fn save<T: Serialize>(mut self, key: &str, payload: &T, permanent: bool) -> Self {
|
||||||
|
let serialized =
|
||||||
|
serde_json::to_string(payload).expect("failed to serialize cookie payload");
|
||||||
|
|
||||||
|
let cookie = Cookie::new(key.to_owned(), serialized);
|
||||||
|
let mut cookie = self.options.apply(cookie);
|
||||||
|
|
||||||
|
if permanent {
|
||||||
|
// XXX: this should use a clock
|
||||||
|
cookie.make_permanent();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner = self.inner.add(cookie);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, CookieDecodeError> {
|
||||||
|
let Some(cookie) = self.inner.get(key) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let decoded = serde_json::from_str(cookie.value())?;
|
||||||
|
Ok(Some(decoded))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponseParts for CookieJar {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
fn into_response_parts(self, res: ResponseParts) -> Result<ResponseParts, Self::Error> {
|
||||||
|
self.inner.into_response_parts(res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
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 mas_storage::Clock;
|
use mas_storage::Clock;
|
||||||
@@ -21,7 +20,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_with::{serde_as, TimestampSeconds};
|
use serde_with::{serde_as, TimestampSeconds};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{cookies::CookieDecodeError, CookieExt};
|
use crate::cookies::{CookieDecodeError, CookieJar};
|
||||||
|
|
||||||
/// Failed to validate CSRF token
|
/// Failed to validate CSRF token
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
@@ -118,36 +117,41 @@ pub trait CsrfExt {
|
|||||||
C: Clock;
|
C: Clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K> CsrfExt for PrivateCookieJar<K> {
|
impl CsrfExt for CookieJar {
|
||||||
fn csrf_token<C, R>(self, clock: &C, rng: R) -> (CsrfToken, Self)
|
fn csrf_token<C, R>(self, clock: &C, rng: R) -> (CsrfToken, Self)
|
||||||
where
|
where
|
||||||
R: RngCore,
|
R: RngCore,
|
||||||
C: Clock,
|
C: Clock,
|
||||||
{
|
{
|
||||||
let jar = self;
|
|
||||||
let mut cookie = jar.get("csrf").unwrap_or_else(|| Cookie::new("csrf", ""));
|
|
||||||
cookie.set_path("/");
|
|
||||||
cookie.set_http_only(true);
|
|
||||||
|
|
||||||
let now = clock.now();
|
let now = clock.now();
|
||||||
let new_token = cookie
|
let maybe_token = match self.load::<CsrfToken>("csrf") {
|
||||||
.decode()
|
Ok(Some(token)) => {
|
||||||
.ok()
|
let token = token.verify_expiration(now);
|
||||||
.and_then(|token: CsrfToken| token.verify_expiration(now).ok())
|
|
||||||
.unwrap_or_else(|| CsrfToken::generate(now, rng, Duration::hours(1)))
|
|
||||||
.refresh(now, Duration::hours(1));
|
|
||||||
|
|
||||||
let cookie = cookie.encode(&new_token);
|
// If the token is expired, just ignore it
|
||||||
let jar = jar.add(cookie);
|
token.ok()
|
||||||
(new_token, jar)
|
}
|
||||||
|
Ok(None) => None,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("Failed to decode CSRF cookie: {}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let token = maybe_token.map_or_else(
|
||||||
|
|| CsrfToken::generate(now, rng, Duration::hours(1)),
|
||||||
|
|token| token.refresh(now, Duration::hours(1)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let jar = self.save("csrf", &token, false);
|
||||||
|
(token, jar)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_form<C, T>(&self, clock: &C, form: ProtectedForm<T>) -> Result<T, CsrfError>
|
fn verify_form<C, T>(&self, clock: &C, form: ProtectedForm<T>) -> Result<T, CsrfError>
|
||||||
where
|
where
|
||||||
C: Clock,
|
C: Clock,
|
||||||
{
|
{
|
||||||
let cookie = self.get("csrf").ok_or(CsrfError::Missing)?;
|
let token: CsrfToken = self.load("csrf")?.ok_or(CsrfError::Missing)?;
|
||||||
let token: CsrfToken = cookie.decode()?;
|
|
||||||
let token = token.verify_expiration(clock.now())?;
|
let token = token.verify_expiration(clock.now())?;
|
||||||
token.verify_form_value(&form.csrf)?;
|
token.verify_form_value(&form.csrf)?;
|
||||||
Ok(form.inner)
|
Ok(form.inner)
|
||||||
|
@@ -34,7 +34,6 @@ pub mod user_authorization;
|
|||||||
pub use axum;
|
pub use axum;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
cookies::CookieExt,
|
|
||||||
fancy_error::FancyError,
|
fancy_error::FancyError,
|
||||||
session::{SessionInfo, SessionInfoExt},
|
session::{SessionInfo, SessionInfoExt},
|
||||||
};
|
};
|
||||||
|
@@ -12,13 +12,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use axum_extra::extract::cookie::{Cookie, PrivateCookieJar};
|
|
||||||
use mas_data_model::BrowserSession;
|
use mas_data_model::BrowserSession;
|
||||||
use mas_storage::{user::BrowserSessionRepository, RepositoryAccess};
|
use mas_storage::{user::BrowserSessionRepository, RepositoryAccess};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
use crate::CookieExt;
|
use crate::cookies::CookieJar;
|
||||||
|
|
||||||
/// An encrypted cookie to save the session ID
|
/// An encrypted cookie to save the session ID
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
@@ -79,26 +78,22 @@ pub trait SessionInfoExt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K> SessionInfoExt for PrivateCookieJar<K> {
|
impl SessionInfoExt for CookieJar {
|
||||||
fn session_info(self) -> (SessionInfo, Self) {
|
fn session_info(self) -> (SessionInfo, Self) {
|
||||||
let jar = self;
|
let info = match self.load("session") {
|
||||||
let mut cookie = jar
|
Ok(Some(s)) => s,
|
||||||
.get("session")
|
Ok(None) => SessionInfo::default(),
|
||||||
.unwrap_or_else(|| Cookie::new("session", ""));
|
Err(e) => {
|
||||||
cookie.set_path("/");
|
tracing::error!("failed to load session cookie: {}", e);
|
||||||
cookie.set_http_only(true);
|
SessionInfo::default()
|
||||||
let session_info = cookie.decode().unwrap_or_default();
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let cookie = cookie.encode(&session_info);
|
let jar = self.update_session_info(&info);
|
||||||
let jar = jar.add(cookie);
|
(info, jar)
|
||||||
(session_info, jar)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_session_info(self, info: &SessionInfo) -> Self {
|
fn update_session_info(self, info: &SessionInfo) -> Self {
|
||||||
let mut cookie = Cookie::new("session", "");
|
self.save("session", info, true)
|
||||||
cookie.set_path("/");
|
|
||||||
cookie.set_http_only(true);
|
|
||||||
let cookie = cookie.encode(&info);
|
|
||||||
self.add(cookie)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ use anyhow::Context;
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use mas_config::AppConfig;
|
use mas_config::AppConfig;
|
||||||
use mas_handlers::{AppState, HttpClientFactory, MatrixHomeserver};
|
use mas_handlers::{AppState, CookieManager, HttpClientFactory, MatrixHomeserver};
|
||||||
use mas_listener::{server::Server, shutdown::ShutdownStream};
|
use mas_listener::{server::Server, shutdown::ShutdownStream};
|
||||||
use mas_matrix_synapse::SynapseConnection;
|
use mas_matrix_synapse::SynapseConnection;
|
||||||
use mas_router::UrlBuilder;
|
use mas_router::UrlBuilder;
|
||||||
@@ -52,6 +52,11 @@ impl Options {
|
|||||||
let span = info_span!("cli.run.init").entered();
|
let span = info_span!("cli.run.init").entered();
|
||||||
let config: AppConfig = root.load_config()?;
|
let config: AppConfig = root.load_config()?;
|
||||||
|
|
||||||
|
// XXX: there should be a generic config verification step
|
||||||
|
if config.http.public_base.path() != "/" {
|
||||||
|
anyhow::bail!("The http.public_base path is not set to /, this is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to the database
|
// Connect to the database
|
||||||
info!("Connecting to the database");
|
info!("Connecting to the database");
|
||||||
let pool = database_from_config(&config.database).await?;
|
let pool = database_from_config(&config.database).await?;
|
||||||
@@ -73,6 +78,8 @@ impl Options {
|
|||||||
.context("could not import keys from config")?;
|
.context("could not import keys from config")?;
|
||||||
|
|
||||||
let encrypter = config.secrets.encrypter();
|
let encrypter = config.secrets.encrypter();
|
||||||
|
let cookie_manager =
|
||||||
|
CookieManager::derive_from(config.http.public_base.clone(), &config.secrets.encryption);
|
||||||
|
|
||||||
// Load and compile the WASM policies (and fallback to the default embedded one)
|
// Load and compile the WASM policies (and fallback to the default embedded one)
|
||||||
info!("Loading and compiling the policy module");
|
info!("Loading and compiling the policy module");
|
||||||
@@ -140,6 +147,7 @@ impl Options {
|
|||||||
pool,
|
pool,
|
||||||
templates,
|
templates,
|
||||||
key_store,
|
key_store,
|
||||||
|
cookie_manager,
|
||||||
encrypter,
|
encrypter,
|
||||||
url_builder,
|
url_builder,
|
||||||
homeserver,
|
homeserver,
|
||||||
|
@@ -73,7 +73,7 @@ pub struct SecretsConfig {
|
|||||||
example = "example_secret"
|
example = "example_secret"
|
||||||
)]
|
)]
|
||||||
#[serde_as(as = "serde_with::hex::Hex")]
|
#[serde_as(as = "serde_with::hex::Hex")]
|
||||||
encryption: [u8; 32],
|
pub encryption: [u8; 32],
|
||||||
|
|
||||||
/// List of private keys to use for signing and encrypting payloads
|
/// List of private keys to use for signing and encrypting payloads
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@@ -20,7 +20,7 @@ use axum::{
|
|||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
};
|
};
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::http_client_factory::HttpClientFactory;
|
use mas_axum_utils::{cookies::CookieManager, http_client_factory::HttpClientFactory};
|
||||||
use mas_keystore::{Encrypter, Keystore};
|
use mas_keystore::{Encrypter, Keystore};
|
||||||
use mas_policy::PolicyFactory;
|
use mas_policy::PolicyFactory;
|
||||||
use mas_router::UrlBuilder;
|
use mas_router::UrlBuilder;
|
||||||
@@ -42,6 +42,7 @@ pub struct AppState {
|
|||||||
pub pool: PgPool,
|
pub pool: PgPool,
|
||||||
pub templates: Templates,
|
pub templates: Templates,
|
||||||
pub key_store: Keystore,
|
pub key_store: Keystore,
|
||||||
|
pub cookie_manager: CookieManager,
|
||||||
pub encrypter: Encrypter,
|
pub encrypter: Encrypter,
|
||||||
pub url_builder: UrlBuilder,
|
pub url_builder: UrlBuilder,
|
||||||
pub homeserver: MatrixHomeserver,
|
pub homeserver: MatrixHomeserver,
|
||||||
@@ -161,6 +162,12 @@ impl FromRef<AppState> for PasswordManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromRef<AppState> for CookieManager {
|
||||||
|
fn from_ref(input: &AppState) -> Self {
|
||||||
|
input.cookie_manager.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl FromRequestParts<AppState> for BoxClock {
|
impl FromRequestParts<AppState> for BoxClock {
|
||||||
type Rejection = Infallible;
|
type Rejection = Infallible;
|
||||||
|
@@ -20,14 +20,13 @@ use axum::{
|
|||||||
extract::{Form, Path, Query, State},
|
extract::{Form, Path, Query, State},
|
||||||
response::{Html, IntoResponse, Redirect, Response},
|
response::{Html, IntoResponse, Redirect, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, ProtectedForm},
|
csrf::{CsrfExt, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_data_model::Device;
|
use mas_data_model::Device;
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::{CompatLoginSsoAction, PostAuthAction, Route};
|
use mas_router::{CompatLoginSsoAction, PostAuthAction, Route};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
compat::{CompatSessionRepository, CompatSsoLoginRepository},
|
compat::{CompatSessionRepository, CompatSsoLoginRepository},
|
||||||
@@ -63,7 +62,7 @@ pub async fn get(
|
|||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(id): Path<Ulid>,
|
Path(id): Path<Ulid>,
|
||||||
Query(params): Query<Params>,
|
Query(params): Query<Params>,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
@@ -129,7 +128,7 @@ pub async fn post(
|
|||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(id): Path<Ulid>,
|
Path(id): Path<Ulid>,
|
||||||
Query(params): Query<Params>,
|
Query(params): Query<Params>,
|
||||||
Form(form): Form<ProtectedForm<()>>,
|
Form(form): Form<ProtectedForm<()>>,
|
||||||
|
@@ -25,13 +25,11 @@ use axum::{
|
|||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
Json, TypedHeader,
|
Json, TypedHeader,
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use futures_util::TryStreamExt;
|
use futures_util::TryStreamExt;
|
||||||
use headers::{authorization::Bearer, Authorization, ContentType, HeaderValue};
|
use headers::{authorization::Bearer, Authorization, ContentType, HeaderValue};
|
||||||
use hyper::header::CACHE_CONTROL;
|
use hyper::header::CACHE_CONTROL;
|
||||||
use mas_axum_utils::{FancyError, SessionInfo, SessionInfoExt};
|
use mas_axum_utils::{cookies::CookieJar, FancyError, SessionInfo, SessionInfoExt};
|
||||||
use mas_graphql::{Requester, Schema};
|
use mas_graphql::{Requester, Schema};
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_matrix::HomeserverConnection;
|
use mas_matrix::HomeserverConnection;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
BoxClock, BoxRepository, BoxRng, Clock, Repository, RepositoryError, SystemClock,
|
BoxClock, BoxRepository, BoxRng, Clock, Repository, RepositoryError, SystemClock,
|
||||||
@@ -228,7 +226,7 @@ pub async fn post(
|
|||||||
State(schema): State<Schema>,
|
State(schema): State<Schema>,
|
||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
repo: BoxRepository,
|
repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
content_type: Option<TypedHeader<ContentType>>,
|
content_type: Option<TypedHeader<ContentType>>,
|
||||||
authorization: Option<TypedHeader<Authorization<Bearer>>>,
|
authorization: Option<TypedHeader<Authorization<Bearer>>>,
|
||||||
body: BodyStream,
|
body: BodyStream,
|
||||||
@@ -268,7 +266,7 @@ pub async fn get(
|
|||||||
State(schema): State<Schema>,
|
State(schema): State<Schema>,
|
||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
repo: BoxRepository,
|
repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
authorization: Option<TypedHeader<Authorization<Bearer>>>,
|
authorization: Option<TypedHeader<Authorization<Bearer>>>,
|
||||||
RawQuery(query): RawQuery,
|
RawQuery(query): RawQuery,
|
||||||
) -> Result<impl IntoResponse, FancyError> {
|
) -> Result<impl IntoResponse, FancyError> {
|
||||||
|
@@ -47,7 +47,7 @@ use hyper::{
|
|||||||
},
|
},
|
||||||
StatusCode, Version,
|
StatusCode, Version,
|
||||||
};
|
};
|
||||||
use mas_axum_utils::FancyError;
|
use mas_axum_utils::{cookies::CookieJar, FancyError};
|
||||||
use mas_http::CorsLayerExt;
|
use mas_http::CorsLayerExt;
|
||||||
use mas_keystore::{Encrypter, Keystore};
|
use mas_keystore::{Encrypter, Keystore};
|
||||||
use mas_policy::PolicyFactory;
|
use mas_policy::PolicyFactory;
|
||||||
@@ -87,7 +87,7 @@ macro_rules! impl_from_error_for_route {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use mas_axum_utils::http_client_factory::HttpClientFactory;
|
pub use mas_axum_utils::{cookies::CookieManager, http_client_factory::HttpClientFactory};
|
||||||
|
|
||||||
pub use self::{app_state::AppState, compat::MatrixHomeserver, graphql::schema as graphql_schema};
|
pub use self::{app_state::AppState, compat::MatrixHomeserver, graphql::schema as graphql_schema};
|
||||||
|
|
||||||
@@ -110,6 +110,7 @@ where
|
|||||||
BoxRepository: FromRequestParts<S>,
|
BoxRepository: FromRequestParts<S>,
|
||||||
BoxClock: FromRequestParts<S>,
|
BoxClock: FromRequestParts<S>,
|
||||||
Encrypter: FromRef<S>,
|
Encrypter: FromRef<S>,
|
||||||
|
CookieJar: FromRequestParts<S>,
|
||||||
{
|
{
|
||||||
let mut router = Router::new().route(
|
let mut router = Router::new().route(
|
||||||
"/graphql",
|
"/graphql",
|
||||||
@@ -267,6 +268,7 @@ where
|
|||||||
UrlBuilder: FromRef<S>,
|
UrlBuilder: FromRef<S>,
|
||||||
Arc<PolicyFactory>: FromRef<S>,
|
Arc<PolicyFactory>: FromRef<S>,
|
||||||
BoxRepository: FromRequestParts<S>,
|
BoxRepository: FromRequestParts<S>,
|
||||||
|
CookieJar: FromRequestParts<S>,
|
||||||
Encrypter: FromRef<S>,
|
Encrypter: FromRef<S>,
|
||||||
Templates: FromRef<S>,
|
Templates: FromRef<S>,
|
||||||
Keystore: FromRef<S>,
|
Keystore: FromRef<S>,
|
||||||
|
@@ -18,11 +18,10 @@ use axum::{
|
|||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::{csrf::CsrfExt, SessionInfoExt};
|
use mas_axum_utils::{cookies::CookieJar, csrf::CsrfExt, SessionInfoExt};
|
||||||
use mas_data_model::{AuthorizationGrant, BrowserSession, Client, Device};
|
use mas_data_model::{AuthorizationGrant, BrowserSession, Client, Device};
|
||||||
use mas_keystore::{Encrypter, Keystore};
|
use mas_keystore::Keystore;
|
||||||
use mas_policy::{EvaluationResult, PolicyFactory};
|
use mas_policy::{EvaluationResult, PolicyFactory};
|
||||||
use mas_router::{PostAuthAction, Route, UrlBuilder};
|
use mas_router::{PostAuthAction, Route, UrlBuilder};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
@@ -96,7 +95,7 @@ pub(crate) async fn get(
|
|||||||
State(url_builder): State<UrlBuilder>,
|
State(url_builder): State<UrlBuilder>,
|
||||||
State(key_store): State<Keystore>,
|
State(key_store): State<Keystore>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(grant_id): Path<Ulid>,
|
Path(grant_id): Path<Ulid>,
|
||||||
) -> Result<Response, RouteError> {
|
) -> Result<Response, RouteError> {
|
||||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||||
|
@@ -18,11 +18,10 @@ use axum::{
|
|||||||
extract::{Form, State},
|
extract::{Form, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::{csrf::CsrfExt, SessionInfoExt};
|
use mas_axum_utils::{cookies::CookieJar, csrf::CsrfExt, SessionInfoExt};
|
||||||
use mas_data_model::{AuthorizationCode, Pkce};
|
use mas_data_model::{AuthorizationCode, Pkce};
|
||||||
use mas_keystore::{Encrypter, Keystore};
|
use mas_keystore::Keystore;
|
||||||
use mas_policy::PolicyFactory;
|
use mas_policy::PolicyFactory;
|
||||||
use mas_router::{PostAuthAction, Route, UrlBuilder};
|
use mas_router::{PostAuthAction, Route, UrlBuilder};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
@@ -146,7 +145,7 @@ pub(crate) async fn get(
|
|||||||
State(key_store): State<Keystore>,
|
State(key_store): State<Keystore>,
|
||||||
State(url_builder): State<UrlBuilder>,
|
State(url_builder): State<UrlBuilder>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Form(params): Form<Params>,
|
Form(params): Form<Params>,
|
||||||
) -> Result<Response, RouteError> {
|
) -> Result<Response, RouteError> {
|
||||||
// First, figure out what client it is
|
// First, figure out what client it is
|
||||||
|
@@ -18,14 +18,13 @@ use axum::{
|
|||||||
extract::{Form, Path, State},
|
extract::{Form, Path, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, ProtectedForm},
|
csrf::{CsrfExt, ProtectedForm},
|
||||||
SessionInfoExt,
|
SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_data_model::{AuthorizationGrantStage, Device};
|
use mas_data_model::{AuthorizationGrantStage, Device};
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_policy::PolicyFactory;
|
use mas_policy::PolicyFactory;
|
||||||
use mas_router::{PostAuthAction, Route};
|
use mas_router::{PostAuthAction, Route};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
@@ -84,7 +83,7 @@ pub(crate) async fn get(
|
|||||||
State(policy_factory): State<Arc<PolicyFactory>>,
|
State(policy_factory): State<Arc<PolicyFactory>>,
|
||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(grant_id): Path<Ulid>,
|
Path(grant_id): Path<Ulid>,
|
||||||
) -> Result<Response, RouteError> {
|
) -> Result<Response, RouteError> {
|
||||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||||
@@ -149,7 +148,7 @@ pub(crate) async fn post(
|
|||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
State(policy_factory): State<Arc<PolicyFactory>>,
|
State(policy_factory): State<Arc<PolicyFactory>>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(grant_id): Path<Ulid>,
|
Path(grant_id): Path<Ulid>,
|
||||||
Form(form): Form<ProtectedForm<()>>,
|
Form(form): Form<ProtectedForm<()>>,
|
||||||
) -> Result<Response, RouteError> {
|
) -> Result<Response, RouteError> {
|
||||||
|
@@ -24,7 +24,7 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use headers::{Authorization, ContentType, HeaderMapExt, HeaderName};
|
use headers::{Authorization, ContentType, HeaderMapExt, HeaderName};
|
||||||
use hyper::{header::CONTENT_TYPE, Request, Response, StatusCode};
|
use hyper::{header::CONTENT_TYPE, Request, Response, StatusCode};
|
||||||
use mas_axum_utils::http_client_factory::HttpClientFactory;
|
use mas_axum_utils::{cookies::CookieManager, http_client_factory::HttpClientFactory};
|
||||||
use mas_keystore::{Encrypter, JsonWebKey, JsonWebKeySet, Keystore, PrivateKey};
|
use mas_keystore::{Encrypter, JsonWebKey, JsonWebKeySet, Keystore, PrivateKey};
|
||||||
use mas_matrix::{HomeserverConnection, MockHomeserverConnection};
|
use mas_matrix::{HomeserverConnection, MockHomeserverConnection};
|
||||||
use mas_policy::PolicyFactory;
|
use mas_policy::PolicyFactory;
|
||||||
@@ -59,6 +59,7 @@ pub(crate) struct TestState {
|
|||||||
pub pool: PgPool,
|
pub pool: PgPool,
|
||||||
pub templates: Templates,
|
pub templates: Templates,
|
||||||
pub key_store: Keystore,
|
pub key_store: Keystore,
|
||||||
|
pub cookie_manager: CookieManager,
|
||||||
pub encrypter: Encrypter,
|
pub encrypter: Encrypter,
|
||||||
pub url_builder: UrlBuilder,
|
pub url_builder: UrlBuilder,
|
||||||
pub homeserver: MatrixHomeserver,
|
pub homeserver: MatrixHomeserver,
|
||||||
@@ -95,6 +96,8 @@ impl TestState {
|
|||||||
let key_store = Keystore::new(jwks);
|
let key_store = Keystore::new(jwks);
|
||||||
|
|
||||||
let encrypter = Encrypter::new(&[0x42; 32]);
|
let encrypter = Encrypter::new(&[0x42; 32]);
|
||||||
|
let cookie_manager =
|
||||||
|
CookieManager::derive_from("https://example.com".parse()?, &[0x42; 32]);
|
||||||
|
|
||||||
let password_manager = PasswordManager::new([(1, Hasher::argon2id(None))])?;
|
let password_manager = PasswordManager::new([(1, Hasher::argon2id(None))])?;
|
||||||
|
|
||||||
@@ -135,6 +138,7 @@ impl TestState {
|
|||||||
pool,
|
pool,
|
||||||
templates,
|
templates,
|
||||||
key_store,
|
key_store,
|
||||||
|
cookie_manager,
|
||||||
encrypter,
|
encrypter,
|
||||||
url_builder,
|
url_builder,
|
||||||
homeserver,
|
homeserver,
|
||||||
@@ -317,6 +321,12 @@ impl FromRef<TestState> for PasswordManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromRef<TestState> for CookieManager {
|
||||||
|
fn from_ref(input: &TestState) -> Self {
|
||||||
|
input.cookie_manager.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl FromRequestParts<TestState> for BoxClock {
|
impl FromRequestParts<TestState> for BoxClock {
|
||||||
type Rejection = Infallible;
|
type Rejection = Infallible;
|
||||||
|
@@ -16,10 +16,8 @@ use axum::{
|
|||||||
extract::{Path, Query, State},
|
extract::{Path, Query, State},
|
||||||
response::{IntoResponse, Redirect},
|
response::{IntoResponse, Redirect},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::http_client_factory::HttpClientFactory;
|
use mas_axum_utils::{cookies::CookieJar, http_client_factory::HttpClientFactory};
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_oidc_client::requests::authorization_code::AuthorizationRequestData;
|
use mas_oidc_client::requests::authorization_code::AuthorizationRequestData;
|
||||||
use mas_router::UrlBuilder;
|
use mas_router::UrlBuilder;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
@@ -68,7 +66,7 @@ pub(crate) async fn get(
|
|||||||
State(http_client_factory): State<HttpClientFactory>,
|
State(http_client_factory): State<HttpClientFactory>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
State(url_builder): State<UrlBuilder>,
|
State(url_builder): State<UrlBuilder>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(provider_id): Path<Ulid>,
|
Path(provider_id): Path<Ulid>,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
) -> Result<impl IntoResponse, RouteError> {
|
) -> Result<impl IntoResponse, RouteError> {
|
||||||
|
@@ -16,9 +16,8 @@ use axum::{
|
|||||||
extract::{Path, Query, State},
|
extract::{Path, Query, State},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::http_client_factory::HttpClientFactory;
|
use mas_axum_utils::{cookies::CookieJar, http_client_factory::HttpClientFactory};
|
||||||
use mas_jose::claims::ClaimError;
|
use mas_jose::claims::ClaimError;
|
||||||
use mas_keystore::{Encrypter, Keystore};
|
use mas_keystore::{Encrypter, Keystore};
|
||||||
use mas_oidc_client::requests::{
|
use mas_oidc_client::requests::{
|
||||||
@@ -133,7 +132,7 @@ pub(crate) async fn get(
|
|||||||
State(url_builder): State<UrlBuilder>,
|
State(url_builder): State<UrlBuilder>,
|
||||||
State(encrypter): State<Encrypter>,
|
State(encrypter): State<Encrypter>,
|
||||||
State(keystore): State<Keystore>,
|
State(keystore): State<Keystore>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(provider_id): Path<Ulid>,
|
Path(provider_id): Path<Ulid>,
|
||||||
Query(params): Query<QueryParams>,
|
Query(params): Query<QueryParams>,
|
||||||
) -> Result<impl IntoResponse, RouteError> {
|
) -> Result<impl IntoResponse, RouteError> {
|
||||||
|
@@ -14,14 +14,12 @@
|
|||||||
|
|
||||||
// TODO: move that to a standalone cookie manager
|
// TODO: move that to a standalone cookie manager
|
||||||
|
|
||||||
use axum_extra::extract::{cookie::Cookie, PrivateCookieJar};
|
|
||||||
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
||||||
use mas_axum_utils::CookieExt;
|
use mas_axum_utils::cookies::CookieJar;
|
||||||
use mas_router::PostAuthAction;
|
use mas_router::PostAuthAction;
|
||||||
use mas_storage::Clock;
|
use mas_storage::Clock;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use time::OffsetDateTime;
|
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
/// Name of the cookie
|
/// Name of the cookie
|
||||||
@@ -62,30 +60,24 @@ pub struct UpstreamSessionNotFound;
|
|||||||
|
|
||||||
impl UpstreamSessions {
|
impl UpstreamSessions {
|
||||||
/// Load the upstreams sessions cookie
|
/// Load the upstreams sessions cookie
|
||||||
pub fn load<K>(cookie_jar: &PrivateCookieJar<K>) -> Self {
|
pub fn load(cookie_jar: &CookieJar) -> Self {
|
||||||
cookie_jar
|
match cookie_jar.load(COOKIE_NAME) {
|
||||||
.get(COOKIE_NAME)
|
Ok(Some(sessions)) => sessions,
|
||||||
.and_then(|c| c.decode().ok())
|
Ok(None) => Self::default(),
|
||||||
.unwrap_or_default()
|
Err(e) => {
|
||||||
|
tracing::warn!("Invalid upstream sessions cookie: {}", e);
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save the upstreams sessions to the cookie jar
|
/// Save the upstreams sessions to the cookie jar
|
||||||
pub fn save<K, C>(self, cookie_jar: PrivateCookieJar<K>, clock: &C) -> PrivateCookieJar<K>
|
pub fn save<C>(self, cookie_jar: CookieJar, clock: &C) -> CookieJar
|
||||||
where
|
where
|
||||||
C: Clock,
|
C: Clock,
|
||||||
{
|
{
|
||||||
let now = clock.now();
|
let this = self.expire(clock.now());
|
||||||
let this = self.expire(now);
|
cookie_jar.save(COOKIE_NAME, &this, false)
|
||||||
let mut cookie = Cookie::named(COOKIE_NAME).encode(&this);
|
|
||||||
cookie.set_path("/");
|
|
||||||
cookie.set_http_only(true);
|
|
||||||
|
|
||||||
let expiration = now + Duration::seconds(SESSION_MAX_TIME_SECS);
|
|
||||||
let expiration = OffsetDateTime::from_unix_timestamp(expiration.timestamp())
|
|
||||||
.expect("invalid unix timestamp");
|
|
||||||
cookie.set_expires(expiration);
|
|
||||||
|
|
||||||
cookie_jar.add(cookie)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expire(mut self, now: DateTime<Utc>) -> Self {
|
fn expire(mut self, now: DateTime<Utc>) -> Self {
|
||||||
|
@@ -17,15 +17,14 @@ use axum::{
|
|||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse},
|
||||||
Form,
|
Form,
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, ProtectedForm},
|
csrf::{CsrfExt, ProtectedForm},
|
||||||
SessionInfoExt,
|
SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_data_model::{UpstreamOAuthProviderImportPreference, User};
|
use mas_data_model::{UpstreamOAuthProviderImportPreference, User};
|
||||||
use mas_jose::jwt::Jwt;
|
use mas_jose::jwt::Jwt;
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
job::{JobRepositoryExt, ProvisionUserJob},
|
job::{JobRepositoryExt, ProvisionUserJob},
|
||||||
upstream_oauth2::{UpstreamOAuthLinkRepository, UpstreamOAuthSessionRepository},
|
upstream_oauth2::{UpstreamOAuthLinkRepository, UpstreamOAuthSessionRepository},
|
||||||
@@ -170,7 +169,7 @@ pub(crate) async fn get(
|
|||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(link_id): Path<Ulid>,
|
Path(link_id): Path<Ulid>,
|
||||||
) -> Result<impl IntoResponse, RouteError> {
|
) -> Result<impl IntoResponse, RouteError> {
|
||||||
let sessions_cookie = UpstreamSessionsCookie::load(&cookie_jar);
|
let sessions_cookie = UpstreamSessionsCookie::load(&cookie_jar);
|
||||||
@@ -350,7 +349,7 @@ pub(crate) async fn post(
|
|||||||
mut rng: BoxRng,
|
mut rng: BoxRng,
|
||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Path(link_id): Path<Ulid>,
|
Path(link_id): Path<Ulid>,
|
||||||
Form(form): Form<ProtectedForm<FormData>>,
|
Form(form): Form<ProtectedForm<FormData>>,
|
||||||
) -> Result<impl IntoResponse, RouteError> {
|
) -> Result<impl IntoResponse, RouteError> {
|
||||||
|
@@ -16,12 +16,11 @@ use axum::{
|
|||||||
extract::{Form, Query, State},
|
extract::{Form, Query, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, ProtectedForm},
|
csrf::{CsrfExt, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
job::{JobRepositoryExt, VerifyEmailJob},
|
job::{JobRepositoryExt, VerifyEmailJob},
|
||||||
@@ -44,7 +43,7 @@ pub(crate) async fn get(
|
|||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||||
@@ -70,7 +69,7 @@ pub(crate) async fn post(
|
|||||||
mut rng: BoxRng,
|
mut rng: BoxRng,
|
||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
Form(form): Form<ProtectedForm<EmailForm>>,
|
Form(form): Form<ProtectedForm<EmailForm>>,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
|
@@ -17,12 +17,11 @@ use axum::{
|
|||||||
extract::{Form, Path, Query, State},
|
extract::{Form, Path, Query, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, ProtectedForm},
|
csrf::{CsrfExt, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
job::{JobRepositoryExt, ProvisionUserJob},
|
job::{JobRepositoryExt, ProvisionUserJob},
|
||||||
@@ -53,7 +52,7 @@ pub(crate) async fn get(
|
|||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
Path(id): Path<Ulid>,
|
Path(id): Path<Ulid>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||||
@@ -96,7 +95,7 @@ pub(crate) async fn get(
|
|||||||
pub(crate) async fn post(
|
pub(crate) async fn post(
|
||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
Path(id): Path<Ulid>,
|
Path(id): Path<Ulid>,
|
||||||
Form(form): Form<ProtectedForm<CodeForm>>,
|
Form(form): Form<ProtectedForm<CodeForm>>,
|
||||||
|
@@ -18,13 +18,12 @@ use axum::{
|
|||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, ProtectedForm},
|
csrf::{CsrfExt, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_data_model::BrowserSession;
|
use mas_data_model::BrowserSession;
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
user::{BrowserSessionRepository, UserPasswordRepository},
|
user::{BrowserSessionRepository, UserPasswordRepository},
|
||||||
@@ -51,7 +50,7 @@ pub(crate) async fn get(
|
|||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
State(password_manager): State<PasswordManager>,
|
State(password_manager): State<PasswordManager>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
// If the password manager is disabled, we can go back to the account page.
|
// If the password manager is disabled, we can go back to the account page.
|
||||||
if !password_manager.is_enabled() {
|
if !password_manager.is_enabled() {
|
||||||
@@ -75,7 +74,7 @@ async fn render(
|
|||||||
clock: &impl Clock,
|
clock: &impl Clock,
|
||||||
templates: Templates,
|
templates: Templates,
|
||||||
session: BrowserSession,
|
session: BrowserSession,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock, rng);
|
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(clock, rng);
|
||||||
|
|
||||||
@@ -95,7 +94,7 @@ pub(crate) async fn post(
|
|||||||
State(password_manager): State<PasswordManager>,
|
State(password_manager): State<PasswordManager>,
|
||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Form(form): Form<ProtectedForm<ChangeForm>>,
|
Form(form): Form<ProtectedForm<ChangeForm>>,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
if !password_manager.is_enabled() {
|
if !password_manager.is_enabled() {
|
||||||
|
@@ -16,9 +16,7 @@ use axum::{
|
|||||||
extract::State,
|
extract::State,
|
||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
use mas_axum_utils::{cookies::CookieJar, FancyError, SessionInfoExt};
|
||||||
use mas_axum_utils::{FancyError, SessionInfoExt};
|
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::{PostAuthAction, Route};
|
use mas_router::{PostAuthAction, Route};
|
||||||
use mas_storage::BoxRepository;
|
use mas_storage::BoxRepository;
|
||||||
use mas_templates::{AppContext, Templates};
|
use mas_templates::{AppContext, Templates};
|
||||||
@@ -27,7 +25,7 @@ use mas_templates::{AppContext, Templates};
|
|||||||
pub async fn get(
|
pub async fn get(
|
||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<impl IntoResponse, FancyError> {
|
) -> Result<impl IntoResponse, FancyError> {
|
||||||
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 repo).await?;
|
let session = session_info.load_session(&mut repo).await?;
|
||||||
|
@@ -16,9 +16,7 @@ use axum::{
|
|||||||
extract::State,
|
extract::State,
|
||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
use mas_axum_utils::{cookies::CookieJar, csrf::CsrfExt, FancyError, SessionInfoExt};
|
||||||
use mas_axum_utils::{csrf::CsrfExt, FancyError, SessionInfoExt};
|
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::UrlBuilder;
|
use mas_router::UrlBuilder;
|
||||||
use mas_storage::{BoxClock, BoxRepository, BoxRng};
|
use mas_storage::{BoxClock, BoxRepository, BoxRng};
|
||||||
use mas_templates::{IndexContext, TemplateContext, Templates};
|
use mas_templates::{IndexContext, TemplateContext, Templates};
|
||||||
@@ -30,7 +28,7 @@ pub async fn get(
|
|||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
State(url_builder): State<UrlBuilder>,
|
State(url_builder): State<UrlBuilder>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<impl IntoResponse, FancyError> {
|
) -> Result<impl IntoResponse, FancyError> {
|
||||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||||
|
@@ -16,14 +16,13 @@ use axum::{
|
|||||||
extract::{Form, Query, State},
|
extract::{Form, Query, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, CsrfToken, ProtectedForm},
|
csrf::{CsrfExt, CsrfToken, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_data_model::BrowserSession;
|
use mas_data_model::BrowserSession;
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::{Route, UpstreamOAuth2Authorize};
|
use mas_router::{Route, UpstreamOAuth2Authorize};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
upstream_oauth2::UpstreamOAuthProviderRepository,
|
upstream_oauth2::UpstreamOAuthProviderRepository,
|
||||||
@@ -58,7 +57,7 @@ pub(crate) async fn get(
|
|||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||||
@@ -109,7 +108,7 @@ pub(crate) async fn post(
|
|||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Form(form): Form<ProtectedForm<LoginForm>>,
|
Form(form): Form<ProtectedForm<LoginForm>>,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
if !password_manager.is_enabled() {
|
if !password_manager.is_enabled() {
|
||||||
|
@@ -13,12 +13,11 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use axum::{extract::Form, response::IntoResponse};
|
use axum::{extract::Form, response::IntoResponse};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, ProtectedForm},
|
csrf::{CsrfExt, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::{PostAuthAction, Route};
|
use mas_router::{PostAuthAction, Route};
|
||||||
use mas_storage::{user::BrowserSessionRepository, BoxClock, BoxRepository};
|
use mas_storage::{user::BrowserSessionRepository, BoxClock, BoxRepository};
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ use mas_storage::{user::BrowserSessionRepository, BoxClock, BoxRepository};
|
|||||||
pub(crate) async fn post(
|
pub(crate) async fn post(
|
||||||
clock: BoxClock,
|
clock: BoxClock,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Form(form): Form<ProtectedForm<Option<PostAuthAction>>>,
|
Form(form): Form<ProtectedForm<Option<PostAuthAction>>>,
|
||||||
) -> Result<impl IntoResponse, FancyError> {
|
) -> Result<impl IntoResponse, FancyError> {
|
||||||
let form = cookie_jar.verify_form(&clock, form)?;
|
let form = cookie_jar.verify_form(&clock, form)?;
|
||||||
|
@@ -17,13 +17,12 @@ use axum::{
|
|||||||
extract::{Form, Query, State},
|
extract::{Form, Query, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, ProtectedForm},
|
csrf::{CsrfExt, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
user::{BrowserSessionRepository, UserPasswordRepository},
|
user::{BrowserSessionRepository, UserPasswordRepository},
|
||||||
@@ -49,7 +48,7 @@ pub(crate) async fn get(
|
|||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
if !password_manager.is_enabled() {
|
if !password_manager.is_enabled() {
|
||||||
// XXX: do something better here
|
// XXX: do something better here
|
||||||
@@ -89,7 +88,7 @@ pub(crate) async fn post(
|
|||||||
State(password_manager): State<PasswordManager>,
|
State(password_manager): State<PasswordManager>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Form(form): Form<ProtectedForm<ReauthForm>>,
|
Form(form): Form<ProtectedForm<ReauthForm>>,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
if !password_manager.is_enabled() {
|
if !password_manager.is_enabled() {
|
||||||
|
@@ -18,14 +18,13 @@ use axum::{
|
|||||||
extract::{Form, Query, State},
|
extract::{Form, Query, State},
|
||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::PrivateCookieJar;
|
|
||||||
use hyper::StatusCode;
|
use hyper::StatusCode;
|
||||||
use lettre::Address;
|
use lettre::Address;
|
||||||
use mas_axum_utils::{
|
use mas_axum_utils::{
|
||||||
|
cookies::CookieJar,
|
||||||
csrf::{CsrfExt, CsrfToken, ProtectedForm},
|
csrf::{CsrfExt, CsrfToken, ProtectedForm},
|
||||||
FancyError, SessionInfoExt,
|
FancyError, SessionInfoExt,
|
||||||
};
|
};
|
||||||
use mas_keystore::Encrypter;
|
|
||||||
use mas_policy::PolicyFactory;
|
use mas_policy::PolicyFactory;
|
||||||
use mas_router::Route;
|
use mas_router::Route;
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
@@ -63,7 +62,7 @@ pub(crate) async fn get(
|
|||||||
State(password_manager): State<PasswordManager>,
|
State(password_manager): State<PasswordManager>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||||
@@ -104,7 +103,7 @@ pub(crate) async fn post(
|
|||||||
State(templates): State<Templates>,
|
State(templates): State<Templates>,
|
||||||
mut repo: BoxRepository,
|
mut repo: BoxRepository,
|
||||||
Query(query): Query<OptionalPostAuthAction>,
|
Query(query): Query<OptionalPostAuthAction>,
|
||||||
cookie_jar: PrivateCookieJar<Encrypter>,
|
cookie_jar: CookieJar,
|
||||||
Form(form): Form<ProtectedForm<RegisterForm>>,
|
Form(form): Form<ProtectedForm<RegisterForm>>,
|
||||||
) -> Result<Response, FancyError> {
|
) -> Result<Response, FancyError> {
|
||||||
if !password_manager.is_enabled() {
|
if !password_manager.is_enabled() {
|
||||||
|
@@ -8,7 +8,6 @@ license = "Apache-2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
aead = { version = "0.5.2", features = ["std"] }
|
aead = { version = "0.5.2", features = ["std"] }
|
||||||
const-oid = { version = "0.9.5", features = ["std"] }
|
const-oid = { version = "0.9.5", features = ["std"] }
|
||||||
cookie = { version = "0.17.0", features = ["key-expansion", "private"] }
|
|
||||||
der = { version = "0.7.8", features = ["std"] }
|
der = { version = "0.7.8", features = ["std"] }
|
||||||
ecdsa = { version = "0.16.8", features = ["std"] }
|
ecdsa = { version = "0.16.8", features = ["std"] }
|
||||||
elliptic-curve = { version = "0.13.5", features = ["std", "pem", "sec1"] }
|
elliptic-curve = { version = "0.13.5", features = ["std", "pem", "sec1"] }
|
||||||
|
@@ -17,23 +17,15 @@ use std::sync::Arc;
|
|||||||
use aead::Aead;
|
use aead::Aead;
|
||||||
use base64ct::{Base64, Encoding};
|
use base64ct::{Base64, Encoding};
|
||||||
use chacha20poly1305::{ChaCha20Poly1305, KeyInit};
|
use chacha20poly1305::{ChaCha20Poly1305, KeyInit};
|
||||||
use cookie::Key;
|
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Helps encrypting and decrypting data
|
/// Helps encrypting and decrypting data
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Encrypter {
|
pub struct Encrypter {
|
||||||
cookie_key: Arc<Key>,
|
|
||||||
aead: Arc<ChaCha20Poly1305>,
|
aead: Arc<ChaCha20Poly1305>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Encrypter> for Key {
|
|
||||||
fn from(e: Encrypter) -> Self {
|
|
||||||
e.cookie_key.as_ref().clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[error("Decryption error")]
|
#[error("Decryption error")]
|
||||||
pub enum DecryptError {
|
pub enum DecryptError {
|
||||||
@@ -46,12 +38,10 @@ impl Encrypter {
|
|||||||
/// Creates an [`Encrypter`] out of an encryption key
|
/// Creates an [`Encrypter`] out of an encryption key
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(key: &[u8; 32]) -> Self {
|
pub fn new(key: &[u8; 32]) -> Self {
|
||||||
let cookie_key = Key::derive_from(&key[..]);
|
|
||||||
let cookie_key = Arc::new(cookie_key);
|
|
||||||
let key = GenericArray::from_slice(key);
|
let key = GenericArray::from_slice(key);
|
||||||
let aead = ChaCha20Poly1305::new(key);
|
let aead = ChaCha20Poly1305::new(key);
|
||||||
let aead = Arc::new(aead);
|
let aead = Arc::new(aead);
|
||||||
Self { cookie_key, aead }
|
Self { aead }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt a payload
|
/// Encrypt a payload
|
||||||
|
Reference in New Issue
Block a user