fix
All checks were successful
/ upload (release) Successful in 21s

This commit is contained in:
pavel 2026-02-24 21:58:25 +01:00
commit 1ec1ba0028
4 changed files with 192 additions and 30 deletions

View file

@ -14,13 +14,15 @@ use uuid::Uuid;
use crate::{AppState, db};
const OAUTH_STATE_COOKIE: &str = "chattz_oauth_state";
const SESSION_TTL_SECS: u64 = 60 * 60 * 24 * 7;
const SESSION_TTL_SECS: u64 = 60 * 15; // 15 minutes
const REFRESH_TTL_SECS: u64 = 60 * 60 * 24 * 30; // 30 days
#[derive(Debug, Serialize, Deserialize, Clone)]
struct SessionClaims {
sub: String,
exp: usize,
iat: usize,
pub struct SessionClaims {
pub sub: String,
pub kind: String, // "access" or "refresh"
pub exp: usize,
pub iat: usize,
}
#[derive(Debug, Clone)]
@ -94,8 +96,8 @@ where
.or_else(|| read_query_token(parts))
.ok_or_else(|| ApiError::unauthorized("missing jwt token"))?;
let user_id = verify_session(&token, &app.settings.session_secret)
.map_err(|_| ApiError::unauthorized("invalid token"))?;
let user_id = verify_session(&token, &app.settings.session_secret, "access")
.map_err(|_| ApiError::unauthorized("invalid or expired token"))?;
let exists = db::user_exists(&app.db, user_id)
.await
@ -138,25 +140,41 @@ pub fn read_oauth_state_from_headers(headers: &axum::http::HeaderMap) -> Option<
})
}
pub fn new_jwt_token(user_id: Uuid, secret: &str) -> Result<String> {
pub fn new_jwt_tokens(user_id: Uuid, secret: &str) -> Result<(String, String)> {
let now = now_ts();
let claims = SessionClaims {
let access_claims = SessionClaims {
sub: user_id.to_string(),
kind: "access".to_string(),
iat: now as usize,
exp: (now + SESSION_TTL_SECS) as usize,
};
let token = encode(
let refresh_claims = SessionClaims {
sub: user_id.to_string(),
kind: "refresh".to_string(),
iat: now as usize,
exp: (now + REFRESH_TTL_SECS) as usize,
};
let access_token = encode(
&Header::default(),
&claims,
&access_claims,
&EncodingKey::from_secret(secret.as_bytes()),
)
.context("failed to encode session token")?;
.context("failed to encode access token")?;
Ok(token)
let refresh_token = encode(
&Header::default(),
&refresh_claims,
&EncodingKey::from_secret(secret.as_bytes()),
)
.context("failed to encode refresh token")?;
Ok((access_token, refresh_token))
}
fn verify_session(token: &str, secret: &str) -> Result<Uuid> {
pub fn verify_session(token: &str, secret: &str, expected_kind: &str) -> Result<Uuid> {
let mut validation = Validation::default();
validation.validate_exp = true;
@ -167,6 +185,10 @@ fn verify_session(token: &str, secret: &str) -> Result<Uuid> {
)
.context("failed to decode session token")?;
if data.claims.kind != expected_kind {
return Err(anyhow!("invalid token kind"));
}
let user_id = Uuid::parse_str(&data.claims.sub).context("invalid sub in session token")?;
Ok(user_id)
}