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