Skip to content

Commit

Permalink
Refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
CharlesTaylor7 committed May 13, 2024
1 parent e3e8748 commit 7183b09
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 77 deletions.
40 changes: 16 additions & 24 deletions src/server/auth.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,34 @@
use super::supabase::SignInResponse;
use crate::server::{state::AppState, supabase::EmailCreds};
use axum_extra::extract::{cookie::Cookie, PrivateCookieJar};
use std::collections::HashMap;
use std::borrow::Cow;
use std::{collections::HashMap, sync::Arc};

#[derive(Clone)]
pub struct Session {
pub user_id: String,
pub access_token: String,
pub refresh_token: String,
pub expires_in: u64,
pub access_token: Arc<str>,
pub refresh_token: Arc<str>,
}

impl Session {
pub fn new(json: SignInResponse) -> Self {
Self {
user_id: json.user.id,
access_token: json.access_token,
refresh_token: json.refresh_token,
expires_in: json.expires_in,
}
}

pub fn update(&mut self, response: SignInResponse) {
self.refresh_token = response.refresh_token;
self.access_token = response.access_token;
}
}

#[derive(Default)]
pub struct Sessions(pub HashMap<String, Session>);
pub struct Sessions(pub HashMap<Arc<str>, Session>);

impl Sessions {
pub fn session_from_cookies(&self, cookies: &PrivateCookieJar) -> Option<&Session> {
pub fn session_from_cookies(&self, cookies: &PrivateCookieJar) -> Option<Session> {
let session_id = cookies.get("session_id")?;
let session_id = session_id.value();
self.session_from_id(session_id)
}

pub fn session_from_id(&self, session_id: &str) -> Option<&Session> {
self.0.get(session_id)
pub fn session_from_id(&self, session_id: &str) -> Option<Session> {
self.0.get(session_id).cloned()
}
}

Expand All @@ -64,10 +55,11 @@ pub async fn login(
None => {
let session = state.supabase.signin_email(creds).await?;
log::info!("Setting session cookie with 1 week expiry");
cookies = cookies.add(
Cookie::build(("session_id", (session.user_id.clone())))
.max_age(time::Duration::WEEK),
);

let user_id = session.user.id.as_ref().to_owned();
let mut cookie = Cookie::new("session_id", user_id);
cookie.set_max_age(time::Duration::WEEK);
cookies = cookies.add(cookie);
state.add_session(session).await;
}
};
Expand All @@ -84,8 +76,8 @@ pub async fn signup(
anyhow::bail!("Already has a session cookie");
}
let session = state.supabase.signup_email(creds).await?;
let cookie =
Cookie::build(("session_id", session.user_id.clone())).max_age(time::Duration::WEEK);
let cookie = Cookie::build(("session_id", String::from(session.user.id.as_ref())))
.max_age(time::Duration::WEEK);
cookies = cookies.add(cookie);
state.add_session(session).await;
Ok(cookies)
Expand Down
79 changes: 45 additions & 34 deletions src/server/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use std::collections::{HashMap, HashSet};
use tower_http::services::ServeDir;
use tower_http::trace::TraceLayer;

use super::auth;

pub fn get_router(state: AppState) -> Router {
Router::new()
.route("/", get(get_index))
Expand All @@ -51,38 +53,6 @@ pub fn get_router(state: AppState) -> Router {
.with_state(state)
}

// Make our own error that wraps `anyhow::Error`.
struct AnyhowError(anyhow::Error);

// Tell axum how to convert `AppError` into a response.
impl IntoResponse for AnyhowError {
fn into_response(self) -> Response {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!(
"Internal Server Error\n{}",
if cfg!(feature = "dev") {
self.0
} else {
anyhow::anyhow!("")
}
),
)
.into_response()
}
}

// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
// `Result<_, AppError>`. That way you don't need to do that manually.
impl<E> From<E> for AnyhowError
where
E: Into<anyhow::Error>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}

async fn get_index() -> Result<Response, AnyhowError> {
Ok((markup::index::page()).into_response())
}
Expand All @@ -101,8 +71,15 @@ async fn get_signup(app: State<AppState>, mut cookies: PrivateCookieJar) -> impl
async fn get_login(app: State<AppState>, mut cookies: PrivateCookieJar) -> impl IntoResponse {
"TODO".into_response()
}
async fn post_logout(app: State<AppState>, mut cookies: PrivateCookieJar) -> impl IntoResponse {
"TODO".into_response()

async fn post_logout(app: State<AppState>, cookies: PrivateCookieJar) -> AppResponse {
let session = app.sessions.read().await.session_from_cookies(&cookies);
if let Some(session) = session {
app.supabase.logout(&session.access_token).await?;
Ok(().into_response())
} else {
Ok((StatusCode::BAD_REQUEST, "not logged in").into_response())
}
}

async fn get_lobby(app: State<AppState>, mut cookies: PrivateCookieJar) -> impl IntoResponse {
Expand Down Expand Up @@ -558,3 +535,37 @@ async fn get_game_menu(
_ => Ok(StatusCode::NOT_FOUND.into_response()),
}
}

type AppResponse = Result<Response, AnyhowError>;

// Make our own error that wraps `anyhow::Error`.
struct AnyhowError(anyhow::Error);

// Tell axum how to convert `AppError` into a response.
impl IntoResponse for AnyhowError {
fn into_response(self) -> Response {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!(
"Internal Server Error\n{}",
if cfg!(feature = "dev") {
self.0
} else {
anyhow::anyhow!("")
}
),
)
.into_response()
}
}

// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
// `Result<_, AppError>`. That way you don't need to do that manually.
impl<E> From<E> for AnyhowError
where
E: Into<anyhow::Error>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}
23 changes: 13 additions & 10 deletions src/server/state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::auth::{Session, Sessions};
use super::supabase::SignInResponse;
use crate::server::supabase::SupabaseAnonClient;
use crate::server::ws;
use crate::{game::Game, lobby::Lobby};
Expand Down Expand Up @@ -60,25 +61,27 @@ impl AppState {
Ok(())
}

pub async fn add_session(&self, session: Session) {
self.clone().spawn_session_refresh_task(&session);
pub async fn add_session(&self, signin: SignInResponse) {
let session = Session {
access_token: signin.access_token.clone(),
refresh_token: signin.refresh_token.clone(),
};
self.sessions
.write()
.await
.0
.insert(session.user_id.clone(), session);
.insert(signin.user.id.clone(), session);
self.clone().spawn_session_refresh_task(signin);
}

pub fn spawn_session_refresh_task(self, session: &Session) {
let token = session.refresh_token.clone();
let session_id = session.user_id.clone();
let duration = tokio::time::Duration::from_secs(session.expires_in / 2);
pub fn spawn_session_refresh_task(self, signin: SignInResponse) {
let duration = tokio::time::Duration::from_secs(signin.expires_in / 2);
tokio::task::spawn(async move {
let mut interval = tokio::time::interval(duration);
interval.tick().await;
loop {
interval.tick().await;
match self.supabase.refresh(&token).await {
match self.supabase.refresh(&signin.refresh_token).await {
Ok(signin) => {
if let Some(session) =
self.sessions.write().await.0.get_mut(&signin.user.id)
Expand All @@ -90,12 +93,12 @@ impl AppState {
}
Err(e) => {
log::error!("{}", e);
self.sessions.write().await.0.remove(&session_id);
self.sessions.write().await.0.remove(&signin.user.id);
break;
}
}
}
self.connections.lock().unwrap().0.remove(&session_id)
//self.connections.lock().unwrap().0.remove(&signin.user.id)
});
}
}
16 changes: 7 additions & 9 deletions src/server/supabase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ pub struct RefreshToken<'a> {

#[derive(Deserialize)]
pub struct SignInResponse {
pub access_token: String,
pub refresh_token: String,
pub access_token: Arc<str>,
pub refresh_token: Arc<str>,
pub expires_in: u64,
pub user: UserSignInResponse,
}

#[derive(Deserialize)]
pub struct UserSignInResponse {
pub id: String,
pub id: Arc<str>,
}

#[derive(Clone)]
Expand Down Expand Up @@ -68,7 +68,7 @@ impl SupabaseAnonClient {
}
}

pub async fn signup_email(&self, creds: &EmailCreds<'_>) -> anyhow::Result<Session> {
pub async fn signup_email(&self, creds: &EmailCreds<'_>) -> anyhow::Result<SignInResponse> {
let response: Response = self
.client
.post(&format!("{}/auth/v1/signup", self.url))
Expand All @@ -78,11 +78,10 @@ impl SupabaseAnonClient {
.send()
.await?;
let json = response.json::<SignInResponse>().await?;
let client = Session::new(json);
Ok(client)
Ok(json)
}

pub async fn signin_email(&self, creds: &EmailCreds<'_>) -> anyhow::Result<Session> {
pub async fn signin_email(&self, creds: &EmailCreds<'_>) -> anyhow::Result<SignInResponse> {
let response = self
.client
.post(&format!("{}/auth/v1/token?grant_type=password", self.url))
Expand All @@ -92,8 +91,7 @@ impl SupabaseAnonClient {
.send()
.await?;
let json = response.json::<SignInResponse>().await?;
let client = Session::new(json);
Ok(client)
Ok(json)
}

pub async fn refresh(&self, refresh_token: &str) -> anyhow::Result<SignInResponse> {
Expand Down

0 comments on commit 7183b09

Please sign in to comment.