oauth2_test_server/lib.rs
1//! # Rust OAuth2 Server
2//!
3//!
4//! A **complete, standards-compliant OAuth 2.0 and OpenID Connect 1.0 Authorization Server**,
5//! implemented in **pure Rust** using **Axum**. This server is specially designed to support
6//! **testing authentication flow of MCP Servers and Clients**, with full support for Dynamic Client Registration (DCR).
7//!
8//! **⚠️ Warning: NOT** for production use - in-memory, no persistence, no rate limiting.
9//!
10//! ## Caution
11//!
12//! This server was developed with the purpose of supporting testing and development of
13//! the **[`rust-mcp-sdk`](https://crates.io/crates/rust-mcp-sdk)**, and may not be maintained or updated regularly. Please consider this when integrating
14//! or using this server in other contexts.
15//!
16//!
17//! ## Purpose
18//!
19//! This server implements **all major OAuth 2.0 flows** and **OpenID Connect core features**
20//! in-memory, making it ideal for:
21//!
22//! - Testing OAuth clients (web, mobile, SPA, backend)
23//! - Specifically tailored for **testing authentication flow MCP Servers and Clients**, with DCR support
24//! - Integration testing of authorization flows
25//! - Local development against a real OAuth provider
26//! - Demonstrating OAuth concepts
27//! - CI/CD pipeline validation
28//!
29//! ## Supported Standards
30//!
31//! | Standard | Implemented |
32//! |--------|-------------|
33//! | [RFC 6749](https://tools.ietf.org/html/rfc6749) – OAuth 2.0 | Full |
34//! | [RFC 6750](https://tools.ietf.org/html/rfc6750) – Bearer Token | Yes |
35//! | [RFC 7636](https://tools.ietf.org/html/rfc7636) – PKCE | Yes (`plain`, `S256`) |
36//! | [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) – Dynamic Client Registration | Yes |
37//! | [RFC 7662](https://tools.ietf.org/html/rfc7662) – Token Introspection | Yes |
38//! | [RFC 7009](https://tools.ietf.org/html/rfc7009) – Token Revocation | Yes |
39//! | [RFC 7519](https://tools.ietf.org/html/rfc7519) – JWT Access Tokens (RS256) | Yes |
40//! | [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) | Yes |
41//! | [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html) | Partial (UserInfo, `sub`, `iss`) |
42//!
43//! ## Key Features
44//!
45//! - **Dynamic Client Registration (DCR)** (`POST /register`) with full metadata support
46//! - **Authorization Code Flow** with **PKCE** (`/authorize`, `/token`)
47//! - **Refresh Token Flow** with rotation and revocation
48//! - **Client Credentials Grant**
49//! - **JWT Access Tokens** signed with **RS256** (auto-generated RSA key pair)
50//! - **Token Introspection** (`POST /introspect`)
51//! - **Token Revocation** (`POST /revoke`)
52//! - **OpenID Connect Discovery** (`.well-known/openid-configuration`)
53//! - **JWKS Endpoint** (`.well-known/jwks.json`)
54//! - **UserInfo Endpoint** (`GET /userinfo`)
55//! - **In-memory stores** (clients, codes, tokens) - no external DB required
56//! - **Full error handling** with redirect errors and JSON error responses
57//! - **State parameter**, **scope**, **redirect_uri validation**
58//!
59//! ## Endpoints
60//!
61//! | Method | Path | Description |
62//! |-------|------|-------------|
63//! | `GET` | `/.well-known/openid-configuration` | OIDC Discovery |
64//! | `GET` | `/.well-known/jwks.json` | Public keys for JWT validation |
65//! | `POST` | `/register` | Dynamic client registration |
66//! | `GET` | `/register/:client_id` | Retrieve registered client |
67//! | `GET` | `/authorize` | Authorization endpoint (code flow) |
68//! | `POST` | `/token` | Token endpoint (all grants) |
69//! | `POST` | `/introspect` | RFC 7662 introspection |
70//! | `POST` | `/revoke` | RFC 7009 revocation |
71//! | `GET` | `/userinfo` | OIDC user info (requires Bearer token) |
72//! | `GET` | `/error` | Human-readable error page |
73//!
74//! ## In-Memory Stores
75//!
76//! - `clients`: `HashMap<String, Client>` - registered clients
77//! - `codes`: `HashMap<String, AuthorizationCode>` - short-lived auth codes
78//! - `tokens`: `HashMap<String, Token>` - access tokens (JWTs)
79//! - `refresh_tokens`: `HashMap<String, Token>` - refresh token mapping
80//!
81//! ## Security & Testing
82//!
83//! - **No persistence** - perfect for isolated tests
84//! - **Auto-generated RSA key pair** on startup
85//! - **PKCE verification** (`S256` and `plain`)
86//! - **Token revocation propagation**
87//! - **Expiration enforcement**
88//! - **Scope and redirect_uri validation**
89//!
90//!
91//! ## Running
92//!
93//! ```bash
94//! cargo run
95//! # OAuth Test Server running on http://127.0.0.1:8090/
96//! # • Discovery: http://127.0.0.1:8090/.well-known/openid-configuration
97//! # • Jwks: http://127.0.0.1:8090/.well-known/jwks.json
98//! # • Authorize: http://127.0.0.1:8090/register
99//! # • Token: http://127.0.0.1:8090/token
100//! # • Register: http://127.0.0.1:8090/authorize
101//! # • Introspection: http://127.0.0.1:8090/introspect
102//! # • UserInfo: http://127.0.0.1:8090/userinfo
103//! # • Revoke: http://127.0.0.1:8090/revoke
104//! ```
105//!
106//! ## Configuration
107//!
108//! The server is configured via [`IssuerConfig`]. Set `default_user_id` to
109//! change the user identity used in authorization flows:
110//!
111//! ```rust,no_run
112//! use oauth2_test_server::IssuerConfig;
113//!
114//! let config = IssuerConfig {
115//! default_user_id: "alice".into(),
116//! port: 0,
117//! ..Default::default()
118//! };
119//! ```
120//!
121//! All fields can also be loaded from environment variables prefixed with
122//! `OAUTH_` (e.g. `OAUTH_DEFAULT_USER_ID=alice`) via [`IssuerConfig::from_env`],
123//! or from a YAML/TOML file via [`IssuerConfig::from_file`].
124//! See the [README](https://github.com/rust-mcp-stack/oauth2-test-server#configuration)
125//! for a full list of options.
126//!
127//! ## Example Usage
128//!
129//! ```bash
130//! # Register a client
131//! curl -X POST http://localhost:8090/register -H "Content-Type: application/json" -d '{
132//! "redirect_uris": ["http://localhost:8090/callback"],
133//! "grant_types": ["authorization_code"],
134//! "response_types": ["code"],
135//! "scope": "openid profile email"
136//! }'
137//! ```
138//!
139//! ## How to Use in Tests
140//!
141//! ```
142//! #[tokio::test]
143//! async fn my_oauth_test() {
144//! let server = oauth2_test_server::OAuthTestServer::start().await;
145//! println!("server: {}", server.base_url());
146//! println!("authorize endpoint: {}", server.endpoints.authorize);
147//! // register a client
148//! let client = server.register_client(serde_json::json!({ "scope": "openid",
149//! "redirect_uris":["http://localhost:8080/callback"],
150//! "client_name": "rust-mcp-sdk"
151//! })).await;
152//! // generate a jwt for the client
153//! let token = server.generate_token(&client, server.jwt_options().user_id("rustmcp").build()).await;
154//! assert_eq!(token.access_token.split('.').count(), 3);
155//! }
156//!```
157//!
158//! ## Ideal For
159//!
160//! - Testing OAuth libraries
161//! - End-to-end flow validation
162//! - Local development
163//! - Security research
164//! - Teaching OAuth/OIDC
165//!
166//! **⚠️ Warning: Not for production use** - in-memory, no persistence, no rate limiting.
167pub mod config;
168pub mod crypto;
169pub mod error;
170pub mod handlers;
171pub mod models;
172pub mod router;
173pub mod store;
174
175pub mod testkit;
176
177pub use config::{ConfigError, IssuerConfig};
178pub use crypto::{calculate_at_hash, calculate_c_hash, issue_id_token, Keys};
179pub use models::{Client, IdTokenClaims, Token};
180pub use store::AppState;
181
182#[cfg(feature = "testing")]
183pub use testkit::{
184 AuthorizeParams, JwtOptions, JwtOptionsBuilder, OAuthTestServer, OauthEndpoints, PkcePair,
185};