Skip to content

Commit

Permalink
Merge pull request #30 from Rajdip019/rajdeep/session
Browse files Browse the repository at this point in the history
Session implemented
  • Loading branch information
Rajdip019 authored May 10, 2024
2 parents e4452b0 + 6123a4b commit f1da2ad
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 79 deletions.
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

0 comments on commit f1da2ad

Please sign in to comment.