feat: change to more complete token instead of just id
All checks were successful
ci/woodpecker/pr/test Pipeline was successful

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2023-11-04 17:32:51 +01:00
parent 74362f3b1c
commit 70915aec65
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
4 changed files with 49 additions and 24 deletions

View File

@ -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<Url>;
async fn login_token(&self, user: &str, password: &str) -> anyhow::Result<String>;
async fn login_token(&self, user: &str, password: &str) -> anyhow::Result<IdToken>;
async fn login_authorized(&self, code: &str, state: &str) -> anyhow::Result<(HeaderMap, Url)>;
async fn get_user_from_session(&self, cookie: &str) -> anyhow::Result<User>;
}
@ -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<String> {
async fn login_token(&self, _user: &str, password: &str) -> anyhow::Result<IdToken> {
self.introspection.get_id_token(password).await
}
async fn get_user_from_session(&self, cookie: &str) -> anyhow::Result<User> {
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<String> {
async fn login_token(&self, _user: &str, _password: &str) -> anyhow::Result<IdToken> {
todo!()
}

View File

@ -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 })
}
}

View File

@ -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<String>;
async fn get_id_token(&self, token: &str) -> anyhow::Result<IdToken>;
}
pub struct IntrospectionService(Arc<dyn Introspection + Send + Sync + 'static>);
@ -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<String> {
async fn get_id_token(&self, token: &str) -> anyhow::Result<IdToken> {
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 })
}
}

View File

@ -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<String>;
async fn get_user(&self, cookie: &str) -> anyhow::Result<Option<String>>;
async fn insert_user(&self, id: &str, id_token: IdToken) -> anyhow::Result<String>;
async fn get_user(&self, cookie: &str) -> anyhow::Result<Option<User>>;
}
pub struct SessionService(Arc<dyn Session + Send + Sync + 'static>);
@ -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<String> {
async fn insert_user(&self, _id: &str, id_token: IdToken) -> anyhow::Result<String> {
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<Option<String>> {
async fn get_user(&self, cookie: &str) -> anyhow::Result<Option<User>> {
if let Some(session) = self.store.load_session(cookie.to_string()).await.unwrap() {
if let Some(user) = session.get::<User>("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<String> {
async fn insert_user(&self, _id: &str, _id_token: IdToken) -> anyhow::Result<String> {
todo!()
}
async fn get_user(&self, _cookie: &str) -> anyhow::Result<Option<String>> {
async fn get_user(&self, _cookie: &str) -> anyhow::Result<Option<User>> {
todo!()
}
}