This commit is contained in:
Deep 2025-09-21 16:01:16 +05:30
parent 2cb2004840
commit 5ad6a1986d
6 changed files with 21 additions and 30 deletions

View file

@ -1,6 +1,6 @@
use axum::{
extract::{FromRequestParts, State},
http::{header, request::Parts, StatusCode},
extract::FromRequestParts,
http::{header, request::Parts},
};
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
@ -9,10 +9,6 @@ use worker::Env;
use crate::error::AppError;
// Secret key for signing JWTs. In a real application, this should be a strong,
// securely stored secret from the Worker's environment variables.
const JWT_SECRET: &str = "a-very-secure-secret-key-that-should-be-in-env";
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String, // User ID
@ -26,13 +22,11 @@ pub struct Claims {
pub amr: Vec<String>,
}
impl<S> FromRequestParts<S> for Claims
where
S: Send + Sync,
impl FromRequestParts<Arc<Env>> for Claims
{
type Rejection = AppError;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
async fn from_request_parts(parts: &mut Parts, state: &Arc<Env>) -> Result<Self, Self::Rejection> {
// Extract the token from the authorization header
let token = parts
.headers
@ -47,8 +41,10 @@ where
})
.ok_or_else(|| AppError::Unauthorized("Missing or invalid token".to_string()))?;
let secret = state.secret("JWT_SECRET")?;
// Decode and validate the token
let decoding_key = DecodingKey::from_secret(JWT_SECRET.as_ref());
let decoding_key = DecodingKey::from_secret(secret.to_string().as_ref());
let token_data = decode::<Claims>(&token, &decoding_key, &Validation::default())
.map_err(|_| AppError::Unauthorized("Invalid token".to_string()))?;

View file

@ -21,7 +21,6 @@ pub async fn prelogin(
.ok_or_else(|| AppError::BadRequest("Missing email".to_string()))?;
let db = db::get_db(&env)?;
log::info!("Getting kdf for user {email:?}");
let stmt = db.prepare("SELECT kdf_iterations FROM users WHERE email = ?1");
let query = stmt.bind(&[email.into()])?;
let kdf_iterations: Option<i32> = query
@ -29,7 +28,6 @@ pub async fn prelogin(
.await
.map_err(|_| AppError::Database)?;
log::info!("Returning {kdf_iterations:?}");
Ok(Json(PreloginResponse {
kdf: 0, // PBKDF2
kdf_iterations: kdf_iterations.unwrap_or(600_000),
@ -41,9 +39,20 @@ pub async fn register(
State(env): State<Arc<Env>>,
Json(payload): Json<RegisterRequest>,
) -> Result<Json<Value>, AppError> {
log::info!("Get db");
let allowed_emails = env
.secret("ALLOWED_EMAILS")
.map_err(|_| AppError::Internal)?;
let allowed_emails = allowed_emails
.as_ref()
.as_string()
.ok_or_else(|| AppError::Internal)?;
if allowed_emails
.split(",")
.all(|email| email.trim() != payload.email)
{
return Err(AppError::Unauthorized("Not allowed to signup".to_string()));
}
let db = db::get_db(&env)?;
log::info!("db got");
let now = Utc::now().to_rfc3339();
let user = User {
id: Uuid::new_v4().to_string(),
@ -62,7 +71,6 @@ pub async fn register(
updated_at: now,
};
log::info!("User {user:?}");
let query = query!(
&db,
"INSERT INTO users (id, name, email, master_password_hash, key, private_key, public_key, kdf_iterations, security_stamp, created_at, updated_at)
@ -79,13 +87,11 @@ pub async fn register(
user.created_at,
user.updated_at
).map_err(|error|{
log::error!("failed {error:?}");
AppError::Database
})?
.run()
.await
.map_err(|error|{
log::error!("failed {error:?}");
AppError::Database
})?;

View file

@ -54,7 +54,6 @@ pub async fn token(
State(env): State<Arc<Env>>,
Form(payload): Form<TokenRequest>,
) -> Result<Json<TokenResponse>, AppError> {
log::info!("Token payload {payload:?}");
if payload.grant_type != "password" {
return Err(AppError::BadRequest("Unsupported grant_type".to_string()));
}
@ -64,7 +63,6 @@ pub async fn token(
let db = db::get_db(&env)?;
log::info!("Lookup user");
let user: Value = db
.prepare("SELECT * FROM users WHERE email = ?1")
.bind(&[payload.username.to_lowercase().into()])?
@ -72,17 +70,14 @@ pub async fn token(
.await
.map_err(|_| AppError::Unauthorized("Invalid credentials".to_string()))?
.ok_or_else(|| AppError::Unauthorized("Invalid credentials".to_string()))?;
log::info!("User is {user:?}");
let user: User = serde_json::from_value(user).map_err(|_| AppError::Internal)?;
// Securely compare the provided hash with the stored hash
if !constant_time_eq(
user.master_password_hash.as_bytes(),
password_hash.as_bytes(),
) {
log::info!("Password is incorrect");
return Err(AppError::Unauthorized("Invalid credentials".to_string()));
}
log::info!("Password matched");
// Create JWT claims
let now = Utc::now();

View file

@ -23,7 +23,6 @@ pub async fn get_sync_data(
let user_id = claims.sub;
let db = db::get_db(&env)?;
log::info!("Fetch user");
// Fetch profile
let user: User = db
.prepare("SELECT * FROM users WHERE id = ?1")
@ -32,7 +31,6 @@ pub async fn get_sync_data(
.await?
.ok_or_else(|| AppError::NotFound("User not found".to_string()))?;
log::info!("Fetch folders");
// Fetch folders
let folders: Vec<Folder> = db
.prepare("SELECT * FROM folders WHERE user_id = ?1")
@ -41,7 +39,6 @@ pub async fn get_sync_data(
.await?
.results()?;
log::info!("Fetch ciphers");
// Fetch ciphers
let ciphers: Vec<Value> = db
.prepare("SELECT * FROM ciphers WHERE user_id = ?1")
@ -64,7 +61,6 @@ pub async fn get_sync_data(
.map(|cipher| cipher.into())
.collect::<Vec<Cipher>>();
log::info!("Fetch time");
let time = chrono::DateTime::parse_from_rfc3339(&user.created_at)
.map_err(|_| AppError::Internal)?
.to_rfc3339_opts(chrono::SecondsFormat::Micros, true);

View file

@ -1,4 +1,3 @@
use axum::Router;
use tower_http::cors::{Any, CorsLayer};
use tower_service::Service;
use worker::*;
@ -19,7 +18,7 @@ pub async fn main(
) -> Result<axum::http::Response<axum::body::Body>> {
// Set up logging
console_error_panic_hook::set_once();
console_log::init_with_level(log::Level::Debug);
let _ = console_log::init_with_level(log::Level::Debug);
// Allow all origins for CORS, which is typical for a public API like Bitwarden's.
let cors = CorsLayer::new()

View file

@ -170,7 +170,6 @@ impl Serialize for Cipher {
response_map.insert("creationDate".to_string(), json!(self.created_at));
response_map.insert("deletedDate".to_string(), json!(self.deleted_at));
log::debug!("Response data is {:?}", self.data);
if let Some(data_obj) = self.data.as_object() {
let data_clone = data_obj.clone();