nxtgauge-backend-rust/crates/cache/src/token.rs
Ashwin Kumar bb8155dd27 feat: add Redis for OTP, auth tokens, rate limiting, lead dedup and marketplace cache
- Add crates/cache with client, otp, rate_limit, token, lead, jobs modules
- OTP tokens stored in Redis (15-min TTL, single-use GETDEL on verify)
- Refresh tokens stored in Redis (30-day TTL) — removed DB storage
- Password reset tokens stored in Redis (1-hour TTL, single-use)
- Rate limiting: register (10/hr), login (10/15min), OTP resend (3/hr), lead (5/hr), job post (20/hr)
- Lead request deduplication: 24-hour Redis lock per professional+requirement pair
- Marketplace listings cached in Redis (5-min TTL per profession+page+limit)
- Add ProfessionState{pool, redis} to contracts crate, replacing bare PgPool in all 9 profession apps
- All profession handlers and main.rs updated to use ProfessionState
- REDIS_URL env var (default: redis://127.0.0.1:6379) used across all services
- Fix profession model struct name mangling in 6 handlers (MakeupArtistRepository etc.)
- Add custom_data JSONB migration for all 9 profession profile tables
- Add onboarding_state model and repository (save_progress, complete, is_complete)
- Add onboarding handler accepting roleKey:String (not role_id:UUID) for frontend compat

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 22:58:42 +01:00

64 lines
2 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 = 3_600; // 1 hour
// ── 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}");
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
}