diff --git a/.env.example b/.env.example index 6e03067..16d72ef 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ DATABASE_URL=postgresql://{USERNAME}:{PASSWORD}@localhost:5432/{DATABASE_NAME} REVEAAL_ADDRESS=http://{IP}:{PORT} API_ADDRESS={IP}:{PORT} -HS512_SECRET={SECRET} +ACCESS_TOKEN_HS512_SECRET={SECRET} +REFRESH_TOKEN_HS512_SECRET={SECRET} diff --git a/Ecdar-ProtoBuf b/Ecdar-ProtoBuf index fe4caa3..64460bf 160000 --- a/Ecdar-ProtoBuf +++ b/Ecdar-ProtoBuf @@ -1 +1 @@ -Subproject commit fe4caa3fcc6bda790a1c4aa3c13ee7c7fabe8649 +Subproject commit 64460bf6458d0c7078bef33edc751725f93d564d diff --git a/src/api/auth.rs b/src/api/auth.rs index c19836e..f307b02 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -14,8 +14,9 @@ pub struct Claims { exp: usize, } -pub fn create_jwt(uid: &str) -> Result { - let secret = env::var("HS512_SECRET").expect("Expected HS512_SECRET to be set."); +pub fn create_access_token(uid: &str) -> Result { + let secret = env::var("ACCESS_TOKEN_HS512_SECRET") + .expect("Expected ACCESS_TOKEN_HS512_SECRET to be set."); let expiration = Utc::now() .checked_add_signed(chrono::Duration::minutes(20)) @@ -36,6 +37,29 @@ pub fn create_jwt(uid: &str) -> Result { .map_err(|_| ErrorKind::InvalidToken.into()) } +pub fn create_refresh_token(uid: &str) -> Result { + let secret = env::var("REFRESH_TOKEN_HS512_SECRET") + .expect("Expected REFRESH_TOKEN_HS512_SECRET to be set."); + + let expiration = Utc::now() + .checked_add_signed(chrono::Duration::days(90)) + .expect("valid timestamp") + .timestamp(); + + let claims = Claims { + sub: uid.to_owned(), + exp: expiration as usize, + }; + + let header = Header::new(Algorithm::HS512); + encode( + &header, + &claims, + &EncodingKey::from_secret(secret.as_bytes()), + ) + .map_err(|_| ErrorKind::InvalidToken.into()) +} + pub fn token_validation(mut req: Request<()>) -> Result, Status> { let token = match req.metadata().get("authorization") { Some(token) => token.to_str(), diff --git a/src/api/ecdar_api.rs b/src/api/ecdar_api.rs index dd0544f..4c231d5 100644 --- a/src/api/ecdar_api.rs +++ b/src/api/ecdar_api.rs @@ -2,6 +2,7 @@ use std::env; use std::sync::Arc; use crate::api::ecdar_api::helpers::helpers::{setup_db_with_entities, AnyEntity}; +use crate::api::server::server::get_auth_token_request::{user_credentials, AuthOption}; use regex::Regex; use sea_orm::SqlErr; use tonic::{Code, Request, Response, Status}; @@ -213,17 +214,53 @@ fn is_valid_username(username: &str) -> bool { impl EcdarApiAuth for ConcreteEcdarApi { async fn get_auth_token( &self, - _request: Request, + request: Request, ) -> Result, Status> { - let uid = "1234"; - let token = auth::create_jwt(uid); + let message = request.get_ref().clone(); + let uid = match message.auth_option { + Some(auth_option) => match auth_option { + AuthOption::RefreshToken(refresh_token) => { + get_uid_from_request(&request).unwrap().to_string() + } + AuthOption::UserCredentials(user_credentials) => { + if let Some(user) = user_credentials.user { + match user { + user_credentials::User::Username(username) => { + match self.user_context.get_by_username(username).await { + Ok(Some(user)) => user.id.to_string(), + Ok(None) => Err(Status::new(Code::Internal, "No user found"))?, + Err(err) => Err(Status::new(Code::Internal, err.to_string()))?, + } + } + user_credentials::User::Email(email) => { + match self.user_context.get_by_email(email).await { + Ok(Some(user)) => user.id.to_string(), + Ok(None) => Err(Status::new(Code::Internal, "No user found"))?, + Err(err) => Err(Status::new(Code::Internal, err.to_string()))?, + } + } + } + } else { + Err(Status::new(Code::Internal, "No user provided"))? + } + } + }, + None => Err(Status::new(Code::Internal, "No auth option provided"))?, + }; - match token { - Ok(token) => Ok(Response::new(GetAuthTokenResponse { token })), - Err(e) => Err(Status::new(Code::Internal, e.to_string())), - } + let access_token = match auth::create_access_token(&uid) { + Ok(token) => token, + Err(e) => return Err(Status::new(Code::Internal, e.to_string())), + }; + let refresh_token = match auth::create_refresh_token(&uid) { + Ok(token) => token, + Err(e) => return Err(Status::new(Code::Internal, e.to_string())), + }; + Ok(Response::new(GetAuthTokenResponse { + access_token, + refresh_token, + })) } - async fn create_user( &self, request: Request, diff --git a/src/database/user_context.rs b/src/database/user_context.rs index 4fe674b..203f22e 100644 --- a/src/database/user_context.rs +++ b/src/database/user_context.rs @@ -7,6 +7,7 @@ use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter, Ru use crate::database::database_context::DatabaseContextTrait; use crate::database::entity_context::EntityContextTrait; use crate::entities::prelude::User as UserEntity; +use crate::entities::user::Column as UserColumn; use crate::entities::user::{ActiveModel, Model as User}; #[derive(Debug)] @@ -24,6 +25,7 @@ pub trait UserContextTrait: EntityContextTrait { /// assert_eq!(model.id,1); /// ``` async fn get_by_username(&self, username: String) -> Result, DbErr>; + async fn get_by_email(&self, email: String) -> Result, DbErr>; } impl Debug for dyn UserContextTrait + Send + Sync + 'static { @@ -36,7 +38,13 @@ impl Debug for dyn UserContextTrait + Send + Sync + 'static { impl UserContextTrait for UserContext { async fn get_by_username(&self, username: String) -> Result, DbErr> { UserEntity::find() - .filter(crate::entities::user::Column::Username.eq(username)) + .filter(UserColumn::Username.eq(username)) + .one(&self.db_context.get_connection()) + .await + } + async fn get_by_email(&self, email: String) -> Result, DbErr> { + UserEntity::find() + .filter(UserColumn::Email.eq(email)) .one(&self.db_context.get_connection()) .await }