Skip to main content

mockforge_registry_server/middleware/
api_token_auth.rs

1//! API Token authentication middleware
2//!
3//! This middleware authenticates requests using Personal Access Tokens (PATs).
4//! Tokens are passed in the Authorization header as "Bearer mfx_..." or "Token mfx_..."
5
6use axum::http::StatusCode;
7use uuid::Uuid;
8
9use crate::{models::ApiToken, AppState};
10
11/// Authentication result containing user and token info
12#[derive(Debug, Clone)]
13pub struct TokenAuthResult {
14    pub user_id: Uuid,
15    pub org_id: Uuid,
16    pub token: ApiToken,
17}
18
19/// Authenticate request using API token
20/// Returns (user_id, org_id, token) if valid, None otherwise
21pub async fn authenticate_api_token(
22    state: &AppState,
23    token: &str,
24) -> Result<Option<TokenAuthResult>, StatusCode> {
25    let pool = state.db.pool();
26
27    // Verify token
28    let api_token = ApiToken::verify_token(pool, token)
29        .await
30        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
31
32    let api_token = match api_token {
33        Some(t) => t,
34        None => return Ok(None),
35    };
36
37    // Check if token is expired
38    if let Some(expires_at) = api_token.expires_at {
39        if expires_at < chrono::Utc::now() {
40            return Ok(None); // Token expired
41        }
42    }
43
44    // Get user_id from token (if associated) or from org owner
45    let user_id = if let Some(uid) = api_token.user_id {
46        uid
47    } else {
48        // If token has no user_id, get org owner
49        use crate::models::Organization;
50        let org = Organization::find_by_id(pool, api_token.org_id)
51            .await
52            .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
53            .ok_or(StatusCode::UNAUTHORIZED)?;
54        org.owner_id
55    };
56
57    Ok(Some(TokenAuthResult {
58        user_id,
59        org_id: api_token.org_id,
60        token: api_token,
61    }))
62}