summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrtkay123 <dev@kanjala.com>2025-08-02 13:15:22 +0200
committerrtkay123 <dev@kanjala.com>2025-08-02 13:15:22 +0200
commit9d9e800664503b1e2051ee4e05d83a880e63c6f4 (patch)
treeb0fa9f8a265d5fd812c3d9d17ff8ce8a82ced73d
parent259cad06f8d88db9ddfa85c2c188b5b0130cb393 (diff)
downloadsellershut-9d9e800664503b1e2051ee4e05d83a880e63c6f4.tar.bz2
sellershut-9d9e800664503b1e2051ee4e05d83a880e63c6f4.zip
feat(auth): register profile
-rw-r--r--crates/auth-service/src/server.rs2
-rw-r--r--crates/auth-service/src/server/grpc/auth.rs95
-rw-r--r--crates/auth-service/src/server/keys.rs3
-rw-r--r--crates/auth-service/src/server/routes/authorised.rs103
-rw-r--r--crates/sellershut/src/state.rs10
-rw-r--r--lib/sellershut-core/proto/auth/auth.proto34
6 files changed, 163 insertions, 84 deletions
diff --git a/crates/auth-service/src/server.rs b/crates/auth-service/src/server.rs
index 3433cd2..39a5223 100644
--- a/crates/auth-service/src/server.rs
+++ b/crates/auth-service/src/server.rs
@@ -7,8 +7,8 @@ use crate::{
};
pub mod csrf_token_validation;
-pub mod keys;
pub mod grpc;
+pub mod keys;
pub mod routes;
const CSRF_TOKEN: &str = "csrf_token";
diff --git a/crates/auth-service/src/server/grpc/auth.rs b/crates/auth-service/src/server/grpc/auth.rs
index fb00291..87113a5 100644
--- a/crates/auth-service/src/server/grpc/auth.rs
+++ b/crates/auth-service/src/server/grpc/auth.rs
@@ -1,12 +1,30 @@
use std::str::FromStr;
use jsonwebtoken::DecodingKey;
-use sellershut_core::auth::{ValidationRequest, ValidationResponse, auth_server::Auth};
+use sellershut_core::{
+ auth::{
+ 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::warn;
+use tracing::{error, warn};
+use uuid::Uuid;
-use crate::{auth::Claims, state::AppHandle};
+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 {
@@ -47,4 +65,75 @@ impl Auth for AppHandle {
}
}
}
+
+ async fn register_user(
+ &self,
+ request: Request<RegisterUserRequest>,
+ ) -> Result<Response<RegisterUserResponse>, 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(),
+ }))
+ }
}
diff --git a/crates/auth-service/src/server/keys.rs b/crates/auth-service/src/server/keys.rs
index 5c9ee43..d11463e 100644
--- a/crates/auth-service/src/server/keys.rs
+++ b/crates/auth-service/src/server/keys.rs
@@ -1,7 +1,6 @@
use rsa::{
+ RsaPrivateKey, RsaPublicKey,
pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding},
- RsaPrivateKey,
- RsaPublicKey,
};
use crate::error::AppError;
diff --git a/crates/auth-service/src/server/routes/authorised.rs b/crates/auth-service/src/server/routes/authorised.rs
index b4c2e00..9b97cd2 100644
--- a/crates/auth-service/src/server/routes/authorised.rs
+++ b/crates/auth-service/src/server/routes/authorised.rs
@@ -9,10 +9,14 @@ use axum::{
use axum_extra::{TypedHeader, headers};
use oauth2::{AuthorizationCode, TokenResponse};
use reqwest::{StatusCode, header::SET_COOKIE};
-use sellershut_core::users::CreateUserRequest;
+use sellershut_core::{
+ auth::{RegisterUserRequest, auth_server::Auth, register_user_request::AccountDetails},
+ users::CreateUserRequest,
+};
use serde::{Deserialize, Serialize};
use sqlx::types::uuid;
use time::OffsetDateTime;
+use tonic::IntoRequest;
use tower_sessions::{
SessionStore,
session::{Id, Record},
@@ -23,7 +27,8 @@ use crate::{
auth::Claims,
error::AppError,
server::{
- csrf_token_validation::csrf_token_validation_workflow, keys::generate_actor_keypair, routes::Provider, OAUTH_CSRF_COOKIE
+ OAUTH_CSRF_COOKIE, csrf_token_validation::csrf_token_validation_workflow,
+ grpc::auth::DbUser, keys::generate_actor_keypair, routes::Provider,
},
state::AppHandle,
};
@@ -35,7 +40,7 @@ pub struct AuthRequest {
pub state: String,
}
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Debug, Deserialize, Serialize, Clone)]
struct User {
id: String,
avatar: Option<String>,
@@ -45,15 +50,6 @@ struct User {
email: String,
}
-#[derive(Debug, Deserialize, Serialize)]
-struct DbUser {
- id: Uuid,
- email: String,
- private_key: String,
- created_at: OffsetDateTime,
- updated_at: OffsetDateTime,
-}
-
/// The cookie to store the session id for user information.
const SESSION_COOKIE: &str = "info";
const SESSION_DATA_KEY: &str = "data";
@@ -102,8 +98,6 @@ pub async fn login_authorised(
// Create a new session filled with user data
let session_id = Id(i128::from_le_bytes(uuid::Uuid::new_v4().to_bytes_le()));
- let mut transaction = state.services.postgres.begin().await?;
-
let user = sqlx::query_as!(
DbUser,
"
@@ -120,53 +114,29 @@ pub async fn login_authorised(
provider,
user_data.id
)
- .fetch_optional(&mut *transaction)
+ .fetch_optional(&state.services.postgres)
.await?;
- let keys = generate_actor_keypair()?;
-
let user = if let Some(user) = user {
- user
+ user.id
} else {
- let uuid = uuid::Uuid::now_v7();
- 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?;
-
- 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;
- ",
- provider,
- user_data.id,
- user.id
- )
- .fetch_one(&mut *transaction)
- .await?
+ let data = user_data.clone();
+ let request = RegisterUserRequest {
+ email: data.email,
+ account: Some(AccountDetails {
+ provider_id: provider,
+ provider_user_id: data.id,
+ })
+ };
+
+ let resp = state.register_user(request.into_request()).await.unwrap().into_inner();
+ Uuid::parse_str(&resp.auth_id).unwrap()
};
let exp = OffsetDateTime::now_utc() + Duration::from_secs(15 * 60);
let claims = Claims {
- sub: user.id,
+ sub: user,
exp: exp.unix_timestamp(),
iss: "sellershut".to_owned(),
sid: session_id.to_string(),
@@ -182,18 +152,6 @@ pub async fn login_authorised(
),
)?;
-
- let user_request = CreateUserRequest {
- email: user_data.email.to_owned(),
- avatar: user_data.avatar.as_ref().map(|value| {
- format!(
- "https://cdn.discordapp.com/avatars/{}/{value}",
- user_data.id
- )
- }),
- public_key: keys.public_key,
- };
-
store
.create(&mut Record {
id: session_id,
@@ -210,31 +168,20 @@ pub async fn login_authorised(
sqlx::query!(
"insert into token (user_id, token, session_id) values ($1, $2, $3)",
- user.id,
+ user,
token,
session_id.to_string()
)
- .execute(&mut *transaction)
+ .execute(&state.services.postgres)
.await?;
let cookie = format!("{SESSION_COOKIE}={session_id}; SameSite=Lax; HttpOnly; Secure; Path=/");
- let mut profile_client = state.users_client.clone();
- let resp = profile_client.create_user(user_request).await?.into_inner();
-
- let user_id = resp.temp_id;
-
let mut headers = HeaderMap::new();
headers.insert(
SET_COOKIE,
cookie.parse().context("failed to parse cookie")?,
);
- transaction.commit().await?;
-
- Ok((
- headers,
- Redirect::to(&format!("/?user={user_id}&token={token}")),
- )
- .into_response())
+ Ok((headers, Redirect::to(&format!("/?token={token}"))).into_response())
}
diff --git a/crates/sellershut/src/state.rs b/crates/sellershut/src/state.rs
index 6c4822d..cc58507 100644
--- a/crates/sellershut/src/state.rs
+++ b/crates/sellershut/src/state.rs
@@ -44,6 +44,16 @@ impl AppState {
) -> Result<FederationConfig<AppHandle>, AppError> {
let hut_config: LocalConfig = serde_json::from_value(configuration.misc.clone())?;
+ let user_id = &format!(
+ "{}://{}/users/{}",
+ match configuration.application.env {
+ Environment::Development => "http",
+ Environment::Production => "https",
+ },
+ hut_config.hostname,
+ hut_config.instance_name
+ );
+
let user = User::new(
&hut_config.instance_name,
&hut_config.hostname,
diff --git a/lib/sellershut-core/proto/auth/auth.proto b/lib/sellershut-core/proto/auth/auth.proto
index 5e340b3..65792bf 100644
--- a/lib/sellershut-core/proto/auth/auth.proto
+++ b/lib/sellershut-core/proto/auth/auth.proto
@@ -14,6 +14,17 @@ message User {
google.protobuf.Timestamp created_at = 3;
// Timestamp for when the user was last updated
google.protobuf.Timestamp updated_at = 4;
+ // Private key
+ string private_key = 5;
+}
+
+message Account {
+ // Unique identifier for the user
+ string provider_id = 1;
+ // Email address of the user
+ string provider_user_id = 2;
+ // Email address of the user
+ string user_id = 3;
}
// Define a message for sending a token to be validated
@@ -28,8 +39,31 @@ message ValidationResponse {
bool valid = 1;
}
+// Define a message for the result of a token validation
+message RegisterUserResponse {
+ // Indicates whether the token is valid
+ string profile_id = 1;
+ // Indicates whether the token is valid
+ string auth_id = 2;
+}
+
+// Define a message for the result of a token validation
+message RegisterUserRequest {
+ // Indicates whether the token is valid
+ string email = 1;
+ message AccountDetails {
+ // Unique identifier for the user
+ string provider_id = 1;
+ // Email address of the user
+ string provider_user_id = 2;
+ }
+ optional AccountDetails account = 2;
+}
+
// Define the AuthServer gRPC service
service Auth {
// Validate a token
rpc ValidateAuthToken (ValidationRequest) returns (ValidationResponse);
+ // Register User
+ rpc RegisterUser (RegisterUserRequest) returns (RegisterUserResponse);
}