You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-29 22:01:14 +03:00
Make the access tokens TTL configurable
This commit is contained in:
@ -34,7 +34,10 @@ use opentelemetry::{
|
||||
use rand::SeedableRng;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{passwords::PasswordManager, upstream_oauth2::cache::MetadataCache, MatrixHomeserver};
|
||||
use crate::{
|
||||
passwords::PasswordManager, site_config::SiteConfig, upstream_oauth2::cache::MetadataCache,
|
||||
MatrixHomeserver,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
@ -50,6 +53,7 @@ pub struct AppState {
|
||||
pub http_client_factory: HttpClientFactory,
|
||||
pub password_manager: PasswordManager,
|
||||
pub metadata_cache: MetadataCache,
|
||||
pub site_config: SiteConfig,
|
||||
pub conn_acquisition_histogram: Option<Histogram<u64>>,
|
||||
}
|
||||
|
||||
@ -199,6 +203,12 @@ impl FromRef<AppState> for MetadataCache {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for SiteConfig {
|
||||
fn from_ref(input: &AppState) -> Self {
|
||||
input.site_config.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromRequestParts<AppState> for BoxClock {
|
||||
type Rejection = Infallible;
|
||||
|
@ -32,7 +32,7 @@ use thiserror::Error;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use super::{MatrixError, MatrixHomeserver};
|
||||
use crate::{impl_from_error_for_route, passwords::PasswordManager};
|
||||
use crate::{impl_from_error_for_route, passwords::PasswordManager, site_config::SiteConfig};
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
@ -210,6 +210,7 @@ pub(crate) async fn post(
|
||||
State(password_manager): State<PasswordManager>,
|
||||
mut repo: BoxRepository,
|
||||
State(homeserver): State<MatrixHomeserver>,
|
||||
State(site_config): State<SiteConfig>,
|
||||
Json(input): Json<RequestBody>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let (session, user) = match (password_manager.is_enabled(), input.credentials) {
|
||||
@ -242,8 +243,7 @@ pub(crate) async fn post(
|
||||
|
||||
// If the client asked for a refreshable token, make it expire
|
||||
let expires_in = if input.refresh_token {
|
||||
// TODO: this should be configurable
|
||||
Some(Duration::minutes(5))
|
||||
Some(site_config.compat_token_ttl)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use axum::{response::IntoResponse, Json};
|
||||
use axum::{extract::State, response::IntoResponse, Json};
|
||||
use chrono::Duration;
|
||||
use hyper::StatusCode;
|
||||
use mas_data_model::{TokenFormatError, TokenType};
|
||||
@ -25,7 +25,7 @@ use serde_with::{serde_as, DurationMilliSeconds};
|
||||
use thiserror::Error;
|
||||
|
||||
use super::MatrixError;
|
||||
use crate::impl_from_error_for_route;
|
||||
use crate::{impl_from_error_for_route, site_config::SiteConfig};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct RequestBody {
|
||||
@ -91,6 +91,7 @@ pub(crate) async fn post(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
mut repo: BoxRepository,
|
||||
State(site_config): State<SiteConfig>,
|
||||
Json(input): Json<RequestBody>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
let token_type = TokenType::check(&input.refresh_token)?;
|
||||
@ -128,7 +129,7 @@ pub(crate) async fn post(
|
||||
let new_refresh_token_str = TokenType::CompatRefreshToken.generate(&mut rng);
|
||||
let new_access_token_str = TokenType::CompatAccessToken.generate(&mut rng);
|
||||
|
||||
let expires_in = Duration::minutes(5);
|
||||
let expires_in = site_config.compat_token_ttl;
|
||||
let new_access_token = repo
|
||||
.compat_access_token()
|
||||
.add(
|
||||
|
@ -68,6 +68,7 @@ pub mod passwords;
|
||||
pub mod upstream_oauth2;
|
||||
mod views;
|
||||
|
||||
mod site_config;
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
|
||||
@ -89,8 +90,10 @@ macro_rules! impl_from_error_for_route {
|
||||
|
||||
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 crate::upstream_oauth2::cache::MetadataCache;
|
||||
pub use self::{
|
||||
app_state::AppState, compat::MatrixHomeserver, graphql::schema as graphql_schema,
|
||||
site_config::SiteConfig, upstream_oauth2::cache::MetadataCache,
|
||||
};
|
||||
|
||||
pub fn healthcheck_router<S, B>() -> Router<S, B>
|
||||
where
|
||||
@ -169,6 +172,7 @@ where
|
||||
BoxRepository: FromRequestParts<S>,
|
||||
Encrypter: FromRef<S>,
|
||||
HttpClientFactory: FromRef<S>,
|
||||
SiteConfig: FromRef<S>,
|
||||
BoxClock: FromRequestParts<S>,
|
||||
BoxRng: FromRequestParts<S>,
|
||||
Policy: FromRequestParts<S>,
|
||||
@ -225,9 +229,10 @@ where
|
||||
<B as HttpBody>::Error: std::error::Error + Send + Sync,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
UrlBuilder: FromRef<S>,
|
||||
BoxRepository: FromRequestParts<S>,
|
||||
SiteConfig: FromRef<S>,
|
||||
MatrixHomeserver: FromRef<S>,
|
||||
PasswordManager: FromRef<S>,
|
||||
BoxRepository: FromRequestParts<S>,
|
||||
BoxClock: FromRequestParts<S>,
|
||||
BoxRng: FromRequestParts<S>,
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ use tracing::debug;
|
||||
use url::Url;
|
||||
|
||||
use super::{generate_id_token, generate_token_pair};
|
||||
use crate::impl_from_error_for_route;
|
||||
use crate::{impl_from_error_for_route, site_config::SiteConfig};
|
||||
|
||||
#[serde_as]
|
||||
#[skip_serializing_none]
|
||||
@ -161,6 +161,7 @@ pub(crate) async fn post(
|
||||
State(key_store): State<Keystore>,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
mut repo: BoxRepository,
|
||||
State(site_config): State<SiteConfig>,
|
||||
State(encrypter): State<Encrypter>,
|
||||
client_authorization: ClientAuthorization<AccessTokenRequest>,
|
||||
) -> Result<impl IntoResponse, RouteError> {
|
||||
@ -191,12 +192,13 @@ pub(crate) async fn post(
|
||||
&client,
|
||||
&key_store,
|
||||
&url_builder,
|
||||
&site_config,
|
||||
repo,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
AccessTokenRequest::RefreshToken(grant) => {
|
||||
refresh_token_grant(&mut rng, &clock, &grant, &client, repo).await?
|
||||
refresh_token_grant(&mut rng, &clock, &grant, &client, &site_config, repo).await?
|
||||
}
|
||||
_ => {
|
||||
return Err(RouteError::UnsupportedGrantType);
|
||||
@ -220,6 +222,7 @@ async fn authorization_code_grant(
|
||||
client: &Client,
|
||||
key_store: &Keystore,
|
||||
url_builder: &UrlBuilder,
|
||||
site_config: &SiteConfig,
|
||||
mut repo: BoxRepository,
|
||||
) -> Result<(AccessTokenResponse, BoxRepository), RouteError> {
|
||||
let authz_grant = repo
|
||||
@ -312,7 +315,7 @@ async fn authorization_code_grant(
|
||||
.get_last_authentication(&browser_session)
|
||||
.await?;
|
||||
|
||||
let ttl = Duration::minutes(5);
|
||||
let ttl = site_config.access_token_ttl;
|
||||
let (access_token, refresh_token) =
|
||||
generate_token_pair(&mut rng, clock, &mut repo, &session, ttl).await?;
|
||||
|
||||
@ -367,6 +370,7 @@ async fn refresh_token_grant(
|
||||
clock: &impl Clock,
|
||||
grant: &RefreshTokenGrant,
|
||||
client: &Client,
|
||||
site_config: &SiteConfig,
|
||||
mut repo: BoxRepository,
|
||||
) -> Result<(AccessTokenResponse, BoxRepository), RouteError> {
|
||||
let refresh_token = repo
|
||||
@ -390,7 +394,7 @@ async fn refresh_token_grant(
|
||||
return Err(RouteError::InvalidGrant);
|
||||
}
|
||||
|
||||
let ttl = Duration::minutes(5);
|
||||
let ttl = site_config.access_token_ttl;
|
||||
let (new_access_token, new_refresh_token) =
|
||||
generate_token_pair(rng, clock, &mut repo, &session, ttl).await?;
|
||||
|
||||
|
31
crates/handlers/src/site_config.rs
Normal file
31
crates/handlers/src/site_config.rs
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2023 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 chrono::Duration;
|
||||
|
||||
/// Random site configuration we don't now where to put yet.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SiteConfig {
|
||||
pub access_token_ttl: Duration,
|
||||
pub compat_token_ttl: Duration,
|
||||
}
|
||||
|
||||
impl Default for SiteConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
access_token_ttl: Duration::minutes(5),
|
||||
compat_token_ttl: Duration::minutes(5),
|
||||
}
|
||||
}
|
||||
}
|
@ -48,6 +48,7 @@ use url::Url;
|
||||
use crate::{
|
||||
app_state::ErrorWrapper,
|
||||
passwords::{Hasher, PasswordManager},
|
||||
site_config::SiteConfig,
|
||||
upstream_oauth2::cache::MetadataCache,
|
||||
MatrixHomeserver,
|
||||
};
|
||||
@ -76,6 +77,7 @@ pub(crate) struct TestState {
|
||||
pub graphql_schema: mas_graphql::Schema,
|
||||
pub http_client_factory: HttpClientFactory,
|
||||
pub password_manager: PasswordManager,
|
||||
pub site_config: SiteConfig,
|
||||
pub clock: Arc<MockClock>,
|
||||
pub rng: Arc<Mutex<ChaChaRng>>,
|
||||
}
|
||||
@ -133,6 +135,8 @@ impl TestState {
|
||||
|
||||
let http_client_factory = HttpClientFactory::new(10);
|
||||
|
||||
let site_config = SiteConfig::default();
|
||||
|
||||
let clock = Arc::new(MockClock::default());
|
||||
let rng = Arc::new(Mutex::new(ChaChaRng::seed_from_u64(42)));
|
||||
|
||||
@ -160,6 +164,7 @@ impl TestState {
|
||||
graphql_schema,
|
||||
http_client_factory,
|
||||
password_manager,
|
||||
site_config,
|
||||
clock,
|
||||
rng,
|
||||
})
|
||||
@ -346,6 +351,12 @@ impl FromRef<TestState> for MetadataCache {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<TestState> for SiteConfig {
|
||||
fn from_ref(input: &TestState) -> Self {
|
||||
input.site_config.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FromRequestParts<TestState> for BoxClock {
|
||||
type Rejection = Infallible;
|
||||
|
Reference in New Issue
Block a user