diff --git a/Cargo.lock b/Cargo.lock index 9ae4860..ec723a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -520,7 +520,7 @@ version = "0.1.0" dependencies = [ "anyhow", "axum", - "clap", + "clap 4.4.6", "nefarious-login", "tokio", "tracing-subscriber", @@ -660,6 +660,18 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "clap 4.4.6", + "nefarious-login", + "tokio", + "tracing-subscriber", +] + [[package]] name = "clap" version = "4.4.6" @@ -1876,7 +1888,7 @@ dependencies = [ "axum", "axum-extra 0.7.7", "axum-sessions", - "clap", + "clap 4.4.6", "oauth2", "openidconnect", "pretty_assertions", diff --git a/crates/nefarious-login/src/auth.rs b/crates/nefarious-login/src/auth.rs index 28dd150..b8678bb 100644 --- a/crates/nefarious-login/src/auth.rs +++ b/crates/nefarious-login/src/auth.rs @@ -88,7 +88,7 @@ impl Auth for ZitadelAuthService { Ok(( headers, - Url::parse("http://localhost:3000/dash/home") + Url::parse("http://localhost:3001/authed") .context("failed to parse login_authorized zitadel return url")?, )) } diff --git a/crates/nefarious-login/src/login.rs b/crates/nefarious-login/src/login.rs index 197fb3d..3e809e6 100644 --- a/crates/nefarious-login/src/login.rs +++ b/crates/nefarious-login/src/login.rs @@ -50,11 +50,8 @@ pub mod config { } #[derive(clap::Args, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] - #[group(requires_all = ["auth_url", "client_id", "client_secret", "redirect_url", "token_url", "authority_url"])] + #[group(requires_all = ["client_id", "client_secret", "redirect_url", "authority_url"])] pub struct ZitadelClap { - #[arg(env = "ZITADEL_AUTH_URL", long = "zitadel-auth-url")] - pub auth_url: Option, - #[arg(env = "ZITADEL_CLIENT_ID", long = "zitadel-client-id")] pub client_id: Option, @@ -66,9 +63,6 @@ pub mod config { #[arg(env = "ZITADEL_AUTHORITY_URL", long = "zitadel-authority-url")] pub authority_url: Option, - - #[arg(env = "ZITADEL_TOKEN_URL", long = "zitadel-token-url")] - pub token_url: Option, } impl TryFrom for OAuth { @@ -85,11 +79,6 @@ pub mod config { impl AuthClap { pub fn merge(&mut self, config: AuthConfigFile) -> &mut Self { if let Some(zitadel) = config.zitadel { - if let Some(auth_url) = zitadel.auth_url { - if self.zitadel.auth_url.is_some() { - _ = self.zitadel.auth_url.replace(auth_url); - } - } if let Some(client_id) = zitadel.client_id { if self.zitadel.client_id.is_some() { _ = self.zitadel.client_id.replace(client_id); @@ -110,11 +99,6 @@ pub mod config { _ = self.zitadel.authority_url.replace(authority_url); } } - if let Some(token_url) = zitadel.token_url { - if self.zitadel.token_url.is_some() { - _ = self.zitadel.token_url.replace(token_url); - } - } } self diff --git a/crates/nefarious-login/src/oauth/zitadel.rs b/crates/nefarious-login/src/oauth/zitadel.rs index 4d3a1c6..81cdfdb 100644 --- a/crates/nefarious-login/src/oauth/zitadel.rs +++ b/crates/nefarious-login/src/oauth/zitadel.rs @@ -78,10 +78,14 @@ impl TryFrom for ZitadelConfig { type Error = anyhow::Error; fn try_from(value: ZitadelClap) -> Result { + let authority_url = value + .authority_url + .ok_or(anyhow::anyhow!("authority_url was not set"))?; + Ok(Self { - auth_url: value - .auth_url - .ok_or(anyhow::anyhow!("auth_url was not set"))?, + auth_url: format!("{}/oauth/v2/authorize", &authority_url), + token_url: format!("{}/oauth/v2/token", &authority_url), + authority_url, client_id: value .client_id .ok_or(anyhow::anyhow!("client_id was not set"))?, @@ -91,12 +95,6 @@ impl TryFrom for ZitadelConfig { redirect_url: value .redirect_url .ok_or(anyhow::anyhow!("redirect_url was not set"))?, - token_url: value - .token_url - .ok_or(anyhow::anyhow!("token_url was not set"))?, - authority_url: value - .authority_url - .ok_or(anyhow::anyhow!("authority_url was not set"))?, }) } } diff --git a/examples/basic/src/main.rs b/examples/basic/src/main.rs index 1c7abe6..ad6f86d 100644 --- a/examples/basic/src/main.rs +++ b/examples/basic/src/main.rs @@ -9,7 +9,15 @@ use axum::{ use nefarious_login::{ auth::AuthService, axum::{AuthController, UserFromSession}, + introspection::IntrospectionService, + login::{ + config::{AuthEngine, ZitadelClap}, + AuthClap, + }, + oauth::OAuth, + session::{PostgresqlSessionClap, SessionBackend, SessionService}, }; +use tracing_subscriber::EnvFilter; #[derive(Clone)] struct AppState { @@ -18,10 +26,33 @@ struct AppState { #[tokio::main] async fn main() -> anyhow::Result<()> { - tracing_subscriber::fmt().init(); + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .init(); - // Change to zitadel test instance - let auth_service = AuthService::new_noop(); + let auth = AuthClap { + engine: AuthEngine::Zitadel, + session_backend: SessionBackend::Postgresql, + zitadel: ZitadelClap { + authority_url: Some("https://personal-wxuujs.zitadel.cloud".into()), + client_id: Some("237412977047895154@nefarious-test".into()), + client_secret: Some( + "rWwDi8gjNOyuMFKoOjNSlhjcVZ1B25wDh6HsDL27f0g2Hb0xGbvEf0WXFY2akOlL".into(), + ), + redirect_url: Some("http://localhost:3001/auth/authorized".into()), + }, + session: nefarious_login::session::SessionClap { + postgresql: PostgresqlSessionClap { + conn: Some("postgres://como:somenotverysecurepassword@localhost:5432/como".into()), + }, + }, + }; + + let auth_service = AuthService::new_zitadel( + OAuth::try_from(auth.clone())?, + IntrospectionService::new_zitadel(&auth).await?, + SessionService::new(&auth).await?, + ); let state = AppState { auth: auth_service.clone(), @@ -35,6 +66,7 @@ async fn main() -> anyhow::Result<()> { let addr = SocketAddr::from(([127, 0, 0, 1], 3001)); println!("listening on: {addr}"); + println!("open browser at: http://localhost:3001/auth/zitadel"); axum::Server::bind(&addr) .serve(app.into_make_service()) .await diff --git a/examples/clap/Cargo.toml b/examples/clap/Cargo.toml new file mode 100644 index 0000000..3fafe7f --- /dev/null +++ b/examples/clap/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "clap" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nefarious-login.workspace = true + +tokio.workspace = true +anyhow.workspace = true +axum.workspace = true +clap.workspace = true + +tracing-subscriber.workspace = true diff --git a/examples/clap/src/main.rs b/examples/clap/src/main.rs new file mode 100644 index 0000000..1ed2a2f --- /dev/null +++ b/examples/clap/src/main.rs @@ -0,0 +1,88 @@ +use std::net::SocketAddr; + +use axum::{ + extract::{FromRef, State}, + response::IntoResponse, + routing::get, + Router, +}; +use clap::Parser; +use nefarious_login::{ + auth::AuthService, + axum::{AuthController, UserFromSession}, + login::AuthClap, + session::SessionService, +}; +use tracing_subscriber::EnvFilter; + +#[derive(Clone)] +struct AppState { + auth: AuthService, +} + +#[derive(Debug, Clone, Parser)] +struct Command { + #[clap(flatten)] + auth: AuthClap, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .init(); + + let cmd = Command::parse_from(vec![ + "base", + "--auth-engine=zitadel", + "--zitadel-authority-url=https://personal-wxuujs.zitadel.cloud", + "--zitadel-redirect-url=http://localhost:3001/auth/authorized", + "--zitadel-client-id=237412977047895154@nefarious-test", + "--zitadel-client-secret=rWwDi8gjNOyuMFKoOjNSlhjcVZ1B25wDh6HsDL27f0g2Hb0xGbvEf0WXFY2akOlL", + "--session-backend=postgresql", + "--session-postgres-conn=postgres://como:somenotverysecurepassword@localhost:5432/como", + ]); + + let auth = cmd.auth; + + let session_service = SessionService::new(&auth).await?; + let auth_service = AuthService::new(&auth, session_service).await?; + + let state = AppState { + auth: auth_service.clone(), + }; + + let app = Router::new() + .route("/unauthed", get(unauthed)) + .route("/authed", get(authed)) + .with_state(state) + .nest("/auth", AuthController::new_router(auth_service).await?); + + let addr = SocketAddr::from(([127, 0, 0, 1], 3001)); + println!("listening on: {addr}"); + println!("open browser at: http://localhost:3001/auth/zitadel"); + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await + .unwrap(); + + Ok(()) +} + +impl FromRef for AuthService { + fn from_ref(input: &AppState) -> Self { + input.auth.clone() + } +} + +async fn unauthed() -> String { + "Hello Unauthorized User".into() +} + +#[axum::debug_handler()] +async fn authed( + user: UserFromSession, + State(_auth_service): State, +) -> impl IntoResponse { + format!("Hello authorized user: {:?}", user.user.id) +}