Auth Provider Setup Guide
This guide walks you through configuring Casdoor as the authorization server for your MCP server. You'll configure the Casdoor side (application settings, scopes, consent) and the MCP server side (Protected Resource Metadata, token validation).
Prerequisites
- A running Casdoor instance (see Server Installation or use Casdoor Cloud)
- Admin access to Casdoor's management interface
- Your MCP server's public URL (e.g.,
https://your-mcp-server.com)
Part 1: Casdoor Configuration
Step 1: Create an Application
Navigate to the Casdoor admin panel and create a new application for your MCP server.
- Go to Applications → Add Application
- Fill in the basic details:
- Name: Choose a descriptive name (e.g., "My Files MCP Server")
- Organization: Select the organization that will own this application
- Category: Select Agent (this unlocks MCP-specific features)
- Type: Select MCP (automatically set when Category is Agent)
The Agent category with MCP type optimizes the application for programmatic access and enables custom scope configuration.
Reference: See Application Categories for more details on Agent vs Default applications.
Step 2: Configure Redirect URIs
Redirect URIs are where Casdoor sends users after authorization. For MCP servers, you need to support both development and production environments.
- In the application settings, find Redirect URIs
- Add development URIs for local testing:
http://localhost:*(wildcard for any local port)http://127.0.0.1:*
- Add production URIs for deployment:
https://your-mcp-server.com/oauth/callback- Any other callback URLs your server uses
Example configuration:
http://localhost:*
http://127.0.0.1:*
https://mcp.example.com/oauth/callback
Security note: Wildcard URIs (*) are convenient for development but should be restricted in production. Consider using exact URLs for production deployments.
Step 3: Set Grant Types
Configure which OAuth grant types your application supports.
- Find the Grant types setting
- Enable these grant types:
- ✅ authorization_code: The primary OAuth flow for MCP
- ✅ refresh_token: Allows long-lived access without re-authentication
Do not enable:
- ❌ implicit: Deprecated and insecure for MCP use
- ❌ password: Not compatible with MCP's authorization flow
Step 4: Define Custom Scopes
Custom scopes represent the permissions your MCP server's tools require. Each scope should map to a logical capability.
- Scroll to Custom Scopes section
- Click Add Scope for each permission you want to define
- For each scope, provide:
- Name: Use
resource:actionformat (e.g.,files:read,db:query) - Display Name: Short, user-friendly name (e.g., "Read Files")
- Description: Explain what the scope allows (e.g., "View and download files from your storage")
- Name: Use
Example scopes for a file management MCP server:
| Name | Display Name | Description |
|---|---|---|
files:read | Read Files | View and download files from your storage |
files:write | Write Files | Create, modify, and delete files in your storage |
files:list | List Files | See file names and metadata in directories |
Example scopes for a database MCP server:
| Name | Display Name | Description |
|---|---|---|
db:query | Query Database | Execute read-only database queries |
db:modify | Modify Database | Create, update, and delete database records |
db:admin | Database Admin | Manage schemas, tables, and database settings |
Best practices:
- Keep scopes granular—users should be able to grant partial access
- Use consistent naming (e.g.,
resource:actionpattern) - Write clear descriptions—users see these during authorization
- Start with fewer scopes, add more as your tools evolve
Reference: See Custom Scopes for detailed guidance.
Step 5: Configure Consent Policy
The consent screen shows users what permissions they're granting.
- Find Consent Policy setting
- Choose one of:
- Always: Show consent screen on every authorization (recommended for sensitive data)
- Once: Show consent only on first authorization (better UX for frequent access)
- Never: Skip consent (only for trusted first-party applications)
For MCP servers with powerful tools (file access, database modifications), we recommend Always or Once to ensure users understand what they're authorizing.
Step 6: Enable Dynamic Client Registration (Optional)
If your MCP server will be distributed to end users who need to register their own clients:
- Go to Organization Settings (not Application Settings)
- Find Enable Dynamic Client Registration
- Toggle to Enabled
When enabled, anyone can register new OAuth clients via /api/oauth/register without admin intervention. This is essential for MCP clients like Claude Desktop that auto-register on first use.
Reference: See Dynamic Client Registration for implementation details.
Step 7: Note Your Discovery URLs
Your MCP server will need these Casdoor URLs for token validation:
- Authorization Server Metadata:
https://your-casdoor.com/.well-known/oauth-authorization-server - OIDC Discovery:
https://your-casdoor.com/.well-known/openid-configuration - JWKS Endpoint:
https://your-casdoor.com/.well-known/jwks
You can verify these endpoints are working:
# Check OAuth metadata
curl https://your-casdoor.com/.well-known/oauth-authorization-server
# Check OIDC discovery
curl https://your-casdoor.com/.well-known/openid-configuration
# Check JWKS (JSON Web Key Set for token validation)
curl https://your-casdoor.com/.well-known/jwks
Part 2: MCP Server Configuration
Now configure your MCP server to use Casdoor for authentication.
Step 1: Implement Protected Resource Metadata Endpoint
Your MCP server must serve a JSON document at /.well-known/oauth-protected-resource that tells clients where to find the authorization server.
Endpoint: GET /.well-known/oauth-protected-resource
Response:
{
"resource": "https://your-mcp-server.com",
"authorization_servers": ["https://your-casdoor.com"],
"scopes_supported": [
"files:read",
"files:write",
"files:list"
],
"bearer_methods_supported": ["header"]
}
Field descriptions:
resource: Your MCP server's public URL (used as theaudclaim in tokens)authorization_servers: Array of OAuth servers (just Casdoor's URL)scopes_supported: The custom scopes you defined in Step 4bearer_methods_supported: Always["header"]for MCP (tokens in Authorization header)
Example implementations:
Python (Flask)
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/.well-known/oauth-protected-resource')
def protected_resource_metadata():
return jsonify({
"resource": "https://your-mcp-server.com",
"authorization_servers": ["https://your-casdoor.com"],
"scopes_supported": ["files:read", "files:write", "files:list"],
"bearer_methods_supported": ["header"]
})
Node.js (Express)
const express = require('express');
const app = express();
app.get('/.well-known/oauth-protected-resource', (req, res) => {
res.json({
resource: 'https://your-mcp-server.com',
authorization_servers: ['https://your-casdoor.com'],
scopes_supported: ['files:read', 'files:write', 'files:list'],
bearer_methods_supported: ['header']
});
});
Go (net/http)
package main
import (
"encoding/json"
"net/http"
)
type ProtectedResourceMetadata struct {
Resource string `json:"resource"`
AuthorizationServers []string `json:"authorization_servers"`
ScopesSupported []string `json:"scopes_supported"`
BearerMethodsSupported []string `json:"bearer_methods_supported"`
}
func protectedResourceHandler(w http.ResponseWriter, r *http.Request) {
metadata := ProtectedResourceMetadata{
Resource: "https://your-mcp-server.com",
AuthorizationServers: []string{"https://your-casdoor.com"},
ScopesSupported: []string{"files:read", "files:write", "files:list"},
BearerMethodsSupported: []string{"header"},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(metadata)
}
func main() {
http.HandleFunc("/.well-known/oauth-protected-resource", protectedResourceHandler)
http.ListenAndServe(":8080", nil)
}
Step 2: Return 401 Challenges on Unauthorized Requests
When an MCP client connects without a valid token, your server must return a 401 response with a WWW-Authenticate header.
Response headers:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="MCP Server", resource_metadata="https://your-mcp-server.com/.well-known/oauth-protected-resource"
Content-Type: application/json
Response body:
{
"error": "unauthorized",
"message": "Authentication required. Use OAuth 2.0 with the authorization server listed in the Protected Resource Metadata."
}
The resource_metadata parameter in the WWW-Authenticate header tells the client where to find your Protected Resource Metadata endpoint.
Step 3: Validate JWT Tokens
When a client sends a request with a Bearer token, your server must validate it against Casdoor's JWKS endpoint.
Token validation steps:
- Extract the token from the
Authorization: Bearer <token>header - Fetch JWKS from
https://your-casdoor.com/.well-known/jwks - Verify the signature using the public key from JWKS
- Check the
audclaim matches your server's resource URI (https://your-mcp-server.com) - Check the
expclaim to ensure the token hasn't expired - Extract scopes from the
scopeclaim (space-separated string)
See Third-party Integration for complete code examples in Python, Node.js, and Go.
Step 4: Enforce Scopes in Tool Handlers
Each MCP tool should check that the token contains the required scopes.
Example: A read_file tool requires the files:read scope:
def read_file(token_scopes, file_path):
if "files:read" not in token_scopes:
raise PermissionError("Missing required scope: files:read")
# Proceed with file reading
with open(file_path, 'r') as f:
return f.read()
Best practices:
- Check scopes before executing any privileged operation
- Return clear error messages when scopes are missing
- Log authorization failures for security auditing
Testing Your Setup
Test the Protected Resource Metadata
curl https://your-mcp-server.com/.well-known/oauth-protected-resource
Expected output:
{
"resource": "https://your-mcp-server.com",
"authorization_servers": ["https://your-casdoor.com"],
"scopes_supported": ["files:read", "files:write"],
"bearer_methods_supported": ["header"]
}
Test the 401 Challenge
curl -i https://your-mcp-server.com/api/mcp
Expected response:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="MCP Server", resource_metadata="https://your-mcp-server.com/.well-known/oauth-protected-resource"
Test with Claude Desktop (or other MCP client)
- Configure your MCP client to connect to
https://your-mcp-server.com - The client should automatically discover Casdoor via Protected Resource Metadata
- The client will redirect you to Casdoor's authorization page
- After consenting, the client receives a token and can call your tools
Troubleshooting
"Invalid redirect URI" error
Problem: Casdoor rejects the authorization request with an invalid redirect URI error.
Solution: Verify that the client's redirect URI is listed in Step 2 (Redirect URIs). For Claude Desktop, you may need http://localhost:*.
"Token signature verification failed"
Problem: Your server can't verify Casdoor's JWT tokens.
Solution:
- Ensure you're using the correct JWKS endpoint:
https://your-casdoor.com/.well-known/jwks - Check that your server's system clock is synchronized (JWT expiry is time-sensitive)
- Verify the token is a valid JWT (use jwt.io to inspect it)
"Audience mismatch" error
Problem: The token's aud claim doesn't match your server's resource URI.
Solution: Ensure the resource field in your Protected Resource Metadata exactly matches your server's public URL, including scheme (https://) and no trailing slash.
Consent screen not showing
Problem: Users are not seeing the consent screen when authorizing.
Solution: Check the Consent Policy in Step 5. If set to "Never", the consent screen is skipped. Change to "Once" or "Always".
Next Steps
- Integration Examples →: See complete, runnable code in Python, Node.js, and Go
- MCP Specification →: Read the official MCP authorization spec
- OAuth 2.0 in Casdoor →: Learn more about Casdoor's OAuth implementation
- OIDC Client →: Understand Casdoor's OIDC capabilities