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//! # • Authorize: http://127.0.0.1:8090/register
98//! # • Token: http://127.0.0.1:8090/token
99//! # • Register: http://127.0.0.1:8090/authorize
100//! # • Introspection: http://127.0.0.1:8090/introspect
101//! # • UserInfo: http://127.0.0.1:8090/userinfo
102//! # • Revoke: http://127.0.0.1:8090/revoke
103//! ```
104//!
105//! ## Example Usage
106//!
107//! ```bash
108//! # Register a client
109//! curl -X POST http://localhost:8090/register -H "Content-Type: application/json" -d '{
110//! "redirect_uris": ["http://localhost:8090/callback"],
111//! "grant_types": ["authorization_code"],
112//! "response_types": ["code"],
113//! "scope": "openid profile email"
114//! }'
115//! ```
116//!
117//! ## How to Use in Tests
118//!
119//! ```
120//! #[tokio::test]
121//! async fn my_oauth_test() {
122//! let server = oauth2_test_server::OAuthTestServer::start().await;
123//! println!("server: {}", server.base_url());
124//! println!("authorize endpoint: {}", server.endpoints.authorize_url);
125//! // register a client
126//! let client = server.register_client(
127//! serde_json::json!({ "scope": "openid", "redirect_uris":["http://localhost:8080/callback"]}),
128//! );
129//! // generate a jwt
130//! let jwt = server.generate_jwt(&client, server.jwt_options().user_id("bob").build());
131//! assert_eq!(jwt.split('.').count(), 3);
132//! assert_eq!(server.clients().read().iter().len(), 1);
133//! assert_eq!(server.tokens().read().iter().len(), 1);
134//! }
135//!```
136//!
137//! ## Ideal For
138//!
139//! - Testing OAuth libraries
140//! - End-to-end flow validation
141//! - Local development
142//! - Security research
143//! - Teaching OAuth/OIDC
144//!
145//! **⚠️ Warning: Not for production use** - in-memory, no persistence, no rate limiting.
146mod server;
147mod testkit;
148
149pub use server::{Client, IssuerConfig, Token};
150pub use testkit::{JwtOptions, JwtOptionsBuilder, OAuthTestServer};