From e26d87f4fa18999c6bcfbcf32cfa85adab11acdd Mon Sep 17 00:00:00 2001 From: rtkay123 Date: Sat, 26 Jul 2025 19:24:38 +0200 Subject: feat(auth): create user call --- crates/auth/src/server/routes/authorised.rs | 232 ---------------------------- 1 file changed, 232 deletions(-) delete mode 100644 crates/auth/src/server/routes/authorised.rs (limited to 'crates/auth/src/server/routes/authorised.rs') diff --git a/crates/auth/src/server/routes/authorised.rs b/crates/auth/src/server/routes/authorised.rs deleted file mode 100644 index 32dd929..0000000 --- a/crates/auth/src/server/routes/authorised.rs +++ /dev/null @@ -1,232 +0,0 @@ -use std::{str::FromStr, time::Duration}; - -use anyhow::Context; -use axum::{ - extract::{Query, State}, - http::HeaderMap, - response::{IntoResponse, Redirect}, -}; -use axum_extra::{TypedHeader, headers}; -use oauth2::{AuthorizationCode, TokenResponse}; -use reqwest::{StatusCode, header::SET_COOKIE}; -use sellershut_core::profile::CreateUserRequest; -use serde::{Deserialize, Serialize}; -use sqlx::types::uuid; -use time::OffsetDateTime; -use tower_sessions::{ - SessionStore, - session::{Id, Record}, -}; -use uuid::Uuid; - -use crate::{ - auth::Claims, - error::AppError, - server::{ - OAUTH_CSRF_COOKIE, csrf_token_validation::csrf_token_validation_workflow, routes::Provider, - }, - state::AppHandle, -}; - -#[derive(Debug, Deserialize)] -pub struct AuthRequest { - provider: Provider, - code: String, - pub state: String, -} - -#[derive(Debug, Deserialize, Serialize)] -struct User { - id: String, - avatar: Option, - username: String, - discriminator: String, - verified: bool, - email: String, -} - -#[derive(Debug, Deserialize, Serialize)] -struct DbUser { - id: Uuid, - email: 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"; - -pub async fn login_authorised( - Query(query): Query, - State(state): State, - TypedHeader(cookies): TypedHeader, -) -> Result { - let provider = query.provider.to_string(); - let oauth_session_id = Id::from_str( - cookies - .get(OAUTH_CSRF_COOKIE) - .context("missing session cookie")?, - )?; - csrf_token_validation_workflow(&query, &state.session_store, oauth_session_id).await?; - - let client = state.http_client.clone(); - let store = state.session_store.clone(); - - // Get an auth token - let token = state - .discord_client - .exchange_code(AuthorizationCode::new(query.code.clone())) - .request_async(&client) - .await - .context("failed in sending request request to authorisation server")?; - - let user_data = client - // https://discord.com/developers/docs/resources/user#get-current-user - .get("https://discordapp.com/api/users/@me") - .bearer_auth(token.access_token().secret()) - .send() - .await - .context("failed in sending request to target Url")? - .json::() - .await - .context("failed to deserialise response as JSON")?; - - dbg!(&user_data); - - let user_data: User = serde_json::from_value(user_data)?; - - if !user_data.verified { - return Ok((StatusCode::UNAUTHORIZED, "email is not verified").into_response()); - } - - // 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, - " - select - p.* - from - auth_user p - inner join - oauth_account a - on - p.id=a.user_id - where a.provider_id = $1 and a.provider_user_id = $2 - ", - provider, - user_data.id - ) - .fetch_optional(&mut *transaction) - .await?; - - let user = if let Some(user) = user { - user - } else { - let uuid = uuid::Uuid::now_v7(); - let user = sqlx::query_as!( - DbUser, - "insert into auth_user (id, email) values ($1, $2) - on conflict (email) do update - set email = excluded.email - returning *; - ", - uuid, - user_data.email, - ) - .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 exp = OffsetDateTime::now_utc() + Duration::from_secs(15 * 60); - - let claims = Claims { - sub: user.id, - exp: exp.unix_timestamp(), - iss: "sellershut".to_owned(), - sid: session_id.to_string(), - aud: "sellershut".to_owned(), - iat: OffsetDateTime::now_utc().unix_timestamp(), - }; - - let token = jsonwebtoken::encode( - &jsonwebtoken::Header::default(), - &claims, - &jsonwebtoken::EncodingKey::from_secret( - state.local_config.oauth.jwt_encoding_key.as_bytes(), - ), - )?; - - 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 - ) - }) - }; - - - store - .create(&mut Record { - id: session_id, - data: [( - SESSION_DATA_KEY.to_string(), - serde_json::to_value(user_data).unwrap(), - )] - .into(), - expiry_date: time::OffsetDateTime::now_utc() - + Duration::from_secs(state.local_config.oauth.session_lifespan), - }) - .await - .context("failed in inserting serialised value into session")?; - - sqlx::query!( - "insert into token (user_id, token, session_id) values ($1, $2, $3)", - user.id, - token, - session_id.to_string() - ) - .execute(&mut *transaction) - .await?; - - let cookie = format!("{SESSION_COOKIE}={session_id}; SameSite=Lax; HttpOnly; Secure; Path=/"); - - let mut profile_client = state.profile_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()) -} -- cgit v1.2.3