nxtgauge-backend-rust/crates/cache/src/token.rs
Ashwin Kumar Sivakumar 01f3810c8f
All checks were successful
build-and-release / build (cron) (push) Successful in 4m55s
build-and-release / build (catering-services) (push) Successful in 9m18s
build-and-release / build (developers) (push) Successful in 9m14s
build-and-release / build (employees) (push) Successful in 9m15s
build-and-release / build (customers) (push) Successful in 9m35s
build-and-release / build (companies) (push) Successful in 9m49s
build-and-release / build (gateway) (push) Successful in 3m2s
build-and-release / build (fitness-trainers) (push) Successful in 8m20s
build-and-release / build (jobs) (push) Successful in 4m22s
build-and-release / build (graphic-designers) (push) Successful in 8m41s
build-and-release / build (job-seekers) (push) Successful in 9m14s
build-and-release / build (leads) (push) Successful in 9m10s
build-and-release / build (makeup-artists) (push) Successful in 8m51s
build-and-release / build (payments) (push) Successful in 8m51s
build-and-release / build (photographers) (push) Successful in 8m39s
build-and-release / build (ugc-content-creators) (push) Successful in 6m57s
build-and-release / build (social-media-managers) (push) Successful in 8m25s
build-and-release / build (tutors) (push) Successful in 8m28s
build-and-release / build (video-editors) (push) Successful in 8m43s
build-and-release / build (users) (push) Successful in 11m8s
feat(employees): add forgot-password/reset-password for admin accounts
Employees (internal admin/staff) had no self-service password reset —
only /login, /logout, /session existed. Adds /api/admin/auth/forgot-password
and /api/admin/auth/reset-password, mirroring the existing users-table flow
but against EmployeeRepository and a distinct Redis key namespace
(reset:employee:*) so a code for one identity store can never be consumed
against the other.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 23:28:39 +05:30

89 lines
3 KiB
Rust

//! Auth token management in Redis.
//!
//! Refresh tokens
//! ──────────────
//! `refresh:{token}` → user_id, TTL 30 days
//!
//! Password-reset tokens
//! ─────────────────────
//! `reset:{token}` → user_id, TTL 1 hour
use redis::AsyncCommands;
use crate::RedisPool;
const REFRESH_TTL: u64 = 30 * 24 * 3_600; // 30 days in seconds
const RESET_TTL: u64 = 900; // 15 minutes
// ── Refresh tokens ────────────────────────────────────────────────────────────
pub async fn store_refresh(
redis: &mut RedisPool,
token: &str,
user_id: &str,
) -> Result<(), redis::RedisError> {
let key = format!("refresh:{token}");
redis.set_ex(key, user_id, REFRESH_TTL).await
}
/// Returns the user_id string associated with this token, or `None` if expired/invalid.
pub async fn get_refresh(
redis: &mut RedisPool,
token: &str,
) -> Result<Option<String>, redis::RedisError> {
let key = format!("refresh:{token}");
redis.get(key).await
}
/// Delete refresh token (logout / rotation).
pub async fn revoke_refresh(
redis: &mut RedisPool,
token: &str,
) -> Result<(), redis::RedisError> {
let key = format!("refresh:{token}");
redis.del(key).await
}
// ── Password-reset tokens ─────────────────────────────────────────────────────
pub async fn store_reset(
redis: &mut RedisPool,
token: &str,
user_id: &str,
) -> Result<(), redis::RedisError> {
let key = format!("reset:{token}");
let plain_key = format!("otp:plain:{user_id}");
// Store both: token→user_id (for verification) and plain→token (for dev debugging)
redis.set_ex::<_, _, ()>(&plain_key, token, RESET_TTL).await?;
redis.set_ex::<_, _, ()>(key, user_id, RESET_TTL).await
}
/// Atomically fetch and delete the reset token (single-use).
pub async fn consume_reset(
redis: &mut RedisPool,
token: &str,
) -> Result<Option<String>, redis::RedisError> {
let key = format!("reset:{token}");
redis.get_del(key).await
}
// ── Employee (internal admin) password-reset tokens ──────────────────────────
// Distinct key namespace from the user-facing reset tokens above, so a code
// generated for one identity store can never be consumed against the other.
pub async fn store_employee_reset(
redis: &mut RedisPool,
token: &str,
employee_id: &str,
) -> Result<(), redis::RedisError> {
let key = format!("reset:employee:{token}");
redis.set_ex(key, employee_id, RESET_TTL).await
}
/// Atomically fetch and delete the employee reset token (single-use).
pub async fn consume_employee_reset(
redis: &mut RedisPool,
token: &str,
) -> Result<Option<String>, redis::RedisError> {
let key = format!("reset:employee:{token}");
redis.get_del(key).await
}