Skip to content

cmackenzie1/axum-jwt-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

7a4c0f1 · Jan 25, 2025

History

24 Commits
Jan 25, 2025
Jan 2, 2025
Jan 25, 2025
Jul 28, 2023
Jan 2, 2025
Jul 27, 2023
Jan 25, 2025
Jan 25, 2025
Jul 28, 2023
Jan 2, 2025
Jan 25, 2025
Jan 25, 2025

Repository files navigation

axum-jwt-auth

Rust

A simple middleware for extracting JWT tokens from requests and making them available to your handlers.

See examples for how to use.

Usage

use axum::{
    extract::FromRef,
    response::{IntoResponse, Response},
    routing::{get, post},
    Json, Router,
};

use chrono::{Duration, Utc};
use jsonwebtoken::{encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use axum_jwt_auth::{Claims, Decoder, JwtDecoderState, LocalDecoder};
use serde::{Deserialize, Serialize};

#[derive(Clone, FromRef)]
struct AppState {
    decoder: JwtDecoderState,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MyClaims {
    iat: u64,
    aud: String,
    exp: u64,
}

async fn index() -> Response {
    "Hello, World!".into_response()
}

// Claims extractor will return a 401 if the token is invalid
async fn user_info(Claims(claims): Claims<MyClaims>) -> Response {
    Json(claims).into_response()
}

async fn login() -> Response {
    let key = EncodingKey::from_rsa_pem(include_bytes!("../jwt.key")).unwrap();
    let mut header = Header::new(Algorithm::RS256);
    header.kid = Some("test".to_string());

    let exp = Utc::now() + Duration::hours(1);
    let claims = MyClaims {
        iat: 1234567890,
        aud: "https://example.com".to_string(),
        exp: exp.timestamp() as u64,
    };

    let token = encode::<MyClaims>(&header, &claims, &key).unwrap();

    token.into_response()
}

#[tokio::main]
async fn main() {
    let keys = vec![DecodingKey::from_rsa_pem(include_bytes!("jwt.key.pub")).unwrap()];
    let mut validation = Validation::new(Algorithm::RS256);
    // Set the audience to the expected value. Not setting this will cause the token to be invalid.
    validation.set_audience(&["https://example.com"]);
    let decoder: Decoder = LocalDecoder::new(keys, validation).into();
    let state = AppState {
        decoder: JwtDecoderState { decoder },
    };

    let app = Router::new()
        .route("/", get(index))
        .route("/user_info", get(user_info))
        .route("/login", post(login))
        .with_state(state);

    // run it on localhost:3000
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}