You've already forked authentication-service
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:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2132,6 +2132,7 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"url",
|
||||||
"warp",
|
"warp",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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");
|
||||||
|
@@ -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,
|
||||||
);
|
);
|
||||||
|
@@ -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();
|
||||||
|
@@ -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")
|
||||||
|
@@ -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)
|
||||||
|
@@ -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(
|
||||||
|
@@ -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()?;
|
||||||
}
|
}
|
||||||
|
@@ -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,
|
||||||
);
|
);
|
||||||
|
@@ -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());
|
||||||
|
|
||||||
|
@@ -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,
|
||||||
);
|
);
|
||||||
|
@@ -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"
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
92
crates/warp-utils/src/filters/url_builder.rs
Normal file
92
crates/warp-utils/src/filters/url_builder.rs
Normal 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())
|
||||||
|
}
|
Reference in New Issue
Block a user