JWT & OAuth2
Issue and validate JWT access tokens and integrate OAuth2 authorization flows for Rust APIs.
Search across all documentation pages
Issue and validate JWT access tokens and integrate OAuth2 authorization flows for Rust APIs.
Quick-reference recipe card - copy-paste ready.
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
#[derive(serde::Serialize, serde::Deserialize)]
struct Claims { sub: String, exp: usize, aud: String }
let token = encode(&Header::default(), &claims, &EncodingKey::from_secret(secret))?;
let data = decode::<Claims>(&token, &DecodingKey::from_secret(secret), &Validation::default())?;When to reach for this: Stateless API auth, microservice identity propagation, or delegating login to Google/Auth0 via OAuth2.
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use oauth2::{AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl, basic::BasicClient};
#[derive(serde::Serialize, serde::Deserialize)]
struct AccessClaims {
sub: String,
exp: usize,
aud: String,
}
fn issue_access_token(sub: &str, aud: &str, secret: &[u8], ttl_secs: usize) -> String {
let exp = (std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs()
+ ttl_secs as u64) as usize;
let claims = AccessClaims { sub: sub.into(), exp, aud: aud.into() };
encode(&Header::default(), &claims, &EncodingKey::from_secret(secret)).unwrap()
}
fn validate_access_token(token: &str, secret: &[u8], expected_aud: &str) -> Result<AccessClaims, jsonwebtoken::errors::Error> {
let mut validation = Validation::default();
validation.set_audience(&[expected_aud]);
let data = decode::<AccessClaims>(token, &DecodingKey::from_secret(secret), &validation)?;
Ok(data.claims)
}
fn oauth_client() -> BasicClient {
BasicClient::new(ClientId::new("client-id".into()))
.set_client_secret(ClientSecret::new("client-secret".into()))
.set_auth_uri(AuthUrl::new("https://issuer.example.com/oauth/authorize".into()).unwrap())
.set_token_uri(TokenUrl::new("https://issuer.example.com/oauth/token".into()).unwrap())
.set_redirect_uri(RedirectUrl::new("http://localhost:3000/callback".into()).unwrap())
}What this demonstrates:
jsonwebtoken.aud) validation prevents token reuse across services.oauth2 crate client setup for authorization code flow.exp) enforced by Validation.| Flow | Use |
|---|---|
| Client credentials | Machine-to-machine |
| Authorization code + PKCE | User login for SPAs/mobile |
| Refresh token | Renew access without re-login |
// RS256 with public key from OIDC JWKS
let key = DecodingKey::from_rsa_pem(public_pem)?;Validation audience and issuer.state stored in session.Authorization in tracing.| Alternative | Use When | Don't Use When |
|---|---|---|
| Session cookies | Browser-first apps | Mobile/API-only clients |
| PASETO | Opinionated token format | Ecosystem expects JWT/OIDC |
| API keys | Internal scripts | Interactive users |
OIDC adds identity (id_token) on OAuth2 - use for user profile.
HttpOnly cookie or secure mobile keystore - never localStorage.
Cache keys with TTL; refetch on kid mismatch.
Encode in token claims; enforce per handler.
Client credentials flow with short-lived JWT.
See Authentication & Sessions page for FromRequestParts pattern.
validation.leeway seconds for exp/nbf.
Token denylist until exp or session server-side for refresh revoke.
Static test keys; encode helper in test module.
Use issuer metadata URL; validate RS256 against published JWKS.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Reviewed by Chris St. John·Last updated Jul 16, 2026