1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-06 06:02:40 +03:00

Move public base URL from oauth2 config to http config

This commit is contained in:
Quentin Gliech
2022-02-01 09:34:17 +01:00
parent f96c5b0cec
commit c0e5b66ea4
15 changed files with 166 additions and 74 deletions

1
Cargo.lock generated
View File

@@ -2132,6 +2132,7 @@ dependencies = [
"thiserror", "thiserror",
"tokio", "tokio",
"tracing", "tracing",
"url",
"warp", "warp",
] ]

View File

@@ -17,6 +17,7 @@ use std::path::PathBuf;
use async_trait::async_trait; use async_trait::async_trait;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
use super::ConfigurationSection; use super::ConfigurationSection;
@@ -24,6 +25,10 @@ fn default_http_address() -> String {
"[::]:8080".into() "[::]:8080".into()
} }
fn default_public_base() -> Url {
"http://[::]:8080".parse().unwrap()
}
fn http_address_example_1() -> &'static str { fn http_address_example_1() -> &'static str {
"[::1]:8080" "[::1]:8080"
} }
@@ -54,6 +59,9 @@ pub struct HttpConfig {
/// the static files embedded in the server binary /// the static files embedded in the server binary
#[serde(default)] #[serde(default)]
pub web_root: Option<PathBuf>, pub web_root: Option<PathBuf>,
/// Public URL base from where the authentication service is reachable
pub public_base: Url,
} }
impl Default for HttpConfig { impl Default for HttpConfig {
@@ -61,6 +69,7 @@ impl Default for HttpConfig {
Self { Self {
address: default_http_address(), address: default_http_address(),
web_root: None, web_root: None,
public_base: default_public_base(),
} }
} }
} }

View File

@@ -126,15 +126,8 @@ impl OAuth2ClientConfig {
} }
} }
fn default_oauth2_issuer() -> Url {
"http://[::]:8080".parse().unwrap()
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct OAuth2Config { pub struct OAuth2Config {
#[serde(default = "default_oauth2_issuer")]
pub issuer: Url,
#[serde(default)] #[serde(default)]
pub clients: Vec<OAuth2ClientConfig>, pub clients: Vec<OAuth2ClientConfig>,
@@ -143,13 +136,6 @@ pub struct OAuth2Config {
} }
impl OAuth2Config { impl OAuth2Config {
#[must_use]
pub fn discovery_url(&self) -> Url {
self.issuer
.join(".well-known/openid-configuration")
.expect("could not build discovery url")
}
pub async fn key_store(&self) -> anyhow::Result<StaticKeystore> { pub async fn key_store(&self) -> anyhow::Result<StaticKeystore> {
let mut store = StaticKeystore::new(); let mut store = StaticKeystore::new();
@@ -251,7 +237,6 @@ impl ConfigurationSection<'_> for OAuth2Config {
}; };
Ok(Self { Ok(Self {
issuer: default_oauth2_issuer(),
clients: Vec::new(), clients: Vec::new(),
keys: vec![rsa_key, ecdsa_key], keys: vec![rsa_key, ecdsa_key],
}) })
@@ -291,7 +276,6 @@ impl ConfigurationSection<'_> for OAuth2Config {
}; };
Self { Self {
issuer: default_oauth2_issuer(),
clients: Vec::new(), clients: Vec::new(),
keys: vec![rsa_key, ecdsa_key], keys: vec![rsa_key, ecdsa_key],
} }
@@ -331,7 +315,6 @@ mod tests {
NaiDiepgUJ2GI5eq2V8D8nahRANCAARMK9aKUd/H28qaU+0qvS6bSJItzAge1VHn NaiDiepgUJ2GI5eq2V8D8nahRANCAARMK9aKUd/H28qaU+0qvS6bSJItzAge1VHn
OhBAAUVci1RpmUA+KdCL5sw9nadAEiONeiGr+28RYHZmlB9qXnjC OhBAAUVci1RpmUA+KdCL5sw9nadAEiONeiGr+28RYHZmlB9qXnjC
-----END PRIVATE KEY----- -----END PRIVATE KEY-----
issuer: https://example.com
clients: clients:
- client_id: public - client_id: public
client_auth_method: none client_auth_method: none
@@ -372,7 +355,6 @@ mod tests {
let config = OAuth2Config::load_from_file("config.yaml")?; let config = OAuth2Config::load_from_file("config.yaml")?;
assert_eq!(config.issuer, "https://example.com".parse().unwrap());
assert_eq!(config.clients.len(), 5); assert_eq!(config.clients.len(), 5);
assert_eq!(config.clients[0].client_id, "public"); assert_eq!(config.clients[0].client_id, "public");

View File

@@ -47,12 +47,19 @@ pub fn root(
config: &RootConfig, config: &RootConfig,
) -> BoxedFilter<(impl Reply,)> { ) -> BoxedFilter<(impl Reply,)> {
let health = health(pool); let health = health(pool);
let oauth2 = oauth2(pool, templates, key_store, &config.oauth2, &config.cookies); let oauth2 = oauth2(
pool,
templates,
key_store,
&config.oauth2,
&config.http,
&config.cookies,
);
let views = views( let views = views(
pool, pool,
templates, templates,
mailer, mailer,
&config.oauth2, &config.http,
&config.csrf, &config.csrf,
&config.cookies, &config.cookies,
); );

View File

@@ -14,7 +14,7 @@
use std::collections::HashSet; use std::collections::HashSet;
use mas_config::OAuth2Config; use mas_config::HttpConfig;
use mas_iana::{ use mas_iana::{
jose::JsonWebSignatureAlg, jose::JsonWebSignatureAlg,
oauth::{ oauth::{
@@ -23,6 +23,7 @@ use mas_iana::{
}, },
}; };
use mas_jose::SigningKeystore; use mas_jose::SigningKeystore;
use mas_warp_utils::filters::url_builder::UrlBuilder;
use oauth2_types::{ use oauth2_types::{
oidc::{ClaimType, Metadata, SubjectType}, oidc::{ClaimType, Metadata, SubjectType},
requests::{Display, GrantType, ResponseMode}, requests::{Display, GrantType, ResponseMode},
@@ -32,9 +33,9 @@ use warp::{filters::BoxedFilter, Filter, Reply};
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub(super) fn filter( pub(super) fn filter(
key_store: impl SigningKeystore, key_store: impl SigningKeystore,
config: &OAuth2Config, http_config: &HttpConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> { ) -> BoxedFilter<(Box<dyn Reply>,)> {
let base = config.issuer.clone(); let builder = UrlBuilder::from(http_config);
// This is how clients can authenticate // This is how clients can authenticate
let client_auth_methods_supported = Some({ let client_auth_methods_supported = Some({
@@ -62,12 +63,12 @@ pub(super) fn filter(
let jwt_signing_alg_values_supported = Some(key_store.supported_algorithms()); let jwt_signing_alg_values_supported = Some(key_store.supported_algorithms());
// Prepare all the endpoints // Prepare all the endpoints
let issuer = Some(base.clone()); let issuer = Some(builder.oidc_issuer());
let authorization_endpoint = base.join("oauth2/authorize").ok(); let authorization_endpoint = Some(builder.oauth_authorization_endpoint());
let token_endpoint = base.join("oauth2/token").ok(); let token_endpoint = Some(builder.oauth_token_endpoint());
let jwks_uri = base.join("oauth2/keys.json").ok(); let jwks_uri = Some(builder.jwks_uri());
let introspection_endpoint = base.join("oauth2/introspect").ok(); let introspection_endpoint = Some(builder.oauth_introspection_endpoint());
let userinfo_endpoint = base.join("oauth2/userinfo").ok(); let userinfo_endpoint = Some(builder.oidc_userinfo_endpoint());
let scopes_supported = Some({ let scopes_supported = Some({
let mut s = HashSet::new(); let mut s = HashSet::new();

View File

@@ -12,7 +12,7 @@
// 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 mas_config::{OAuth2ClientConfig, OAuth2Config}; use mas_config::{HttpConfig, OAuth2ClientConfig, OAuth2Config};
use mas_data_model::TokenType; use mas_data_model::TokenType;
use mas_iana::oauth::{OAuthClientAuthenticationMethod, OAuthTokenTypeHint}; use mas_iana::oauth::{OAuthClientAuthenticationMethod, OAuthTokenTypeHint};
use mas_storage::oauth2::{ use mas_storage::oauth2::{
@@ -20,18 +20,20 @@ use mas_storage::oauth2::{
}; };
use mas_warp_utils::{ use mas_warp_utils::{
errors::WrapError, errors::WrapError,
filters::{client::client_authentication, database::connection}, filters::{client::client_authentication, database::connection, url_builder::UrlBuilder},
}; };
use oauth2_types::requests::{IntrospectionRequest, IntrospectionResponse}; use oauth2_types::requests::{IntrospectionRequest, IntrospectionResponse};
use sqlx::{pool::PoolConnection, PgPool, Postgres}; use sqlx::{pool::PoolConnection, PgPool, Postgres};
use tracing::{info, warn}; use tracing::{info, warn};
use warp::{filters::BoxedFilter, Filter, Rejection, Reply}; use warp::{filters::BoxedFilter, Filter, Rejection, Reply};
pub fn filter(pool: &PgPool, oauth2_config: &OAuth2Config) -> BoxedFilter<(Box<dyn Reply>,)> { pub fn filter(
let audience = oauth2_config pool: &PgPool,
.issuer oauth2_config: &OAuth2Config,
.join("/oauth2/introspect") http_config: &HttpConfig,
.unwrap() ) -> BoxedFilter<(Box<dyn Reply>,)> {
let audience = UrlBuilder::from(http_config)
.oauth_introspection_endpoint()
.to_string(); .to_string();
warp::path!("oauth2" / "introspect") warp::path!("oauth2" / "introspect")

View File

@@ -15,7 +15,7 @@
use std::sync::Arc; use std::sync::Arc;
use hyper::Method; use hyper::Method;
use mas_config::{CookiesConfig, OAuth2Config}; use mas_config::{CookiesConfig, HttpConfig, OAuth2Config};
use mas_jose::StaticKeystore; use mas_jose::StaticKeystore;
use mas_templates::Templates; use mas_templates::Templates;
use mas_warp_utils::filters::cors::cors; use mas_warp_utils::filters::cors::cors;
@@ -41,14 +41,15 @@ pub fn filter(
templates: &Templates, templates: &Templates,
key_store: &Arc<StaticKeystore>, key_store: &Arc<StaticKeystore>,
oauth2_config: &OAuth2Config, oauth2_config: &OAuth2Config,
http_config: &HttpConfig,
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> BoxedFilter<(impl Reply,)> { ) -> BoxedFilter<(impl Reply,)> {
let discovery = discovery(key_store.as_ref(), oauth2_config); let discovery = discovery(key_store.as_ref(), http_config);
let keys = keys(key_store); let keys = keys(key_store);
let authorization = authorization(pool, templates, oauth2_config, cookies_config); let authorization = authorization(pool, templates, oauth2_config, cookies_config);
let userinfo = userinfo(pool, oauth2_config); let userinfo = userinfo(pool, oauth2_config);
let introspection = introspection(pool, oauth2_config); let introspection = introspection(pool, oauth2_config, http_config);
let token = token(pool, key_store, oauth2_config); let token = token(pool, key_store, oauth2_config, http_config);
let filter = discovery let filter = discovery
.or(keys) .or(keys)

View File

@@ -19,7 +19,7 @@ use chrono::{DateTime, Duration, Utc};
use data_encoding::BASE64URL_NOPAD; use data_encoding::BASE64URL_NOPAD;
use headers::{CacheControl, Pragma}; use headers::{CacheControl, Pragma};
use hyper::StatusCode; use hyper::StatusCode;
use mas_config::{OAuth2ClientConfig, OAuth2Config}; use mas_config::{HttpConfig, OAuth2ClientConfig, OAuth2Config};
use mas_data_model::{AuthorizationGrantStage, TokenType}; use mas_data_model::{AuthorizationGrantStage, TokenType};
use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod}; use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod};
use mas_jose::{ use mas_jose::{
@@ -37,7 +37,7 @@ use mas_storage::{
}; };
use mas_warp_utils::{ use mas_warp_utils::{
errors::WrapError, errors::WrapError,
filters::{client::client_authentication, database::connection}, filters::{client::client_authentication, database::connection, url_builder::UrlBuilder},
reply::with_typed_header, reply::with_typed_header,
}; };
use oauth2_types::{ use oauth2_types::{
@@ -99,14 +99,13 @@ pub fn filter(
pool: &PgPool, pool: &PgPool,
key_store: &Arc<StaticKeystore>, key_store: &Arc<StaticKeystore>,
oauth2_config: &OAuth2Config, oauth2_config: &OAuth2Config,
http_config: &HttpConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> { ) -> BoxedFilter<(Box<dyn Reply>,)> {
let key_store = key_store.clone(); let key_store = key_store.clone();
let audience = oauth2_config let builder = UrlBuilder::from(http_config);
.issuer let audience = builder.oauth_token_endpoint().to_string();
.join("/oauth2/token")
.unwrap() let issuer = builder.oidc_issuer();
.to_string();
let issuer = oauth2_config.issuer.clone();
warp::path!("oauth2" / "token") warp::path!("oauth2" / "token")
.and( .and(

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
use lettre::{message::Mailbox, Address}; use lettre::{message::Mailbox, Address};
use mas_config::{CookiesConfig, CsrfConfig, OAuth2Config}; use mas_config::{CookiesConfig, CsrfConfig, HttpConfig};
use mas_data_model::{BrowserSession, User, UserEmail}; use mas_data_model::{BrowserSession, User, UserEmail};
use mas_email::Mailer; use mas_email::Mailer;
use mas_storage::{ use mas_storage::{
@@ -31,6 +31,7 @@ use mas_warp_utils::{
csrf::{protected_form, updated_csrf_token}, csrf::{protected_form, updated_csrf_token},
database::{connection, transaction}, database::{connection, transaction},
session::session, session::session,
url_builder::{url_builder, UrlBuilder},
with_templates, CsrfToken, with_templates, CsrfToken,
}, },
}; };
@@ -38,21 +39,18 @@ use rand::{distributions::Alphanumeric, thread_rng, Rng};
use serde::Deserialize; use serde::Deserialize;
use sqlx::{pool::PoolConnection, PgExecutor, PgPool, Postgres, Transaction}; use sqlx::{pool::PoolConnection, PgExecutor, PgPool, Postgres, Transaction};
use tracing::info; use tracing::info;
use url::Url;
use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply}; use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply};
pub(super) fn filter( pub(super) fn filter(
pool: &PgPool, pool: &PgPool,
templates: &Templates, templates: &Templates,
mailer: &Mailer, mailer: &Mailer,
oauth2_config: &OAuth2Config, http_config: &HttpConfig,
csrf_config: &CsrfConfig, csrf_config: &CsrfConfig,
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> { ) -> BoxedFilter<(Box<dyn Reply>,)> {
let mailer = mailer.clone(); let mailer = mailer.clone();
let base = oauth2_config.issuer.clone();
let get = with_templates(templates) let get = with_templates(templates)
.and(encrypted_cookie_saver(cookies_config)) .and(encrypted_cookie_saver(cookies_config))
.and(updated_csrf_token(cookies_config, csrf_config)) .and(updated_csrf_token(cookies_config, csrf_config))
@@ -62,7 +60,7 @@ pub(super) fn filter(
let post = with_templates(templates) let post = with_templates(templates)
.and(warp::any().map(move || mailer.clone())) .and(warp::any().map(move || mailer.clone()))
.and(warp::any().map(move || base.clone())) .and(url_builder(http_config))
.and(encrypted_cookie_saver(cookies_config)) .and(encrypted_cookie_saver(cookies_config))
.and(updated_csrf_token(cookies_config, csrf_config)) .and(updated_csrf_token(cookies_config, csrf_config))
.and(session(pool, cookies_config)) .and(session(pool, cookies_config))
@@ -120,7 +118,7 @@ async fn render(
async fn start_email_verification( async fn start_email_verification(
mailer: &Mailer, mailer: &Mailer,
base: &Url, url_builder: &UrlBuilder,
executor: impl PgExecutor<'_>, executor: impl PgExecutor<'_>,
user: &User<PostgresqlBackend>, user: &User<PostgresqlBackend>,
user_email: &UserEmail<PostgresqlBackend>, user_email: &UserEmail<PostgresqlBackend>,
@@ -139,8 +137,7 @@ async fn start_email_verification(
let mailbox = Mailbox::new(Some(user.username.clone()), address); let mailbox = Mailbox::new(Some(user.username.clone()), address);
let link = base.join("./verify/")?; let link = url_builder.email_verification(&code);
let link = link.join(&code)?;
let context = EmailVerificationContext::new(user.clone().into(), link); let context = EmailVerificationContext::new(user.clone().into(), link);
@@ -154,7 +151,7 @@ async fn start_email_verification(
async fn post( async fn post(
templates: Templates, templates: Templates,
mailer: Mailer, mailer: Mailer,
base: Url, url_builder: UrlBuilder,
cookie_saver: EncryptedCookieSaver, cookie_saver: EncryptedCookieSaver,
csrf_token: CsrfToken, csrf_token: CsrfToken,
mut session: BrowserSession<PostgresqlBackend>, mut session: BrowserSession<PostgresqlBackend>,
@@ -166,7 +163,7 @@ async fn post(
let user_email = add_user_email(&mut txn, &session.user, email) let user_email = add_user_email(&mut txn, &session.user, email)
.await .await
.wrap_error()?; .wrap_error()?;
start_email_verification(&mailer, &base, &mut txn, &session.user, &user_email) start_email_verification(&mailer, &url_builder, &mut txn, &session.user, &user_email)
.await .await
.wrap_error()?; .wrap_error()?;
} }
@@ -184,7 +181,7 @@ async fn post(
.await .await
.wrap_error()?; .wrap_error()?;
start_email_verification(&mailer, &base, &mut txn, &session.user, &user_email) start_email_verification(&mailer, &url_builder, &mut txn, &session.user, &user_email)
.await .await
.wrap_error()?; .wrap_error()?;
} }

View File

@@ -15,7 +15,7 @@
mod emails; mod emails;
mod password; mod password;
use mas_config::{CookiesConfig, CsrfConfig, OAuth2Config}; use mas_config::{CookiesConfig, CsrfConfig, HttpConfig};
use mas_data_model::BrowserSession; use mas_data_model::BrowserSession;
use mas_email::Mailer; use mas_email::Mailer;
use mas_storage::{ use mas_storage::{
@@ -42,7 +42,7 @@ pub(super) fn filter(
pool: &PgPool, pool: &PgPool,
templates: &Templates, templates: &Templates,
mailer: &Mailer, mailer: &Mailer,
oauth2_config: &OAuth2Config, http_config: &HttpConfig,
csrf_config: &CsrfConfig, csrf_config: &CsrfConfig,
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> { ) -> BoxedFilter<(Box<dyn Reply>,)> {
@@ -60,7 +60,7 @@ pub(super) fn filter(
pool, pool,
templates, templates,
mailer, mailer,
oauth2_config, http_config,
csrf_config, csrf_config,
cookies_config, cookies_config,
); );

View File

@@ -12,7 +12,7 @@
// 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 mas_config::{CookiesConfig, CsrfConfig, OAuth2Config}; use mas_config::{CookiesConfig, CsrfConfig, HttpConfig};
use mas_data_model::BrowserSession; use mas_data_model::BrowserSession;
use mas_storage::PostgresqlBackend; use mas_storage::PostgresqlBackend;
use mas_templates::{IndexContext, TemplateContext, Templates}; use mas_templates::{IndexContext, TemplateContext, Templates};
@@ -20,23 +20,22 @@ use mas_warp_utils::filters::{
cookies::{encrypted_cookie_saver, EncryptedCookieSaver}, cookies::{encrypted_cookie_saver, EncryptedCookieSaver},
csrf::updated_csrf_token, csrf::updated_csrf_token,
session::optional_session, session::optional_session,
url_builder::{url_builder, UrlBuilder},
with_templates, CsrfToken, with_templates, CsrfToken,
}; };
use sqlx::PgPool; use sqlx::PgPool;
use url::Url;
use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply}; use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply};
pub(super) fn filter( pub(super) fn filter(
pool: &PgPool, pool: &PgPool,
templates: &Templates, templates: &Templates,
oauth2_config: &OAuth2Config, http_config: &HttpConfig,
csrf_config: &CsrfConfig, csrf_config: &CsrfConfig,
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> { ) -> BoxedFilter<(Box<dyn Reply>,)> {
let discovery_url = oauth2_config.discovery_url();
warp::path::end() warp::path::end()
.and(warp::get()) .and(warp::get())
.map(move || discovery_url.clone()) .and(url_builder(http_config))
.and(with_templates(templates)) .and(with_templates(templates))
.and(encrypted_cookie_saver(cookies_config)) .and(encrypted_cookie_saver(cookies_config))
.and(updated_csrf_token(cookies_config, csrf_config)) .and(updated_csrf_token(cookies_config, csrf_config))
@@ -46,13 +45,13 @@ pub(super) fn filter(
} }
async fn get( async fn get(
discovery_url: Url, url_builder: UrlBuilder,
templates: Templates, templates: Templates,
cookie_saver: EncryptedCookieSaver, cookie_saver: EncryptedCookieSaver,
csrf_token: CsrfToken, csrf_token: CsrfToken,
maybe_session: Option<BrowserSession<PostgresqlBackend>>, maybe_session: Option<BrowserSession<PostgresqlBackend>>,
) -> Result<Box<dyn Reply>, Rejection> { ) -> Result<Box<dyn Reply>, Rejection> {
let ctx = IndexContext::new(discovery_url) let ctx = IndexContext::new(url_builder.oidc_discovery())
.maybe_with_session(maybe_session) .maybe_with_session(maybe_session)
.with_csrf(csrf_token.form_value()); .with_csrf(csrf_token.form_value());

View File

@@ -12,7 +12,7 @@
// 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 mas_config::{CookiesConfig, CsrfConfig, OAuth2Config}; use mas_config::{CookiesConfig, CsrfConfig, HttpConfig};
use mas_email::Mailer; use mas_email::Mailer;
use mas_templates::Templates; use mas_templates::Templates;
use sqlx::PgPool; use sqlx::PgPool;
@@ -40,16 +40,16 @@ pub(super) fn filter(
pool: &PgPool, pool: &PgPool,
templates: &Templates, templates: &Templates,
mailer: &Mailer, mailer: &Mailer,
oauth2_config: &OAuth2Config, http_config: &HttpConfig,
csrf_config: &CsrfConfig, csrf_config: &CsrfConfig,
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> { ) -> BoxedFilter<(Box<dyn Reply>,)> {
let index = index(pool, templates, oauth2_config, csrf_config, cookies_config); let index = index(pool, templates, http_config, csrf_config, cookies_config);
let account = account( let account = account(
pool, pool,
templates, templates,
mailer, mailer,
oauth2_config, http_config,
csrf_config, csrf_config,
cookies_config, cookies_config,
); );

View File

@@ -36,3 +36,4 @@ mas-data-model = { path = "../data-model" }
mas-storage = { path = "../storage" } mas-storage = { path = "../storage" }
mas-jose = { path = "../jose" } mas-jose = { path = "../jose" }
mas-iana = { path = "../iana" } mas-iana = { path = "../iana" }
url = "2.2.2"

View File

@@ -25,6 +25,7 @@ pub mod csrf;
pub mod database; pub mod database;
pub mod headers; pub mod headers;
pub mod session; pub mod session;
pub mod url_builder;
use std::convert::Infallible; use std::convert::Infallible;

View File

@@ -0,0 +1,92 @@
// Copyright 2022 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.
//! Utility to build URLs
// TODO: move this somewhere else
use std::convert::Infallible;
use mas_config::HttpConfig;
use url::Url;
use warp::Filter;
impl From<&HttpConfig> for UrlBuilder {
fn from(config: &HttpConfig) -> Self {
let base = config.public_base.clone();
Self { base }
}
}
/// Helps building absolute URLs
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UrlBuilder {
base: Url,
}
impl UrlBuilder {
/// OIDC issuer
pub fn oidc_issuer(&self) -> Url {
self.base.clone()
}
/// OIDC dicovery document URL
pub fn oidc_discovery(&self) -> Url {
self.base
.join(".well-known/openid-configuration")
.expect("build URL")
}
/// OAuth 2.0 authorization endpoint
pub fn oauth_authorization_endpoint(&self) -> Url {
self.base.join("oauth2/authorize").expect("build URL")
}
/// OAuth 2.0 token endpoint
pub fn oauth_token_endpoint(&self) -> Url {
self.base.join("oauth2/token").expect("build URL")
}
/// OAuth 2.0 introspection endpoint
pub fn oauth_introspection_endpoint(&self) -> Url {
self.base.join("oauth2/introspect").expect("build URL")
}
/// OAuth 2.0 introspection endpoint
pub fn oidc_userinfo_endpoint(&self) -> Url {
self.base.join("oauth2/userinfo").expect("build URL")
}
/// JWKS URI
pub fn jwks_uri(&self) -> Url {
self.base.join("oauth2/jwks.json").expect("build URL")
}
/// Email verification URL
pub fn email_verification(&self, code: &str) -> Url {
self.base
.join("verify")
.expect("build URL")
.join(code)
.expect("build URL")
}
}
/// Injects an [`UrlBuilder`] to help building absolute URLs
pub fn url_builder(
config: &HttpConfig,
) -> impl Filter<Extract = (UrlBuilder,), Error = Infallible> + Clone + Send + Sync + 'static {
let builder: UrlBuilder = config.into();
warp::any().map(move || builder.clone())
}