diff options
| author | rtkay123 <dev@kanjala.com> | 2026-04-10 23:48:24 +0200 |
|---|---|---|
| committer | rtkay123 <dev@kanjala.com> | 2026-04-10 23:48:24 +0200 |
| commit | f06288f156ccb8f9ebf35782a179bf57e6bc8fc2 (patch) | |
| tree | 2e9eb80237094d930b4f3a54261fac0cb3350129 /crates/api-auth/src/discord/mod.rs | |
| parent | be2af8a5fe2e58953b4970e3fed970165fc4b4ca (diff) | |
| download | sellershut-f06288f156ccb8f9ebf35782a179bf57e6bc8fc2.tar.bz2 sellershut-f06288f156ccb8f9ebf35782a179bf57e6bc8fc2.zip | |
feat(auth): get user
Diffstat (limited to 'crates/api-auth/src/discord/mod.rs')
| -rw-r--r-- | crates/api-auth/src/discord/mod.rs | 77 |
1 files changed, 71 insertions, 6 deletions
diff --git a/crates/api-auth/src/discord/mod.rs b/crates/api-auth/src/discord/mod.rs index 1a7d47d..0844f58 100644 --- a/crates/api-auth/src/discord/mod.rs +++ b/crates/api-auth/src/discord/mod.rs @@ -1,12 +1,31 @@ use api_core::models::user::User; use async_session::{Session, serde_json}; use async_trait::async_trait; -use oauth2::{CsrfToken, Scope}; +use oauth2::{AuthorizationCode, CsrfToken, Scope, TokenResponse}; use redis::AsyncCommands; +use serde::{Deserialize, Serialize}; use sh_util::cache::{CacheKey, RedisManager}; use sqlx::PgPool; -use crate::{BasicClient, CSRF_TOKEN, OauthDriver, SessionResponse, error::AuthError}; +use crate::{ + BasicClient, CSRF_TOKEN, OauthDriver, SessionResponse, client::AuthHttpClient, error::AuthError, +}; + +// The user data we'll get back from Discord. +// https://discord.com/developers/docs/resources/user#user-object-user-structure +#[derive(Debug, Serialize, Deserialize)] +struct DiscordUser { + id: String, + avatar: Option<String>, + username: String, + discriminator: String, +} + +impl From<DiscordUser> for User { + fn from(value: DiscordUser) -> Self { + todo!() + } +} #[derive(Clone)] pub struct AuthServiceDiscord { @@ -27,11 +46,57 @@ impl AuthServiceDiscord { #[async_trait] impl OauthDriver for AuthServiceDiscord { - async fn get_auth_token(&self) -> Result<String, AuthError> { - todo!() + async fn get_user(&self, client: &AuthHttpClient, code: &str) -> Result<User, AuthError> { + // Get an auth token + let token = self + .client + .exchange_code(AuthorizationCode::new(code.to_owned())) + .request_async(client) + .await + .unwrap(); + // Fetch user data from discord + let user_data: DiscordUser = 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 + .unwrap() + .json::<DiscordUser>() + .await + .unwrap(); + + Ok(user_data.into()) } - async fn get_user(&self) -> Result<User, AuthError> { - todo!() + async fn validate_session(&self, cookie: &str, state: &str) -> Result<(), AuthError> { + let id = Session::id_from_cookie_value(cookie)?; + let cache_key = CacheKey::Session(&id); + let mut cache = self.cache.get().await.unwrap(); + let session = cache.get::<_, String>(&cache_key).await?; + let session: Session = + serde_json::from_str(&session).map_err(|_e| AuthError::InvalidSession)?; + + match session.validate() { + Some(session) => { + // Extract the CSRF token from the session + let stored_csrf_token = session.get::<CsrfToken>(CSRF_TOKEN); + + if let Some(stored) = stored_csrf_token { + // Cleanup the CSRF token session + cache.del::<_, ()>(cache_key).await?; + + // Validate CSRF token is the same as the one in the auth request + if *stored.secret() != state { + return Err(AuthError::TokenMismatch); + } else { + return Ok(()); + } + } else { + return Err(AuthError::NoCSRFToken); + } + } + None => return Err(AuthError::MissingSession), + } } async fn create_oauth_session(&self) -> Result<SessionResponse, AuthError> { let (auth_url, csrf_token) = self |
