diff --git a/src/api/v1/auth/login.rs b/src/api/v1/auth/login.rs index 257f2bf..d65540b 100644 --- a/src/api/v1/auth/login.rs +++ b/src/api/v1/auth/login.rs @@ -1,10 +1,12 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + use actix_web::{error, post, web, Error, HttpResponse}; use argon2::{Argon2, PasswordHash, PasswordVerifier}; use regex::Regex; use serde::{Deserialize, Serialize}; use futures::StreamExt; -use crate::Data; +use crate::{crypto::{generate_access_token, generate_refresh_token}, Data}; #[derive(Deserialize)] struct LoginInformation { @@ -23,7 +25,7 @@ struct Response { const MAX_SIZE: usize = 262_144; #[post("/login")] -pub async fn res(mut payload: web::Payload, data: web::Data) -> Result { +pub async fn response(mut payload: web::Payload, data: web::Data) -> Result { let mut body = web::BytesMut::new(); while let Some(chunk) = payload.next().await { let chunk = chunk?; @@ -49,14 +51,16 @@ pub async fn res(mut payload: web::Payload, data: web::Data) -> Result) -> Result HttpResponse { +async fn login(data: actix_web::web::Data, uuid: String, request_password: String, database_password: String) -> HttpResponse { if let Ok(parsed_hash) = PasswordHash::new(&database_password) { - if argon2.verify_password(request_password.as_bytes(), &parsed_hash).is_ok() { + if data.argon2.verify_password(request_password.as_bytes(), &parsed_hash).is_ok() { + let refresh_token = generate_refresh_token(); + let access_token = generate_access_token(); + + if refresh_token.is_err() { + eprintln!("{}", refresh_token.unwrap_err()); + return HttpResponse::InternalServerError().finish() + } + + let refresh_token = refresh_token.unwrap(); + + if access_token.is_err() { + eprintln!("{}", access_token.unwrap_err()); + return HttpResponse::InternalServerError().finish() + } + + let access_token = access_token.unwrap(); + + let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64; + + if let Err(error) = sqlx::query(&format!("INSERT INTO refresh_tokens (token, uuid, created) VALUES ($1, '{}', $2 )", uuid)) + .bind(&refresh_token) + .bind(current_time) + .execute(&data.pool) + .await { + eprintln!("{}", error); + return HttpResponse::InternalServerError().finish() + } + + if let Err(error) = sqlx::query(&format!("INSERT INTO refresh_tokens (token, refresh_token, uuid, created) VALUES ($1, $2, '{}', $3 )", uuid)) + .bind(&access_token) + .bind(&refresh_token) + .bind(current_time) + .execute(&data.pool) + .await { + eprintln!("{}", error); + return HttpResponse::InternalServerError().finish() + } + return HttpResponse::Ok().json(Response { access_token: "bogus".to_string(), expires_in: 0, diff --git a/src/api/v1/auth/mod.rs b/src/api/v1/auth/mod.rs index d514206..c7efc91 100644 --- a/src/api/v1/auth/mod.rs +++ b/src/api/v1/auth/mod.rs @@ -7,6 +7,6 @@ mod refresh; pub fn web() -> Scope { web::scope("/auth") .service(register::res) - .service(login::res) + .service(login::response) .service(refresh::res) } diff --git a/src/api/v1/auth/refresh.rs b/src/api/v1/auth/refresh.rs index a04f454..b924d43 100644 --- a/src/api/v1/auth/refresh.rs +++ b/src/api/v1/auth/refresh.rs @@ -3,7 +3,7 @@ use actix_web::{error, post, web, Error, HttpResponse}; use serde::{Deserialize, Serialize}; use futures::StreamExt; -use crate::Data; +use crate::{crypto::{generate_access_token, generate_refresh_token}, Data}; #[derive(Deserialize)] struct RefreshRequest { @@ -12,9 +12,8 @@ struct RefreshRequest { #[derive(Serialize)] struct Response { - refresh_token: Option, + refresh_token: String, access_token: String, - expires_in: u64, } const MAX_SIZE: usize = 262_144; @@ -35,15 +34,82 @@ pub async fn res(mut payload: web::Payload, data: web::Data) -> Result 2592000 { + if let Err(error) = sqlx::query("DELETE FROM refresh_tokens WHERE token = $1") + .bind(&refresh_request.refresh_token) + .execute(&data.pool) + .await { + eprintln!("{}", error); + } + + return Ok(HttpResponse::Unauthorized().finish()) + } - println!("{}, {}", uuid, created); + let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64; - Ok(HttpResponse::InternalServerError().finish()) + let mut refresh_token = refresh_request.refresh_token; + + if lifetime > 1987200 { + let new_refresh_token = generate_refresh_token(); + + if new_refresh_token.is_err() { + eprintln!("{}", new_refresh_token.unwrap_err()); + return Ok(HttpResponse::InternalServerError().finish()) + } + + let new_refresh_token = new_refresh_token.unwrap(); + + match sqlx::query(&format!("UPDATE refresh_tokens SET token = $1, uuid = {}, created = $2 WHERE token = $3", uuid)) + .bind(&new_refresh_token) + .bind(¤t_time) + .bind(&refresh_token) + .execute(&data.pool) + .await { + Ok(_) => { + refresh_token = new_refresh_token; + }, + Err(error) => { + eprintln!("{}", error); + }, + } + } + + let access_token = generate_access_token(); + + if access_token.is_err() { + eprintln!("{}", access_token.unwrap_err()); + return Ok(HttpResponse::InternalServerError().finish()) + } + + let access_token = access_token.unwrap(); + + if let Err(error) = sqlx::query(&format!("INSERT INTO access_tokens (token, refresh_token, uuid, created) VALUES ($1, $2, '{}', $3 )", uuid)) + .bind(&access_token) + .bind(&refresh_token) + .bind(current_time) + .execute(&data.pool) + .await { + eprintln!("{}", error); + return Ok(HttpResponse::InternalServerError().finish()) + } + + return Ok(HttpResponse::Ok().json(Response { + refresh_token, + access_token + })) + } + + Ok(HttpResponse::Unauthorized().finish()) } diff --git a/src/api/v1/auth/register.rs b/src/api/v1/auth/register.rs index 9e8d2fe..250872f 100644 --- a/src/api/v1/auth/register.rs +++ b/src/api/v1/auth/register.rs @@ -7,7 +7,7 @@ use futures::StreamExt; use uuid::Uuid; use argon2::{password_hash::{rand_core::OsRng, SaltString}, PasswordHasher}; -use crate::Data; +use crate::{crypto::{generate_access_token, generate_refresh_token}, Data}; #[derive(Deserialize)] struct AccountInformation { @@ -95,7 +95,7 @@ pub async fn res(mut payload: web::Payload, data: web::Data) -> Result) -> Result) -> Result { - let refresh_token = todo!(); - let access_token = todo!(); + let refresh_token = generate_refresh_token(); + let access_token = generate_access_token(); + + if refresh_token.is_err() { + eprintln!("{}", refresh_token.unwrap_err()); + return Ok(HttpResponse::InternalServerError().finish()) + } + + let refresh_token = refresh_token.unwrap(); + + if access_token.is_err() { + eprintln!("{}", access_token.unwrap_err()); + return Ok(HttpResponse::InternalServerError().finish()) + } + + let access_token = access_token.unwrap(); let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64; if let Err(error) = sqlx::query(&format!("INSERT INTO refresh_tokens (token, uuid, created) VALUES ($1, '{}', $2 )", uuid)) - .bind(refresh_token) + .bind(&refresh_token) .bind(current_time) .execute(&data.pool) .await { @@ -132,9 +146,9 @@ pub async fn res(mut payload: web::Payload, data: web::Data) -> Result Result { +pub fn generate_access_token() -> Result { let mut buf = [0u8; 16]; fill(&mut buf)?; Ok(encode(&buf)) } -fn generate_refresh_token() -> Result { +pub fn generate_refresh_token() -> Result { let mut buf = [0u8; 32]; fill(&mut buf)?; Ok(encode(&buf)) diff --git a/src/main.rs b/src/main.rs index 55159f4..44fca77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use std::time::SystemTime; mod config; use config::{Config, ConfigBuilder}; mod api; -mod crypto; +pub mod crypto; type Error = Box;