From 70915aec652d072554b3f7e49034007d36d34fb1 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Sat, 4 Nov 2023 17:32:51 +0100 Subject: [PATCH] feat: change to more complete token instead of just id Signed-off-by: kjuulh --- crates/nefarious-login/src/auth.rs | 14 +++++------ crates/nefarious-login/src/axum.rs | 10 +++++--- crates/nefarious-login/src/introspection.rs | 27 ++++++++++++++++++--- crates/nefarious-login/src/session.rs | 22 ++++++++++------- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/crates/nefarious-login/src/auth.rs b/crates/nefarious-login/src/auth.rs index 024980b..fbf7793 100644 --- a/crates/nefarious-login/src/auth.rs +++ b/crates/nefarious-login/src/auth.rs @@ -6,7 +6,7 @@ use axum::http::{header::SET_COOKIE, HeaderMap}; use oauth2::url::Url; use crate::{ - introspection::IntrospectionService, + introspection::{IdToken, IntrospectionService}, login::{auth_clap::AuthEngine, config::ConfigClap, AuthClap}, oauth::{zitadel::ZitadelConfig, OAuth}, session::{SessionService, User}, @@ -15,7 +15,7 @@ use crate::{ #[async_trait] pub trait Auth { async fn login(&self) -> anyhow::Result; - async fn login_token(&self, user: &str, password: &str) -> anyhow::Result; + async fn login_token(&self, user: &str, password: &str) -> anyhow::Result; async fn login_authorized(&self, code: &str, state: &str) -> anyhow::Result<(HeaderMap, Url)>; async fn get_user_from_session(&self, cookie: &str) -> anyhow::Result; } @@ -86,8 +86,8 @@ impl Auth for ZitadelAuthService { } async fn login_authorized(&self, code: &str, _state: &str) -> anyhow::Result<(HeaderMap, Url)> { let token = self.oauth.exchange(code).await?; - let user_id = self.introspection.get_id_token(token.as_str()).await?; - let cookie_value = self.session.insert_user("user", user_id.as_str()).await?; + let id_token = self.introspection.get_id_token(token.as_str()).await?; + let cookie_value = self.session.insert_user("user", id_token).await?; let cookie = format!("{}={}; SameSite=Lax; Path=/", COOKIE_NAME, cookie_value); @@ -100,12 +100,12 @@ impl Auth for ZitadelAuthService { .context("failed to parse login_authorized zitadel return url")?, )) } - async fn login_token(&self, _user: &str, password: &str) -> anyhow::Result { + async fn login_token(&self, _user: &str, password: &str) -> anyhow::Result { self.introspection.get_id_token(password).await } async fn get_user_from_session(&self, cookie: &str) -> anyhow::Result { match self.session.get_user(cookie).await? { - Some(u) => Ok(User { id: u }), + Some(u) => Ok(u), None => Err(anyhow::anyhow!("failed to find user")), } } @@ -126,7 +126,7 @@ impl Auth for NoopAuthService { todo!() } - async fn login_token(&self, _user: &str, _password: &str) -> anyhow::Result { + async fn login_token(&self, _user: &str, _password: &str) -> anyhow::Result { todo!() } diff --git a/crates/nefarious-login/src/axum.rs b/crates/nefarious-login/src/axum.rs index 81d4762..73d0663 100644 --- a/crates/nefarious-login/src/axum.rs +++ b/crates/nefarious-login/src/axum.rs @@ -121,7 +121,11 @@ where })?; return Ok(UserFromSession { - user: User { id: token }, + user: User { + id: token.sub, + email: token.email, + name: token.name, + }, }); } @@ -144,8 +148,6 @@ where ) })?; - Ok(UserFromSession { - user: User { id: user.id }, - }) + Ok(UserFromSession { user }) } } diff --git a/crates/nefarious-login/src/introspection.rs b/crates/nefarious-login/src/introspection.rs index 04fab13..3c1a001 100644 --- a/crates/nefarious-login/src/introspection.rs +++ b/crates/nefarious-login/src/introspection.rs @@ -4,6 +4,7 @@ use async_trait::async_trait; use axum::extract::FromRef; use oauth2::TokenIntrospectionResponse; use openidconnect::IntrospectionUrl; +use serde::{Deserialize, Serialize}; use zitadel::{ axum::introspection::IntrospectionStateBuilderError, credentials::Application, @@ -15,10 +16,17 @@ use zitadel::{ use crate::login::AuthClap; +#[derive(Clone, Serialize, Deserialize)] +pub struct IdToken { + pub sub: String, + pub email: String, + pub name: String, +} + #[async_trait] pub trait Introspection { async fn get_user(&self) -> anyhow::Result<()>; - async fn get_id_token(&self, token: &str) -> anyhow::Result; + async fn get_id_token(&self, token: &str) -> anyhow::Result; } pub struct IntrospectionService(Arc); @@ -61,7 +69,7 @@ impl Introspection for ZitadelIntrospection { async fn get_user(&self) -> anyhow::Result<()> { Ok(()) } - async fn get_id_token(&self, token: &str) -> anyhow::Result { + async fn get_id_token(&self, token: &str) -> anyhow::Result { let config = &self.state.config; let res = introspect( &config.introspection_uri, @@ -71,10 +79,21 @@ impl Introspection for ZitadelIntrospection { ) .await?; - Ok(res + let sub = res .sub() .ok_or(anyhow::anyhow!("could not find a userid (sub) in token"))? - .to_string()) + .to_string(); + + let extra = res.extra_fields(); + let email = extra.email.clone().ok_or(anyhow::anyhow!( + "could not find a email (scope email) in token" + ))?; + + let name = extra.name.clone().ok_or(anyhow::anyhow!( + "could not find a name (scope profile) in token" + ))?; + + Ok(IdToken { sub, email, name }) } } diff --git a/crates/nefarious-login/src/session.rs b/crates/nefarious-login/src/session.rs index 7cd5ce4..1667bc1 100644 --- a/crates/nefarious-login/src/session.rs +++ b/crates/nefarious-login/src/session.rs @@ -11,7 +11,7 @@ use async_trait::async_trait; use axum_sessions::async_session::{Session as AxumSession, SessionStore as AxumSessionStore}; use serde::{Deserialize, Serialize}; -use crate::login::AuthClap; +use crate::{introspection::IdToken, login::AuthClap}; #[derive(clap::Args, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SessionClap { @@ -27,8 +27,8 @@ pub struct PostgresqlSessionClap { #[async_trait] pub trait Session { - async fn insert_user(&self, id: &str, user_id: &str) -> anyhow::Result; - async fn get_user(&self, cookie: &str) -> anyhow::Result>; + async fn insert_user(&self, id: &str, id_token: IdToken) -> anyhow::Result; + async fn get_user(&self, cookie: &str) -> anyhow::Result>; } pub struct SessionService(Arc); @@ -73,16 +73,20 @@ pub struct PostgresSessionService { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct User { pub id: String, + pub email: String, + pub name: String, } #[async_trait] impl Session for PostgresSessionService { - async fn insert_user(&self, _id: &str, user_id: &str) -> anyhow::Result { + async fn insert_user(&self, _id: &str, id_token: IdToken) -> anyhow::Result { let mut session = AxumSession::new(); session.insert( "user", User { - id: user_id.to_string(), + id: id_token.sub, + email: id_token.email, + name: id_token.name, }, )?; @@ -94,14 +98,14 @@ impl Session for PostgresSessionService { Ok(cookie) } - async fn get_user(&self, cookie: &str) -> anyhow::Result> { + async fn get_user(&self, cookie: &str) -> anyhow::Result> { if let Some(session) = self.store.load_session(cookie.to_string()).await.unwrap() { if let Some(user) = session.get::("user") { tracing::debug!( "UserFromSession: session decoded success, user_id={:?}", user.id ); - Ok(Some(user.id)) + Ok(Some(user)) } else { Ok(None) } @@ -119,11 +123,11 @@ pub struct InMemorySessionService {} #[async_trait] impl Session for InMemorySessionService { - async fn insert_user(&self, _id: &str, _user_id: &str) -> anyhow::Result { + async fn insert_user(&self, _id: &str, _id_token: IdToken) -> anyhow::Result { todo!() } - async fn get_user(&self, _cookie: &str) -> anyhow::Result> { + async fn get_user(&self, _cookie: &str) -> anyhow::Result> { todo!() } }