Authentication
Neutron provides multiple authentication strategies out of the box: JWT tokens, OAuth2/OIDC, server-side sessions, WebAuthn passkeys, and CSRF protection.
JWT
HMAC-SHA256 token validation as middleware:
use neutron::prelude::*;
use neutron::jwt::{JwtAuth, JwtConfig, Claims};
let config = JwtConfig::new(b"your-secret-key-at-least-32-bytes")
.issuer("my-app")
.audience("my-api")
.leeway(30); // Clock skew tolerance in seconds
let api = Router::new()
.middleware(JwtAuth::new(config))
.get("/profile", get_profile);
async fn get_profile(Extension(claims): Extension<Claims>) -> Json<serde_json::Value> {
Json(serde_json::json!({
"user": claims.sub,
"issued_at": claims.iat
}))
}
Claims
pub struct Claims {
pub sub: Option<String>, // Subject
pub iss: Option<String>, // Issuer
pub aud: Option<String>, // Audience
pub exp: Option<u64>, // Expiration (Unix timestamp)
pub nbf: Option<u64>, // Not before
pub iat: Option<u64>, // Issued at
pub extra: serde_json::Value, // Custom claims
}
OAuth2 / OIDC
Authorization code flow with PKCE:
use neutron_oauth::{OAuthProvider, oauth_redirect_handler, oauth_callback_handler};
let github = OAuthProvider::github()
.client_id(env::var("GITHUB_CLIENT_ID")?)
.client_secret(env::var("GITHUB_CLIENT_SECRET")?)
.redirect_uri("https://myapp.com/auth/github/callback")
.secret(b"session-signing-secret-32-bytes!".to_vec());
let router = Router::new()
.get("/auth/github", oauth_redirect_handler(github.clone()))
.get("/auth/github/callback", oauth_callback_handler(github, on_login));
async fn on_login(user: OAuthUser, _req: Request) -> Response {
// user.id, user.email, user.name, user.avatar_url
format!("Welcome, {}!", user.name.unwrap_or(user.id)).into_response()
}
Sessions
Server-side sessions with signed cookies:
use neutron::session::{Session, SessionLayer, MemoryStore};
use neutron::cookie::Key;
let key = Key::generate();
let store = MemoryStore::new();
let router = Router::new()
.middleware(SessionLayer::new(store, key))
.get("/count", counter);
async fn counter(session: Session) -> String {
let count: u64 = session.get("count").unwrap_or(0);
session.insert("count", count + 1);
format!("Visit #{}", count + 1)
}
For production, use Redis-backed sessions via neutron-redis.
Cookies
use neutron::cookie::{SetCookie, SignedCookieJar, PrivateCookieJar, Key, SameSite};
// Set a cookie
async fn login() -> SetCookie {
SetCookie::new("session", "abc123")
.path("/")
.http_only()
.same_site(SameSite::Lax)
.max_age(86400)
}
// Read a signed cookie (HMAC-verified)
async fn read_signed(jar: SignedCookieJar) -> String {
jar.get("session").unwrap_or_default()
}
// Read an encrypted cookie (AES-GCM)
async fn read_private(jar: PrivateCookieJar) -> String {
jar.get("session").unwrap_or_default()
}
CSRF Protection
Double-submit cookie pattern:
use neutron::csrf::{CsrfLayer, CsrfToken};
let router = Router::new()
.middleware(CsrfLayer::new(key))
.get("/form", show_form)
.post("/submit", handle_submit);
async fn show_form(csrf: CsrfToken) -> String {
format!(r#"
<form method="post" action="/submit">
<input type="hidden" name="csrf" value="{}">
<button type="submit">Submit</button>
</form>
"#, csrf.token())
}
WebAuthn / Passkeys
FIDO2 passkey authentication via neutron-webauthn:
[dependencies]
neutron-webauthn = "0.1"
Provides P-256 ECDSA signature verification for passwordless authentication flows.