axum_jwks/
lib.rs

1//! axum-jwks allows for easily verifying JWTs in an axum application using any
2//! key from a JSON Web Key Set (JWKS).
3//!
4//! # Usage
5//!
6//! Here's a minimal working example of how you would authenticate via JWTs in a
7//! route handler:
8//! ```no_run
9//! use axum::{
10//!     extract::{FromRef, FromRequestParts},
11//!     http::request::Parts,
12//!     http::status::StatusCode,
13//!     response::{IntoResponse, Response},
14//!     routing::get,
15//!     Json,
16//!     Router,
17//! };
18//! use axum_jwks::{Claims, Jwks, ParseTokenClaims, TokenError};
19//! use serde::{Deserialize, Serialize};
20//!
21//! // The state available to all your route handlers.
22//! #[derive(Clone)]
23//! struct AppState {
24//!     jwks: Jwks,
25//! }
26//!
27//! impl FromRef<AppState> for Jwks {
28//!     fn from_ref(state: &AppState) -> Self {
29//!         state.jwks.clone()
30//!     }
31//! }
32//!
33//! // The specific claims you want to parse from received JWTs.
34//! #[derive(Deserialize, Serialize)]
35//! struct TokenClaims {
36//!     pub sub: String
37//! }
38//!
39//! impl ParseTokenClaims for TokenClaims {
40//!     type Rejection = TokenClaimsError;
41//! }
42//!
43//! enum TokenClaimsError {
44//!     Missing,
45//!     Invalid,
46//! }
47//!
48//! impl IntoResponse for TokenClaimsError {
49//!     fn into_response(self) -> Response {
50//!         // You could do something more informative here like providing a
51//!         // response body with different error messages for missing vs.
52//!         // invalid tokens.
53//!         StatusCode::UNAUTHORIZED.into_response()
54//!     }
55//! }
56//!
57//! impl From<TokenError> for TokenClaimsError {
58//!     fn from(value: TokenError) -> Self {
59//!         match value {
60//!             TokenError::Missing => Self::Missing,
61//!             other => Self::Invalid,
62//!         }
63//!     }
64//! }
65//!
66//! // Handler that echos back the claims it receives. If the handler receives
67//! // these claims, it's guaranteed that they come from a JWT that is signed
68//! // by a key from the JWKS and is valid for the specified audience.
69//! async fn echo_claims(Claims(claims): Claims<TokenClaims>) -> Json<TokenClaims> {
70//!     Json(claims)
71//! }
72//!
73//! async fn create_router() -> Router<AppState> {
74//!     let jwks = Jwks::from_oidc_url(
75//!         // The Authorization Server that signs the JWTs you want to consume.
76//!         "https://my-auth-server.example.com/.well-known/openid-configuration",
77//!         // The audience identifier for the application. This ensures that
78//!         // JWTs are intended for this application.
79//!         Some("https://my-api-identifier.example.com/"),
80//!     )
81//!         .await
82//!         .unwrap();
83//!
84//!     Router::new()
85//!         .route("/echo-claims", get(echo_claims))
86//!         .with_state(AppState { jwks })
87//! }
88//! ```
89//!
90//! # Unsupported algorithms
91//! In case a JWK uses an unsupported key algorithm this is logged as warning but otherwise ignored.
92//! Tokens signed by that key will *not* be valid.
93
94mod claims;
95mod jwks;
96mod token;
97
98pub use claims::{Claims, ParseTokenClaims};
99pub use jwks::{JwkError, Jwks, JwksError};
100pub use token::{Token, TokenError};