OAuth 2.1 Authentication¶
The mcp/oauth2 package provides an OAuth 2.1 Authorization Server for authenticated MCP servers. This is required for public MCP servers that integrate with services like ChatGPT.com.
Overview¶
The OAuth2 implementation supports:
- Authorization Code Flow with PKCE (RFC 7636)
- Dynamic Client Registration (RFC 7591)
- Authorization Server Metadata (RFC 8414)
- Bearer token authentication
Quick Start¶
Enable OAuth2 when serving over HTTP:
result, err := rt.ServeHTTP(ctx, &server.HTTPServerOptions{
Addr: ":8080",
OAuth2: &server.OAuth2Options{
Users: map[string]string{
"admin": "password",
},
},
OnReady: func(r *server.HTTPServerResult) {
fmt.Printf("Client ID: %s\n", r.OAuth2.ClientID)
fmt.Printf("Client Secret: %s\n", r.OAuth2.ClientSecret)
},
})
Endpoints¶
When OAuth2 is enabled, these endpoints are automatically configured:
| Endpoint | Purpose |
|---|---|
/.well-known/oauth-authorization-server |
Server metadata (RFC 8414) |
/authorize |
Authorization endpoint |
/token |
Token endpoint |
/register |
Dynamic client registration |
Configuration¶
Basic Configuration¶
OAuth2: &server.OAuth2Options{
// Username/password pairs for authentication
Users: map[string]string{
"user1": "password1",
"user2": "password2",
},
}
With Pre-configured Clients¶
OAuth2: &server.OAuth2Options{
Users: map[string]string{"admin": "password"},
// Pre-configure a client
ClientID: "my-client-id",
ClientSecret: "my-client-secret",
}
Custom Token Lifetime¶
OAuth2: &server.OAuth2Options{
Users: map[string]string{"admin": "password"},
TokenExpiration: 24 * time.Hour, // Default is 1 hour
}
OAuth2 Flow¶
1. Client Registration¶
Clients register dynamically:
POST /register
Content-Type: application/json
{
"client_name": "My MCP Client",
"redirect_uris": ["https://myapp.com/callback"]
}
Response:
2. Authorization¶
Redirect user to authorize:
GET /authorize?
response_type=code&
client_id=CLIENT_ID&
redirect_uri=https://myapp.com/callback&
code_challenge=CHALLENGE&
code_challenge_method=S256&
state=STATE
User authenticates, then redirected to:
3. Token Exchange¶
Exchange code for token:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=AUTH_CODE&
redirect_uri=https://myapp.com/callback&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
code_verifier=VERIFIER
Response:
4. Authenticated Requests¶
Include token in MCP requests:
Server Metadata¶
Clients can discover OAuth2 configuration:
Response:
{
"issuer": "https://myserver.com",
"authorization_endpoint": "https://myserver.com/authorize",
"token_endpoint": "https://myserver.com/token",
"registration_endpoint": "https://myserver.com/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code"],
"code_challenge_methods_supported": ["S256"]
}
PKCE (Proof Key for Code Exchange)¶
PKCE is required for all authorization requests:
- Generate code verifier (random string)
- Create code challenge:
BASE64URL(SHA256(code_verifier)) - Send challenge in authorization request
- Send verifier in token request
Example (Go):
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
)
// Generate verifier
verifier := make([]byte, 32)
rand.Read(verifier)
codeVerifier := base64.RawURLEncoding.EncodeToString(verifier)
// Create challenge
hash := sha256.Sum256([]byte(codeVerifier))
codeChallenge := base64.RawURLEncoding.EncodeToString(hash[:])
Custom Authentication¶
For custom authentication logic, use the OAuth2 package directly:
import "github.com/plexusone/omniskill/mcp/oauth2"
authServer := oauth2.NewServer(&oauth2.Config{
Issuer: "https://myserver.com",
// Custom user validator
ValidateUser: func(username, password string) bool {
// Check against database, LDAP, etc.
return validateCredentials(username, password)
},
})
// Use with HTTP server
http.Handle("/authorize", authServer.AuthorizeHandler())
http.Handle("/token", authServer.TokenHandler())
ChatGPT.com Integration¶
For ChatGPT.com, OAuth2 is required. Configure your server:
rt.ServeHTTP(ctx, &server.HTTPServerOptions{
Addr: ":443",
NgrokAuthtoken: os.Getenv("NGROK_AUTHTOKEN"),
OAuth2: &server.OAuth2Options{
Users: map[string]string{
os.Getenv("OAUTH_USER"): os.Getenv("OAUTH_PASSWORD"),
},
},
OnReady: func(r *server.HTTPServerResult) {
fmt.Println("Configure in ChatGPT:")
fmt.Printf(" MCP URL: %s\n", r.PublicURL)
fmt.Printf(" Client ID: %s\n", r.OAuth2.ClientID)
fmt.Printf(" Client Secret: %s\n", r.OAuth2.ClientSecret)
},
})
Security Notes¶
- HTTPS Required - Always use HTTPS in production
- Strong Secrets - Use cryptographically random client secrets
- Token Expiration - Set appropriate token lifetime
- PKCE Required - Never disable PKCE
- Validate Redirect URIs - Strictly validate registered redirect URIs