feat: change to more complete token instead of just id
All checks were successful
ci/woodpecker/pr/test Pipeline was successful
All checks were successful
ci/woodpecker/pr/test Pipeline was successful
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
74362f3b1c
commit
70915aec65
@ -6,7 +6,7 @@ use axum::http::{header::SET_COOKIE, HeaderMap};
|
|||||||
use oauth2::url::Url;
|
use oauth2::url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
introspection::IntrospectionService,
|
introspection::{IdToken, IntrospectionService},
|
||||||
login::{auth_clap::AuthEngine, config::ConfigClap, AuthClap},
|
login::{auth_clap::AuthEngine, config::ConfigClap, AuthClap},
|
||||||
oauth::{zitadel::ZitadelConfig, OAuth},
|
oauth::{zitadel::ZitadelConfig, OAuth},
|
||||||
session::{SessionService, User},
|
session::{SessionService, User},
|
||||||
@ -15,7 +15,7 @@ use crate::{
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Auth {
|
pub trait Auth {
|
||||||
async fn login(&self) -> anyhow::Result<Url>;
|
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 login_authorized(&self, code: &str, state: &str) -> anyhow::Result<(HeaderMap, Url)>;
|
||||||
async fn get_user_from_session(&self, cookie: &str) -> anyhow::Result<User>;
|
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)> {
|
async fn login_authorized(&self, code: &str, _state: &str) -> anyhow::Result<(HeaderMap, Url)> {
|
||||||
let token = self.oauth.exchange(code).await?;
|
let token = self.oauth.exchange(code).await?;
|
||||||
let user_id = self.introspection.get_id_token(token.as_str()).await?;
|
let id_token = self.introspection.get_id_token(token.as_str()).await?;
|
||||||
let cookie_value = self.session.insert_user("user", user_id.as_str()).await?;
|
let cookie_value = self.session.insert_user("user", id_token).await?;
|
||||||
|
|
||||||
let cookie = format!("{}={}; SameSite=Lax; Path=/", COOKIE_NAME, cookie_value);
|
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")?,
|
.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
|
self.introspection.get_id_token(password).await
|
||||||
}
|
}
|
||||||
async fn get_user_from_session(&self, cookie: &str) -> anyhow::Result<User> {
|
async fn get_user_from_session(&self, cookie: &str) -> anyhow::Result<User> {
|
||||||
match self.session.get_user(cookie).await? {
|
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")),
|
None => Err(anyhow::anyhow!("failed to find user")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ impl Auth for NoopAuthService {
|
|||||||
todo!()
|
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!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,11 @@ where
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
return Ok(UserFromSession {
|
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 {
|
Ok(UserFromSession { user })
|
||||||
user: User { id: user.id },
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ use async_trait::async_trait;
|
|||||||
use axum::extract::FromRef;
|
use axum::extract::FromRef;
|
||||||
use oauth2::TokenIntrospectionResponse;
|
use oauth2::TokenIntrospectionResponse;
|
||||||
use openidconnect::IntrospectionUrl;
|
use openidconnect::IntrospectionUrl;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use zitadel::{
|
use zitadel::{
|
||||||
axum::introspection::IntrospectionStateBuilderError,
|
axum::introspection::IntrospectionStateBuilderError,
|
||||||
credentials::Application,
|
credentials::Application,
|
||||||
@ -15,10 +16,17 @@ use zitadel::{
|
|||||||
|
|
||||||
use crate::login::AuthClap;
|
use crate::login::AuthClap;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct IdToken {
|
||||||
|
pub sub: String,
|
||||||
|
pub email: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Introspection {
|
pub trait Introspection {
|
||||||
async fn get_user(&self) -> anyhow::Result<()>;
|
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>);
|
pub struct IntrospectionService(Arc<dyn Introspection + Send + Sync + 'static>);
|
||||||
@ -61,7 +69,7 @@ impl Introspection for ZitadelIntrospection {
|
|||||||
async fn get_user(&self) -> anyhow::Result<()> {
|
async fn get_user(&self) -> anyhow::Result<()> {
|
||||||
Ok(())
|
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 config = &self.state.config;
|
||||||
let res = introspect(
|
let res = introspect(
|
||||||
&config.introspection_uri,
|
&config.introspection_uri,
|
||||||
@ -71,10 +79,21 @@ impl Introspection for ZitadelIntrospection {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(res
|
let sub = res
|
||||||
.sub()
|
.sub()
|
||||||
.ok_or(anyhow::anyhow!("could not find a userid (sub) in token"))?
|
.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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ use async_trait::async_trait;
|
|||||||
use axum_sessions::async_session::{Session as AxumSession, SessionStore as AxumSessionStore};
|
use axum_sessions::async_session::{Session as AxumSession, SessionStore as AxumSessionStore};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::login::AuthClap;
|
use crate::{introspection::IdToken, login::AuthClap};
|
||||||
|
|
||||||
#[derive(clap::Args, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(clap::Args, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct SessionClap {
|
pub struct SessionClap {
|
||||||
@ -27,8 +27,8 @@ pub struct PostgresqlSessionClap {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Session {
|
pub trait Session {
|
||||||
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>;
|
||||||
async fn get_user(&self, cookie: &str) -> anyhow::Result<Option<String>>;
|
async fn get_user(&self, cookie: &str) -> anyhow::Result<Option<User>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SessionService(Arc<dyn Session + Send + Sync + 'static>);
|
pub struct SessionService(Arc<dyn Session + Send + Sync + 'static>);
|
||||||
@ -73,16 +73,20 @@ pub struct PostgresSessionService {
|
|||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
pub email: String,
|
||||||
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Session for PostgresSessionService {
|
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();
|
let mut session = AxumSession::new();
|
||||||
session.insert(
|
session.insert(
|
||||||
"user",
|
"user",
|
||||||
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)
|
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(session) = self.store.load_session(cookie.to_string()).await.unwrap() {
|
||||||
if let Some(user) = session.get::<User>("user") {
|
if let Some(user) = session.get::<User>("user") {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"UserFromSession: session decoded success, user_id={:?}",
|
"UserFromSession: session decoded success, user_id={:?}",
|
||||||
user.id
|
user.id
|
||||||
);
|
);
|
||||||
Ok(Some(user.id))
|
Ok(Some(user))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -119,11 +123,11 @@ pub struct InMemorySessionService {}
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Session for InMemorySessionService {
|
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!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user(&self, _cookie: &str) -> anyhow::Result<Option<String>> {
|
async fn get_user(&self, _cookie: &str) -> anyhow::Result<Option<User>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user