summaryrefslogtreecommitdiffstats
path: root/crates/auth-service/src/server/grpc/auth.rs
blob: 87113a5fd593877a2a2cd3685ae7b101c7c3adb8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::str::FromStr;

use jsonwebtoken::DecodingKey;
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::{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<ValidationRequest>,
    ) -> Result<Response<ValidationResponse>, Status> {
        let token = request.into_inner().token;

        let token = jsonwebtoken::decode::<Claims>(
            &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<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(),
        }))
    }
}