How to Implement Authentication in Rust (JWT, Sessions)

Web
Use the `jsonwebtoken` crate with Axum's `FromRequestParts` extractor to validate tokens in the `Authorization` header.

How to Implement Authentication in Rust (JWT, Sessions)

Use the jsonwebtoken crate with Axum's FromRequestParts extractor to validate tokens in the Authorization header.

use axum::{
    extract::FromRequestParts,
    http::{request::Parts, StatusCode},
    response::{IntoResponse, Response},
    Router,
};
use axum_extra::{
    headers::{authorization::Bearer, Authorization},
    TypedHeader,
};
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
use std::sync::LazyLock;

static KEYS: LazyLock<DecodingKey> = LazyLock::new(|| DecodingKey::from_secret("secret".as_bytes()));

#[derive(Debug, Deserialize, Serialize)]
struct Claims {
    sub: String,
    exp: i64,
}

impl<S> FromRequestParts<S> for Claims
where
    S: Send + Sync,
{
    type Rejection = AuthError;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let TypedHeader(Authorization(bearer)) = parts
            .extract::<TypedHeader<Authorization<Bearer>>>()
            .await
            .map_err(|_| AuthError::InvalidToken)?;
        
        let mut validation = Validation::default();
        validation.validate_exp = true;

        let token_data = decode::<Claims>(bearer.token(), &KEYS, &validation)
            .map_err(|_| AuthError::InvalidToken)?;
        
        Ok(token_data.claims)
    }
}

#[derive(Debug)]
enum AuthError {
    InvalidToken,
}

impl IntoResponse for AuthError {
    fn into_response(self) -> Response {
        (StatusCode::UNAUTHORIZED, "Invalid token").into_response()
    }
}

#[tokio::main]
async fn main() {
    let app = Router::new().route(
        "/protected",
        axum::routing::get(|claims: Claims| async move { format!("Hello {}", claims.sub) }),
    );
    
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

For sessions, use tower-sessions middleware to store state in Redis or memory instead of tokens.