#json-web-key #jwk #axum

axum-jwks

Use a JSON Web Key Set (JWKS) to verify JWTs in Axum

10 breaking releases

0.12.0 Jun 6, 2025
0.11.0 Jan 21, 2025
0.10.0 Jan 15, 2025
0.8.0 Aug 16, 2024
0.3.0 Mar 30, 2023

#2788 in Authentication

Download history 43/week @ 2026-01-25 66/week @ 2026-02-01 24/week @ 2026-02-08 15/week @ 2026-02-15 76/week @ 2026-02-22 156/week @ 2026-03-01 81/week @ 2026-03-08 51/week @ 2026-03-15 75/week @ 2026-03-22 49/week @ 2026-03-29 45/week @ 2026-04-05 37/week @ 2026-04-12 60/week @ 2026-04-19 29/week @ 2026-04-26 109/week @ 2026-05-03 206/week @ 2026-05-10

404 downloads per month

MIT license

22KB
294 lines

axum-jwks

GitHub Workflow Status Crates.io docs.rs

Use a JSON Web Key Set (JWKS) to verify JWTs in Axum.

Features

  • Use an openid-configuration to get the setup from the Authorization Server.
  • Pull a JWKS directly from an Authorization Server
  • Verify JWTs signed by any key in the JWKS and provided as a bearer token in the Authorization header

For more information, see the crate documentation.


lib.rs:

axum-jwks allows for easily verifying JWTs in an axum application using any key from a JSON Web Key Set (JWKS).

Usage

Here's a minimal working example of how you would authenticate via JWTs in a route handler:

use axum::{
    extract::{FromRef, FromRequestParts},
    http::request::Parts,
    http::status::StatusCode,
    response::{IntoResponse, Response},
    routing::get,
    Json,
    Router,
};
use axum_jwks::{Claims, Jwks, ParseTokenClaims, TokenError};
use serde::{Deserialize, Serialize};

// The state available to all your route handlers.
#[derive(Clone)]
struct AppState {
    jwks: Jwks,
}

impl FromRef<AppState> for Jwks {
    fn from_ref(state: &AppState) -> Self {
        state.jwks.clone()
    }
}

// The specific claims you want to parse from received JWTs.
#[derive(Deserialize, Serialize)]
struct TokenClaims {
    pub sub: String
}

impl ParseTokenClaims for TokenClaims {
    type Rejection = TokenClaimsError;
}

enum TokenClaimsError {
    Missing,
    Invalid,
}

impl IntoResponse for TokenClaimsError {
    fn into_response(self) -> Response {
        // You could do something more informative here like providing a
        // response body with different error messages for missing vs.
        // invalid tokens.
        StatusCode::UNAUTHORIZED.into_response()
    }
}

impl From<TokenError> for TokenClaimsError {
    fn from(value: TokenError) -> Self {
        match value {
            TokenError::Missing => Self::Missing,
            other => Self::Invalid,
        }
    }
}

// Handler that echos back the claims it receives. If the handler receives
// these claims, it's guaranteed that they come from a JWT that is signed
// by a key from the JWKS and is valid for the specified audience.
async fn echo_claims(Claims(claims): Claims<TokenClaims>) -> Json<TokenClaims> {
    Json(claims)
}

async fn create_router() -> Router<AppState> {
    let jwks = Jwks::from_oidc_url(
        // The Authorization Server that signs the JWTs you want to consume.
        "https://my-auth-server.example.com/.well-known/openid-configuration",
        // The audience identifier for the application. This ensures that
        // JWTs are intended for this application.
        Some("https://my-api-identifier.example.com/"),
    )
        .await
        .unwrap();

    Router::new()
        .route("/echo-claims", get(echo_claims))
        .with_state(AppState { jwks })
}

Unsupported algorithms

In case a JWK uses an unsupported key algorithm this is logged as warning but otherwise ignored. Tokens signed by that key will not be valid.

Dependencies

~13–30MB
~357K SLoC