use std::str::FromStr; use jsonwebtoken::DecodingKey; use sellershut_core::{ auth::{ GetPrivateKeyRequest, GetPrivateKeyResponse, RegisterUserRequest, RegisterUserResponse, ValidationRequest, ValidationResponse, auth_server::Auth, }, users::CreateUserRequest, }; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use tonic::{Request, Response, Status, async_trait}; use tower_sessions::{SessionStore, session::Id}; use tracing::{error, warn}; use uuid::Uuid; use crate::{auth::Claims, server::keys::generate_actor_keypair, state::AppHandle}; #[derive(Debug, Deserialize, Serialize)] pub struct DbUser { pub id: Uuid, pub email: String, pub private_key: String, pub created_at: OffsetDateTime, pub updated_at: OffsetDateTime, } #[async_trait] impl Auth for AppHandle { async fn validate_auth_token( &self, request: Request, ) -> Result, Status> { let token = request.into_inner().token; let token = jsonwebtoken::decode::( &token, &DecodingKey::from_secret(self.local_config.oauth.jwt_encoding_key.as_bytes()), &jsonwebtoken::Validation::default(), ); match token { Ok(value) => { let session_id = value.claims.sid; let store = &self.session_store; match Id::from_str(&session_id) { Ok(ref id) => { if let Ok(Some(_)) = store.load(id).await { return Ok(Response::new(ValidationResponse { valid: true })); } else { return Ok(Response::new(Default::default())); } } Err(e) => { warn!("{e}"); return Ok(Response::new(Default::default())); } } } Err(e) => { warn!("{e}"); Ok(Response::new(ValidationResponse::default())) } } } async fn register_user( &self, request: Request, ) -> Result, Status> { let keys = generate_actor_keypair() .map_err(|_e| Status::internal("keys could not be generated"))?; let uuid = Uuid::now_v7(); let mut transaction = self .services .postgres .begin() .await .inspect_err(|e| error!("{e}")) .map_err(|_| Status::internal("db error"))?; let user_data = request.into_inner(); let user = sqlx::query_as!( DbUser, "insert into auth_user (id, email, private_key) values ($1, $2, $3) on conflict (email) do update set email = excluded.email returning *; ", uuid, user_data.email, keys.private_key, ) .fetch_one(&mut *transaction) .await .unwrap(); if let Some(ref account) = user_data.account { sqlx::query_as!( DbUser, "with upsert as ( insert into oauth_account (provider_id, provider_user_id, user_id) values ($1, $2, $3) on conflict (provider_id, provider_user_id) do update set provider_id = excluded.provider_id -- no-op returning user_id ) select u.* from upsert join auth_user u on u.id = upsert.user_id; ", account.provider_id, account.provider_user_id, user.id ) .fetch_one(&mut *transaction) .await .unwrap(); } let user_request = CreateUserRequest { email: user_data.email.to_owned(), public_key: keys.public_key, avatar: None, }; let mut profile_client = self.users_client.clone(); let resp = profile_client.create_user(user_request).await?.into_inner(); transaction.commit().await.unwrap(); let user_id = resp.temp_id; Ok(Response::new(RegisterUserResponse { profile_id: user_id, auth_id: user.id.to_string(), })) } async fn get_private_key( &self, request: Request, ) -> Result, Status> { let email = request.into_inner().email; let private_key = sqlx::query_scalar!( "select private_key from auth_user where email = $1 ", uuid, ) .fetch_one(&self.services.postgres) .await .unwrap(); Ok(Response::new(GetPrivateKeyResponse { private_key })) } }