This directory contains comprehensive OAuth 2.0 authentication examples for the MCP Dart SDK, with a focus on real-world GitHub integration.
The MCP Dart SDK provides flexible OAuth 2.0 authentication support through the OAuthClientProvider interface for clients and OAuth validation for servers. These examples demonstrate production-ready OAuth implementations.
Files: github_oauth_example.dart | Setup Guide
Production-ready example connecting to GitHub's official MCP server with OAuth:
- Complete OAuth 2.0 flow with GitHub
- Automatic browser-based authorization
- Local callback server for OAuth redirect
- Token persistence and reuse
- CSRF protection with state validation
- Works with GitHub's official MCP server
final config = GitHubOAuthConfig(
clientId: Platform.environment['GITHUB_CLIENT_ID']!,
clientSecret: Platform.environment['GITHUB_CLIENT_SECRET']!,
scopes: GitHubOAuthConfig.recommendedScopes,
);
final authProvider = GitHubOAuthProvider(
config: config,
storage: GitHubTokenStorage('.github_oauth_tokens.json'),
);
final transport = StreamableHttpClientTransport(
Uri.parse('https://api.githubcopilot.com/mcp/'),
opts: StreamableHttpClientTransportOptions(
authProvider: authProvider,
),
);
await client.connect(transport);Setup & Run: See the complete GitHub Setup Guide for step-by-step instructions.
# Set credentials
export GITHUB_CLIENT_ID=your_client_id
export GITHUB_CLIENT_SECRET=your_client_secret
# Run example
dart run example/authentication/github_oauth_example.dartFile: github_pat_example.dart
Simpler authentication using GitHub Personal Access Tokens:
- Quick setup without OAuth app registration
- Direct token-based authentication
- Suitable for development and personal use
- Same functionality as OAuth flow
final authProvider = GitHubPATProvider(
token: Platform.environment['GITHUB_TOKEN']!,
);
final transport = StreamableHttpClientTransport(
Uri.parse('https://api.githubcopilot.com/mcp/'),
opts: StreamableHttpClientTransportOptions(
authProvider: authProvider,
),
);Setup & Run:
# Create a GitHub Personal Access Token at:
# https://github.com/settings/tokens
# Set token
export GITHUB_TOKEN=your_personal_access_token
# Run example
dart run example/authentication/github_pat_example.dartFile: oauth_client_example.dart
✅ COMPLIANT with MCP OAuth Specification (2025-06-18)
Complete OAuth 2.0 authorization code flow implementation:
MCP Specification Compliance:
- ✅ PKCE Support - Generates code_verifier and code_challenge
- ✅ Resource Parameter - Includes resource parameter in token requests
- ✅ Proper Authorization - Uses body parameters (OAuth standard)
Features:
- Authorization URL generation with PKCE
- Token exchange and storage
- Automatic token refresh with resource parameter
- Secure token management
final authProvider = OAuth2Provider(
config: OAuthConfig(
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
authorizationEndpoint: Uri.parse('https://auth.example.com/authorize'),
tokenEndpoint: Uri.parse('https://auth.example.com/token'),
scopes: ['mcp.read', 'mcp.write'],
redirectUri: Uri.parse('http://localhost:8080/callback'),
serverUri: 'http://localhost:3000', // MCP server URI for resource parameter
),
storage: TokenStorage('.oauth_tokens.json'),
);
final transport = StreamableHttpClientTransport(
Uri.parse('http://localhost:3000/mcp'),
opts: StreamableHttpClientTransportOptions(
authProvider: authProvider,
),
);Key Features:
- Full OAuth 2.0 flow with PKCE (RFC 7636)
- Resource parameter for audience validation
- Token expiration tracking
- Automatic refresh using refresh tokens
- Persistent token storage
- Authorization callback handling
- Compatible with oauth_server_example.dart
Note: The example uses PKCE "plain" method for simplicity. For production, add the crypto package to pubspec.yaml and use S256 method for better security.
Run Example:
dart run example/authentication/oauth_client_example.dartFile: oauth_server_example.dart
✅ FULLY COMPLIANT with MCP OAuth Specification (2025-06-18)
Production-ready MCP server with OAuth 2.0 authentication that meets all MCP security requirements:
MCP Specification Compliance:
- ✅ PKCE Support (RFC 7636) - Requires code_verifier in authorization code exchange
- ✅ Resource Parameter - Includes resource parameter in all token requests for audience validation
- ✅ Token Audience Validation - Validates tokens are specific to this MCP server
- ✅ Redirect URI Validation - Validates redirect URIs against allowed list
- ✅ OAuth Metadata Discovery - Implements /.well-known/oauth-authorization-server endpoint
- ✅ WWW-Authenticate Header - Returns proper 401 responses with authorization server location
⚠️ HTTPS Support - Optional HTTPS with self-signed cert (use reverse proxy for production)
Additional Features:
- OAuth token validation with provider APIs
- Multi-provider support (GitHub, Google, custom)
- Authorization code exchange with PKCE
- Token refresh handling
- Scope-based access control
- Session management with OAuth tokens
- User context in tool handlers
// Create OAuth configuration
final config = OAuthServerConfig.github(
clientId: Platform.environment['GITHUB_CLIENT_ID']!,
clientSecret: Platform.environment['GITHUB_CLIENT_SECRET']!,
requiredScopes: ['repo', 'read:user'],
);
// Create OAuth validator
final validator = OAuthServerValidator(config);
// Wrap transport with OAuth authentication
final authenticatedTransport = OAuthServerTransport(
transport: innerTransport,
validator: validator,
);
// Create server
final server = createOAuthMcpServer();
await server.connect(authenticatedTransport);Key Features:
- Real OAuth provider integration (GitHub, Google)
- Token validation with user info lookup
- Scope verification and access control
- Token caching with TTL
- Session-token association
- User context in request handlers
- Production security best practices
Run Example:
# GitHub OAuth (HTTP mode - development only)
export GITHUB_CLIENT_ID=your_client_id
export GITHUB_CLIENT_SECRET=your_client_secret
dart run example/authentication/oauth_server_example.dart github
# GitHub OAuth (HTTPS mode with self-signed certificate)
dart run example/authentication/oauth_server_example.dart github --https
# Google OAuth
export GOOGLE_CLIENT_ID=your_client_id
export GOOGLE_CLIENT_SECRET=your_client_secret
dart run example/authentication/oauth_server_example.dart googleTest MCP Compliance:
# 1. Check OAuth metadata discovery
curl http://localhost:3000/.well-known/oauth-authorization-server | jq
# 2. Verify WWW-Authenticate header on unauthorized request
curl -v http://localhost:3000/mcp 2>&1 | grep "WWW-Authenticate"
# 3. Test with valid OAuth token (requires actual token from provider)
curl -H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
http://localhost:3000/mcp- Choose your OAuth provider (GitHub, Google, or custom)
- Implement the
OAuthClientProviderinterface - Configure your transport with the auth provider
- Connect and use the MCP client
// 1. Create auth provider
final authProvider = GitHubOAuthProvider(
config: config,
storage: GitHubTokenStorage('.tokens.json'),
);
// 2. Create transport with auth
final transport = StreamableHttpClientTransport(
Uri.parse('https://api.githubcopilot.com/mcp/'),
opts: StreamableHttpClientTransportOptions(
authProvider: authProvider,
),
);
// 3. Create and connect client
final client = Client(
Implementation(name: 'my-client', version: '1.0.0'),
);
await client.connect(transport);
// 4. Use the client
final tools = await client.listTools();- Create your base server transport
- Configure OAuth validator with provider settings
- Wrap the transport with OAuth authentication
- Handle requests through the authenticated transport
// 1. Create base transport
final innerTransport = StreamableHTTPServerTransport(
options: StreamableHTTPServerTransportOptions(
sessionIdGenerator: () => generateUUID(),
),
);
// 2. Create OAuth validator
final validator = OAuthServerValidator(
OAuthServerConfig.github(
clientId: githubClientId,
clientSecret: githubClientSecret,
requiredScopes: ['repo', 'read:user'],
),
);
// 3. Wrap with OAuth authentication
final authenticatedTransport = OAuthServerTransport(
transport: innerTransport,
validator: validator,
);
// 4. Create server and handle requests
final server = McpServer(
Implementation(name: 'secure-server', version: '1.0.0'),
);
await authenticatedTransport.start();
await server.connect(authenticatedTransport);
// Handle HTTP requests
httpServer.listen((request) async {
await authenticatedTransport.handleRequest(request);
});Client Auth Server MCP Server
| | |
|--Request Access---------->| |
|<--Authorization URL-------| |
| | |
|--User Authorizes--------->| |
|<--Authorization Code------| |
| | |
|--Exchange Code + PKCE---->| |
|<--Access + Refresh--------| |
| |
|--Connect with Token----------------------------> |
|<--Session Established--------------------------- |
| |
|--Token Expired---------------------------------> |
|<--Refresh Token--------------------------------- |
- Never hardcode tokens or secrets in your code
- Use environment variables for development
- Implement secure storage (system keychain, encrypted storage) for production
- Clear tokens on logout or session end
// ❌ Bad - hardcoded
final authProvider = GitHubPATProvider(token: 'ghp_xxxxx');
// ✅ Good - from environment
final authProvider = GitHubPATProvider(
token: Platform.environment['GITHUB_TOKEN']!,
);
// ✅ Better - from secure storage
final token = await secureStorage.read(key: 'github_token');
final authProvider = GitHubPATProvider(token: token);- Track expiration times for all tokens
- Refresh proactively before expiration
- Handle refresh failures gracefully
- Implement exponential backoff for retries
class SmartOAuthProvider implements OAuthClientProvider {
@override
Future<OAuthTokens?> tokens() async {
if (_currentToken?.isExpiringSoon ?? false) {
await _refreshToken(); // Proactive refresh
}
return _currentToken;
}
}- Always validate tokens on the server
- Verify token audience matches your server URI
- Check scopes match required permissions
- Cache validation results with appropriate TTL
- Rate limit authentication attempts
- Always use HTTPS in production
- Never send tokens over unencrypted connections
- Use reverse proxy (nginx, Caddy) for production HTTPS
// ✅ Good - HTTPS
final transport = StreamableHttpClientTransport(
Uri.parse('https://api.example.com/mcp'),
// ...
);
// ❌ Bad - HTTP (only for local development)
final transport = StreamableHttpClientTransport(
Uri.parse('http://localhost:3000/mcp'),
// ...
);- Verify token validity: Check if the token has expired
- Check token format: Ensure Bearer prefix is included
- Validate scopes: Verify required scopes are present
- Server validation: Confirm server is configured correctly
- Check refresh token: Verify refresh token is valid and not expired
- Network issues: Ensure connectivity to auth server
- Rate limiting: Implement backoff for retry attempts
- Token revocation: Handle revoked tokens gracefully
- Redirect URI mismatch: Ensure redirect URIs match exactly
- State parameter: Verify state validation for CSRF protection
- PKCE validation: Check code_verifier matches code_challenge
- Scope requests: Verify requested scopes are allowed by provider
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: API Reference
These examples are provided under the same license as the MCP Dart SDK.