1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! # modo::auth::session::jwt
//!
//! JWT-backed stateful session transport — token encoding, decoding, middleware,
//! and extractors. Issues access/refresh token pairs backed by the
//! `authenticated_sessions` table; validates tokens on every request via `jti` lookup.
//!
//! ## Provides
//!
//! | Type | Purpose |
//! |------|---------|
//! | [`JwtSessionService`] | Stateful service: authenticate, rotate, logout, list, cleanup |
//! | [`JwtSessionsConfig`] | YAML-deserialized configuration (signing secret, TTLs, sources) |
//! | [`TokenPair`] | Access + refresh token pair returned by authenticate/rotate |
//! | [`JwtLayer`] | Tower middleware that enforces JWT auth on axum routes |
//! | [`JwtSession`] | Request-scoped session manager extractor (rotate, logout, list) |
//! | [`Claims`] | Standard JWT registered claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`); axum extractor |
//! | [`JwtEncoder`] | Signs any `Serialize` payload into a JWT token string (HS256) |
//! | [`JwtDecoder`] | Verifies signatures, validates claims, and deserializes into any `DeserializeOwned` |
//! | [`Bearer`] | Standalone axum extractor for the raw Bearer token string |
//! | [`JwtError`] | Typed error enum with static `code()` strings |
//! | [`ValidationConfig`] | Runtime validation policy (leeway, issuer, audience) |
//! | [`TokenSourceConfig`] | YAML enum for selecting a token extraction strategy |
//!
//! | Trait | Purpose |
//! |-------|---------|
//! | [`TokenSource`] | Pluggable token extraction from HTTP requests |
//! | [`TokenSigner`] | JWT signing; extends [`TokenVerifier`] |
//! | [`TokenVerifier`] | JWT signature verification (object-safe, use behind `Arc<dyn TokenVerifier>`) |
//!
//! | Token source | Extracts from |
//! |--------------|---------------|
//! | [`BearerSource`] | `Authorization: Bearer <token>` header |
//! | [`CookieSource`] | Named cookie |
//! | [`QuerySource`] | Named query parameter |
//! | [`HeaderSource`] | Custom request header |
//!
//! | Signer | Algorithm |
//! |--------|-----------|
//! | [`HmacSigner`] | HMAC-SHA256 (HS256), implements [`TokenSigner`] and [`TokenVerifier`] |
//!
//! ## Quick start — stateful system auth flow
//!
//! ```rust,ignore
//! use modo::auth::session::jwt::{JwtSessionService, JwtSessionsConfig};
//! use modo::auth::session::meta::SessionMeta;
//! use axum::Router;
//! use axum::routing::{get, post};
//!
//! // 1. Build the service (validates config at construction — fail fast)
//! let config = JwtSessionsConfig::new("my-super-secret-key-for-signing-tokens");
//! let svc = JwtSessionService::new(db, config)?;
//!
//! // 2. Wire stateful middleware on protected routes
//! let app: Router = Router::new()
//! .route("/me", get(me_handler))
//! .route("/refresh", post(refresh_handler))
//! .route("/logout", post(logout_handler))
//! .route_layer(svc.layer()) // stateful: verifies signature + session row
//! .with_state(svc);
//! ```
//!
//! `svc.layer()` returns a [`JwtLayer`] that verifies the JWT signature and
//! standard claims, then hashes the `jti` claim and loads the session row from
//! `authenticated_sessions`. Returns `401` when the row is absent (logged-out /
//! revoked). The system uses `aud = "access"` for access tokens and
//! `aud = "refresh"` for refresh tokens.
//!
//! ## Low-level / custom payload
//!
//! ```rust,ignore
//! use modo::auth::session::jwt::{JwtSessionsConfig, JwtEncoder, JwtDecoder};
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize)]
//! struct InvitePayload { inviter_id: String, org_id: String, exp: u64 }
//!
//! let config = JwtSessionsConfig::new("my-super-secret-key-for-signing-tokens");
//! let encoder = JwtEncoder::from_config(&config);
//! let decoder = JwtDecoder::from_config(&config);
//!
//! let payload = InvitePayload {
//! inviter_id: "user_1".into(),
//! org_id: "org_1".into(),
//! exp: 9999999999,
//! };
//! let token: String = encoder.encode(&payload)?;
//! let decoded: InvitePayload = decoder.decode(&token)?;
//! ```
pub use Claims;
pub use JwtSessionsConfig;
/// Back-compat alias — prefer [`JwtSessionsConfig`].
pub use JwtSessionsConfig as JwtConfig;
pub use JwtDecoder;
pub use JwtEncoder;
pub use JwtError;
pub use ;
pub use JwtLayer;
pub use JwtSessionService;
pub use ;
pub use ;
pub use TokenPair;
pub use ValidationConfig;