From 7fd2b64938e39fed5458440a33571c4c1bd4fceb Mon Sep 17 00:00:00 2001 From: Sebastian Jeltsch Date: Wed, 17 Dec 2025 22:24:39 +0100 Subject: [PATCH] WIP. --- crates/core/src/app_state.rs | 4 +++ crates/core/src/auth/oauth/callback.rs | 8 +++-- crates/core/src/auth/oauth/providers/mod.rs | 13 +++---- crates/core/src/auth/options.rs | 31 +++++++++++++--- crates/core/src/auth/password.rs | 2 +- crates/core/src/wasm/mod.rs | 40 +++++++++++++++++++++ 6 files changed, 83 insertions(+), 15 deletions(-) diff --git a/crates/core/src/app_state.rs b/crates/core/src/app_state.rs index bf2a0a25..a2c441f8 100644 --- a/crates/core/src/app_state.rs +++ b/crates/core/src/app_state.rs @@ -284,6 +284,10 @@ impl AppState { return self.state.auth.value(); } + pub(crate) fn update_auth_options(&self, f: impl FnOnce(&AuthOptions) -> AuthOptions) { + return self.state.auth.update(move |o| Arc::new(f(o))); + } + pub fn site_url(&self) -> Arc> { return self.state.site_url.value(); } diff --git a/crates/core/src/auth/oauth/callback.rs b/crates/core/src/auth/oauth/callback.rs index ed21fa04..03285c0b 100644 --- a/crates/core/src/auth/oauth/callback.rs +++ b/crates/core/src/auth/oauth/callback.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use axum::{ extract::{Path, Query, State}, response::Redirect, @@ -117,7 +119,7 @@ pub(crate) async fn callback_from_external_auth_provider( async fn callback_from_oauth_provider_setting_token_cookies( state: &AppState, cookies: &Cookies, - provider: &OAuthProviderType, + provider: &Arc, redirect: Option, auth_code: String, server_pkce_code_verifier: String, @@ -174,7 +176,7 @@ async fn callback_from_oauth_provider_setting_token_cookies( async fn callback_from_oauth_provider_using_auth_code_flow( state: &AppState, cookies: &Cookies, - provider: &OAuthProviderType, + provider: &Arc, redirect: Option, auth_code: String, server_pkce_code_verifier: String, @@ -234,7 +236,7 @@ async fn callback_from_oauth_provider_using_auth_code_flow( async fn get_or_create_user( state: &AppState, - provider: &OAuthProviderType, + provider: &Arc, auth_code: String, server_pkce_code_verifier: String, ) -> Result { diff --git a/crates/core/src/auth/oauth/providers/mod.rs b/crates/core/src/auth/oauth/providers/mod.rs index 46cae92f..12452358 100644 --- a/crates/core/src/auth/oauth/providers/mod.rs +++ b/crates/core/src/auth/oauth/providers/mod.rs @@ -10,7 +10,7 @@ mod oidc; pub(crate) mod test; use std::collections::hash_map::HashMap; -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock}; use thiserror::Error; use crate::auth::oauth::OAuthProvider; @@ -22,9 +22,10 @@ pub enum OAuthProviderError { Missing(String), } -pub type OAuthProviderType = Box; -type OAuthFactoryType = - dyn Fn(&str, &OAuthProviderConfig) -> Result + Send + Sync; +pub type OAuthProviderType = dyn OAuthProvider + Send + Sync; +type OAuthFactoryType = dyn Fn(&str, &OAuthProviderConfig) -> Result, OAuthProviderError> + + Send + + Sync; pub(crate) struct OAuthProviderFactory { pub id: OAuthProviderId, @@ -56,7 +57,7 @@ pub(crate) fn oauth_providers_static_registry() -> &'static [OAuthProviderFactor pub(crate) fn build_oauth_providers_from_config( config: AuthConfig, -) -> Result, OAuthProviderError> { +) -> Result>, OAuthProviderError> { return config .oauth_providers .iter() @@ -72,7 +73,7 @@ pub(crate) fn build_oauth_providers_from_config( }; let provider = (entry.factory)(key, config)?; - return Ok((provider.name().to_string(), provider)); + return Ok((provider.name().to_string(), provider.into())); }) .collect(); } diff --git a/crates/core/src/auth/options.rs b/crates/core/src/auth/options.rs index d3c4963d..604b8766 100644 --- a/crates/core/src/auth/options.rs +++ b/crates/core/src/auth/options.rs @@ -1,14 +1,32 @@ use log::*; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; use crate::auth::oauth::providers::{OAuthProviderType, build_oauth_providers_from_config}; use crate::auth::password::PasswordOptions; use crate::config::proto::AuthConfig; -#[derive(Default)] -pub struct AuthOptions { +#[derive(Clone, Default)] +pub(crate) struct AuthOptions { password_options: PasswordOptions, - oauth_providers: HashMap, + oauth_providers: HashMap>, + + pub has_login_ui: bool, + pub has_register_ui: bool, + pub has_profile_ui: bool, +} + +impl PartialEq for AuthOptions { + fn eq(&self, other: &Self) -> bool { + let p0: HashSet<&String> = self.oauth_providers.keys().collect(); + let p1: HashSet<&String> = other.oauth_providers.keys().collect(); + + return p0 == p1 + && self.password_options == other.password_options + && self.has_login_ui == other.has_login_ui + && self.has_register_ui == other.has_register_ui + && self.has_profile_ui == other.has_profile_ui; + } } #[derive(Default)] @@ -35,6 +53,9 @@ impl AuthOptions { error!("Failed to derive configured OAuth providers from config: {err}"); return Default::default(); }), + has_login_ui: false, + has_register_ui: false, + has_profile_ui: false, }; } @@ -42,7 +63,7 @@ impl AuthOptions { return &self.password_options; } - pub fn lookup_oauth_provider(&self, name: &str) -> Option<&OAuthProviderType> { + pub fn lookup_oauth_provider(&self, name: &str) -> Option<&Arc> { if let Some(entry) = self.oauth_providers.get(name) { return Some(entry); } diff --git a/crates/core/src/auth/password.rs b/crates/core/src/auth/password.rs index b57263b8..cbc6f685 100644 --- a/crates/core/src/auth/password.rs +++ b/crates/core/src/auth/password.rs @@ -5,7 +5,7 @@ use std::sync::LazyLock; use crate::auth::AuthError; use crate::auth::user::DbUser; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct PasswordOptions { pub min_length: usize, pub max_length: usize, diff --git a/crates/core/src/wasm/mod.rs b/crates/core/src/wasm/mod.rs index 5ae6482c..0b4c1a4e 100644 --- a/crates/core/src/wasm/mod.rs +++ b/crates/core/src/wasm/mod.rs @@ -162,6 +162,9 @@ pub(crate) async fn install_routes_and_jobs( for (method, path) in init_result.http_handlers { debug!("Installing WASM route: {method:?}: {path}"); + // TODO: Check for presence of login/register/profile auth UIs. + scan_for_auth_ui(state, method, &path); + let runtime = runtime.clone(); let registered_path = path.clone(); @@ -239,6 +242,43 @@ pub(crate) async fn install_routes_and_jobs( return Ok(Some(router)); } +fn scan_for_auth_ui( + state: &AppState, + method: trailbase_wasm_runtime_host::HttpMethodType, + path: &str, +) { + use trailbase_wasm_runtime_host::HttpMethodType; + + if method != HttpMethodType::Get { + return; + } + + match path { + crate::auth::REGISTER_USER_UI => { + state.update_auth_options(|old| { + let mut new = old.clone(); + new.has_register_ui = true; + return new; + }); + } + crate::auth::LOGIN_UI => { + state.update_auth_options(|old| { + let mut new = old.clone(); + new.has_login_ui = true; + return new; + }); + } + crate::auth::PROFILE_UI => { + state.update_auth_options(|old| { + let mut new = old.clone(); + new.has_profile_ui = true; + return new; + }); + } + _ => {} + }; +} + #[inline] fn axum_method(method: trailbase_wasm_runtime_host::HttpMethodType) -> axum::routing::MethodFilter { use trailbase_wasm_runtime_host::HttpMethodType;