Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session implemented #30

Merged
merged 1 commit into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"dotenv",
"jsonwebtoken",
"lettre",
"signin"
"signin",
"signout"
]
}
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ lettre = "0.11"
jsonwebtoken = "9.3.0"
openssl = "0.10.64"
regex = "1.10.4"
uuid = "1.8.0"
143 changes: 83 additions & 60 deletions src/core/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ use bson::{doc, DateTime};
use futures::StreamExt;
use mongodb::{Client, Collection};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use super::{dek::Dek, user::User};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
pub uid: String,
pub session_id: String,
pub email: String,
pub id_token: String,
pub refresh_token: String,
Expand All @@ -40,6 +42,7 @@ impl Session {

Self {
uid: user.uid.to_string(),
session_id: Uuid::new_v4().to_string(),
email: user.email.to_string(),
id_token,
refresh_token,
Expand Down Expand Up @@ -120,15 +123,21 @@ impl Session {
token_data
}

pub async fn refresh_session(
pub async fn refresh(
mongo_client: &Client,
session_id: &str,
id_token: &str,
refresh_token: &str,
) -> Result<(String, String)> {
// verify refresh token
match RefreshToken::verify(&refresh_token) {
Ok(_) => {}
Err(e) => return Err(e),
Err(e) => {
match Self::revoke(&mongo_client, &session_id).await {
Ok(_) => return Err(e),
Err(err) => return Err(err),
}
},
}
match Self::verify(&mongo_client, &id_token).await {
Ok(token_verify_result) => {
Expand All @@ -147,68 +156,79 @@ impl Session {
Encryption::encrypt_data(&refresh_token, &dek_data.dek);

match collection_session
.count_documents(
.find_one(
doc! {
"uid": &encrypted_id,
"id_token": &encrypted_id_token,
"refresh_token": &encrypted_refresh_token,
"session_id": &session_id,
"is_revoked": false,
},
None,
)
.await
{
Ok(count) => {
if count == 1 {
// generate a new id token and refresh token
let user = match User::get_from_uid(&mongo_client, &token_verify_result.0.uid).await {
Ok(user) => user,
Err(e) => return Err(e),
};
let new_id_token = match IDToken::new(&user).sign() {
Ok(token) => token,
Err(_) => "".to_string(),
};

let new_refresh_token = match RefreshToken::new(&token_verify_result.0.uid).sign() {
Ok(token) => token,
Err(_) => "".to_string(),
};

// encrypt the new tokens
let new_id_token_encrypted = Encryption::encrypt_data(&new_id_token, &dek_data.dek);
let new_refresh_token_encrypted = Encryption::encrypt_data(&new_refresh_token, &dek_data.dek);

match collection_session
.update_one(
doc! {
"uid": encrypted_id,
"id_token": encrypted_id_token,
"refresh_token": encrypted_refresh_token,
"is_revoked": false,
},
doc! {
"$set": {
"id_token": new_id_token_encrypted,
"refresh_token": new_refresh_token_encrypted,
"updated_at": DateTime::now(),
}
},
None,
)
.await
{
Ok(_) => return Ok((new_id_token, new_refresh_token)),
Err(e) => return Err(Error::ServerError {
message: e.to_string(),
}),
};
} else {
match Self::revoke_all(&mongo_client, &token_verify_result.0.uid).await {
Ok(_) => return Err(Error::SessionExpired {
Ok(session) => {
match session {
Some(data) => {
let decrypted_session = data.decrypt(&dek_data.dek);
if decrypted_session.id_token == id_token
&& decrypted_session.refresh_token == refresh_token
{
// generate a new id token and refresh token
let user = match User::get_from_uid(&mongo_client, &token_verify_result.0.uid).await {
Ok(user) => user,
Err(e) => return Err(e),
};
let new_id_token = match IDToken::new(&user).sign() {
Ok(token) => token,
Err(_) => "".to_string(),
};

let new_refresh_token = match RefreshToken::new(&token_verify_result.0.uid).sign() {
Ok(token) => token,
Err(_) => "".to_string(),
};

// encrypt the new tokens
let new_id_token_encrypted = Encryption::encrypt_data(&new_id_token, &dek_data.dek);
let new_refresh_token_encrypted = Encryption::encrypt_data(&new_refresh_token, &dek_data.dek);

match collection_session
.update_one(
doc! {
"uid": encrypted_id,
"id_token": encrypted_id_token,
"refresh_token": encrypted_refresh_token,
"is_revoked": false,
},
doc! {
"$set": {
"id_token": new_id_token_encrypted,
"refresh_token": new_refresh_token_encrypted,
"updated_at": DateTime::now(),
}
},
None,
)
.await
{
Ok(_) => return Ok((new_id_token, new_refresh_token)),
Err(e) => return Err(Error::ServerError {
message: e.to_string(),
}),
};
} else {
match Self::revoke(&mongo_client, &session_id).await {
Ok(_) => return Err(Error::InvalidToken {
message: "Invalid token".to_string(),
}),
Err(e) => return Err(e),
}
}
}
None => {
return Err(Error::SessionExpired {
message: "Invalid token".to_string(),
}),
Err(e) => return Err(e),
});
}
}
}
Expand All @@ -223,7 +243,10 @@ impl Session {
}
}
Err(e) => {
return Err(e);
match Self::revoke(&mongo_client, &session_id).await {
Ok(_) => return Err(e),
Err(err) => return Err(err),
}
}
};
}
Expand Down Expand Up @@ -298,13 +321,13 @@ impl Session {
}
}

pub async fn revoke(id_token: &str, refresh_token: &str, mongo_client: &Client) -> Result<()> {
pub async fn revoke(mongo_client: &Client, session_id: &str) -> Result<()> {
let db = mongo_client.database("test");
let collection_session: Collection<Session> = db.collection("sessions");

match collection_session
.update_one(
doc! {"id_token": id_token, "refresh_token": refresh_token },
doc! {"session_id": session_id},
doc! {"$set": {"is_revoked": true}},
None,
)
Expand All @@ -317,13 +340,13 @@ impl Session {
}
}

pub async fn delete(id_token: &str, refresh_token: &str, mongo_client: &Client) -> Result<()> {
pub async fn delete(mongo_client: &Client, session_id: &str) -> Result<()> {
let db = mongo_client.database("test");
let collection_session: Collection<Session> = db.collection("sessions");

match collection_session
.delete_one(
doc! {"id_token": id_token, "refresh_token": refresh_token },
doc! { "session_id": session_id },
None,
)
.await
Expand Down
28 changes: 24 additions & 4 deletions src/handlers/auth_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use axum_macros::debug_handler;
use serde_json::Value;

use crate::{
errors::Result,
models::auth_model::{SignInPayload, SignUpPayload},
utils::auth_utils::{sign_in, sign_up},
AppState,
core::session::Session, errors::{Error, Result}, models::{auth_model::{SignInPayload, SignUpPayload}, session_model::{RevokeSessionsPayload, RevokeSessionsResult}}, utils::auth_utils::{sign_in, sign_up}, AppState
};

#[debug_handler]
Expand All @@ -33,3 +30,26 @@ pub async fn signin_handler(
Err(e) => Err(e),
}
}

pub async fn signout_handler(
State(state): State<AppState>,
payload: Json<RevokeSessionsPayload>,
) -> Result<Json<RevokeSessionsResult>> {
println!(">> HANDLER: signout_handler called");

if payload.session_id.is_empty() {
return Err(Error::InvalidPayload {
message: "Invalid payload passed".to_string(),
});
}

match Session::revoke(&state.mongo_client, &payload.session_id).await {
Ok(_) => Ok(Json(
RevokeSessionsResult {
message: "Session revoked successfully".to_string(),
}
)),
Err(e) => Err(e),
}

}
Loading