Authentication & Authorization
7 min read
MCP Registry supports two authentication modes designed for different deployment scenarios: OSS mode for small teams and development, and Enterprise mode for organizations with existing identity infrastructure.
Authentication Modes
OSS Mode
In OSS mode, the registry manages users internally and issues its own JWTs using HS256 (HMAC with SHA-256).
How it works:
- Client sends credentials to
POST /v1/auth/login - Registry validates credentials against its local user database
- Registry returns a signed JWT with scopes, resources, and organization claims
- Client uses the JWT in subsequent requests
# Obtain a JWT
curl -X POST https://registry.example.com/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "secret"}'
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 900
}
Configuration:
auth:
mode: "oss"
oss:
issuer: "mcp-registry-oss"
audience: "mcp-registry"
issuer_secret: "your-secret-here-at-least-32-bytes"
enable_basic: false
| Key | Description | Default |
|---|---|---|
issuer | Value of the iss claim in issued JWTs | mcp-registry-oss |
audience | Value of the aud claim in issued JWTs | mcp-registry |
issuer_secret | Secret for signing JWTs (must be >= 32 bytes) | Required |
enable_basic | Allow HTTP Basic Auth for API requests | false |
The issuer_secret must be at least 32 characters. The server will refuse to start if a shorter secret is provided. Generate a strong secret with: openssl rand -base64 32
Enterprise Mode
In Enterprise mode, the registry validates externally-issued JWTs via a JWKS (JSON Web Key Set) endpoint. The registry does not manage users or issue tokens – all authentication is delegated to your identity provider (Auth0, Okta, Azure AD, Keycloak, etc.).
How it works:
- Client obtains a JWT from the external IdP
- Client sends the JWT to the registry in the Authorization header
- Registry fetches the public key from the JWKS endpoint (cached)
- Registry validates the JWT signature, issuer, audience, and expiration
- Registry extracts scopes and resource claims from the token
In enterprise mode, the /v1/auth/login endpoint returns 501 Not Implemented.
Configuration:
auth:
mode: "enterprise"
enterprise:
jwks_url: "https://idp.example.com/.well-known/jwks.json"
issuer: "https://idp.example.com/"
audience: "mcp-registry"
| Key | Description |
|---|---|
jwks_url | URL to the JWKS endpoint of your IdP |
issuer | Expected iss claim value (must match exactly, including trailing slash) |
audience | Expected aud claim value |
Supported algorithms: RS256 (RSA with SHA-256) and EdDSA (Ed25519). HS256 is not supported in enterprise mode since it requires a shared secret.
Common JWKS URLs:
| Provider | JWKS URL |
|---|---|
| Auth0 | https://{tenant}.auth0.com/.well-known/jwks.json |
| Okta | https://{domain}.okta.com/oauth2/default/v1/keys |
| Azure AD | https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys |
| Keycloak | https://{host}/realms/{realm}/protocol/openid-connect/certs |
Authentication Methods
Regardless of the mode, the registry accepts three authentication methods on API requests:
Bearer Token (JWT)
The primary authentication method. Pass the JWT in the Authorization header:
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
https://registry.example.com/v1/catalog
API Token
Long-lived tokens for CI/CD pipelines and service accounts. Pass the token ID and secret:
curl -H "Authorization: Token mcp_abc123:sk_fedcba654321..." \
https://registry.example.com/v1/catalog
See API Token Management below for creating and managing tokens.
HTTP Basic Auth (OSS Mode Only)
When auth.oss.enable_basic is true, clients can authenticate with username and password:
curl -u "admin:secret" \
https://registry.example.com/v1/catalog
HTTP Basic Auth transmits credentials on every request (base64-encoded, not encrypted). Only enable this over HTTPS. Prefer Bearer tokens for programmatic access.
JWT Claims Structure
The registry expects the following claims in JWTs:
Standard Claims
| Claim | Required | Description |
|---|---|---|
iss | Yes | Token issuer (must match configured issuer) |
aud | Yes | Token audience (must match configured audience) |
sub | Yes | Subject (user or service identifier) |
exp | Yes | Expiration time (Unix timestamp) |
nbf | No | Not before time (recommended) |
iat | No | Issued at time (recommended) |
Custom Claims
| Claim | Type | Required | Description |
|---|---|---|---|
scopes | []string | Yes | Permission scopes granted to the token |
resources | []string | Yes | Resource patterns the token can access |
orgs | []string | No | Organization IDs the user belongs to |
envs | []string | No | Environment identifiers (reserved for future use) |
Example Token Payload (OSS Mode)
{
"iss": "mcp-registry-oss",
"aud": "mcp-registry",
"sub": "alice",
"exp": 1710003600,
"iat": 1710000000,
"scopes": [
"mcp:catalog:read",
"mcp:resolve",
"artifact:download"
],
"resources": [
"catalog",
"org/acme/"
],
"orgs": ["acme"]
}
Example Token Payload (Enterprise Mode)
{
"iss": "https://auth.company.com/",
"aud": "mcp-registry",
"sub": "sa-ci-pipeline",
"exp": 1710003600,
"iat": 1710000000,
"scopes": [
"mcp:publish",
"mcp:resolve:prepublish"
],
"resources": [
"org/platform/mcp/weather-service"
]
}
Scopes and Permissions
Scopes control what operations a token can perform. Every API call requires both the correct scope and a matching resource pattern.
Available Scopes
| Scope | Description | Typical Users |
|---|---|---|
mcp:catalog:read | Browse package catalog | All users |
mcp:resolve | Resolve published versions to artifact URLs | Clients, executors |
mcp:resolve:prepublish | Resolve unpublished versions (draft, ingested) | CI/CD pipelines |
mcp:publish | Publish new versions and update status | Publishers, CI/CD |
artifact:download | Download manifest and bundle artifacts | Clients, executors |
evidence:read | Download evidence artifacts | Security tools, auditors |
token:create | Create new API tokens | Administrators |
token:list | List existing API tokens | Administrators |
token:delete | Delete (revoke) API tokens | Administrators |
Recommended Scope Sets
Read-only user (browse and download):
["mcp:catalog:read", "mcp:resolve", "artifact:download"]
Publisher (CI/CD pipeline):
["mcp:publish", "mcp:resolve", "mcp:resolve:prepublish", "artifact:download"]
Administrator (full access):
["mcp:catalog:read", "mcp:resolve", "mcp:resolve:prepublish", "mcp:publish",
"artifact:download", "evidence:read", "token:create", "token:list", "token:delete"]
Resource Patterns
Resource patterns in the resources claim control which specific resources a token can access. The authorization system matches the requested resource against each pattern in the token.
Pattern Types
Prefix patterns (ending with /):
org/acme/ -- Matches all resources under org "acme"
org/ -- Matches all resources under any org
Exact patterns:
catalog -- Matches global catalog only
org/acme/catalog -- Matches org catalog only
Glob patterns (using *):
org/*/mcp/* -- Matches any package in any org
org/acme/mcp/* -- Matches any package in org "acme"
Resource Matching Examples
| Pattern | Resource | Match |
|---|---|---|
org/acme/ | org/acme/mcp/foo | Yes |
org/acme/ | org/acme/artifact/sha256:abc/bundle | Yes |
org/acme/ | org/other/mcp/foo | No |
catalog | catalog | Yes |
catalog | org/acme/catalog | No |
org/*/mcp/* | org/acme/mcp/foo | Yes |
org/*/mcp/* | org/other/mcp/bar | Yes |
org/*/mcp/* | org/acme/catalog | No |
Security Best Practices for Resources
- Prefer specific patterns over wildcards:
org/acme/mcp/weather-serviceis better thanorg/acme/ - CI/CD tokens should be scoped to specific packages they publish
- Avoid
org/*/(matches everything) unless absolutely necessary - Grant the narrowest access possible
API Token Management
API tokens are long-lived credentials designed for CI/CD pipelines and service accounts. They provide a way to authenticate without needing to repeatedly obtain JWTs.
Creating a Token
curl -X POST https://registry.example.com/v1/tokens \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"description": "GitHub Actions - weather-service",
"scopes": ["mcp:publish", "mcp:resolve"],
"resources": ["org/acme/mcp/weather-service"],
"expires_in": 2592000
}'
Required scope: token:create
Response:
{
"token_id": "mcp_abc123def456",
"secret": "sk_fedcba654321...",
"expires_at": "2024-03-15T10:30:00Z"
}
The secret value is only shown once at creation time. Store it securely (e.g., in your CI/CD platform’s secrets management). If lost, delete the token and create a new one.
Listing Tokens
curl -H "Authorization: Bearer $JWT" \
https://registry.example.com/v1/tokens
Required scope: token:list
Deleting a Token
curl -X DELETE \
https://registry.example.com/v1/tokens/mcp_abc123def456 \
-H "Authorization: Bearer $JWT"
Required scope: token:delete
Using API Tokens
API tokens are passed in the Authorization header with the Token scheme:
curl -H "Authorization: Token mcp_abc123def456:sk_fedcba654321..." \
https://registry.example.com/v1/catalog
The format is Token {token_id}:{secret}.
Token Security
- API token secrets are hashed with bcrypt before storage – the registry never stores plaintext secrets
- Tokens inherit the scopes and resources specified at creation time
- Tokens have an expiration date (
expires_inin seconds, default: 30 days) - Compromised tokens should be deleted immediately
- Rotate tokens periodically as a security best practice
Token Validation Flow
When a request arrives, the registry validates the token through this sequence:
- Parse the Authorization header (Bearer JWT, Token, or Basic)
- Verify signature (HS256 with shared secret for OSS, RS256/EdDSA via JWKS for Enterprise, bcrypt comparison for API tokens)
- Check
expandnbfclaims (reject expired or not-yet-valid tokens) - Verify
issmatches configured issuer - Verify
audmatches configured audience - Extract claims (
scopes,resources,orgs) - Check scope against the required scope for the endpoint
- Check resource pattern against the requested resource
If any step fails, the request is rejected with 401 Unauthorized or 403 Forbidden.
Public Access Controls
The registry can be configured to allow certain operations without authentication:
public:
read_catalog: true
download_artifacts: false
| Setting | Effect When true |
|---|---|
read_catalog | GET /v1/catalog works without authentication (returns only public packages) |
download_artifacts | Artifacts from public packages can be downloaded without authentication |
In both cases, private packages always require authentication.
Troubleshooting
“invalid token” Error (401)
- Check the token has not expired (
expclaim) - Verify
issmatches the configured issuer exactly (including trailing slash in enterprise mode) - Verify
audmatches the configured audience - In enterprise mode, verify the JWKS URL is accessible
“not authorized” Error (403)
- Decode the token and check the
scopesarray includes the required scope - Check the
resourcesarray includes a pattern matching the requested resource - Verify resource patterns use the correct format (prefix, exact, or glob)
Decoding a JWT for Debugging
# Decode the payload (without verification)
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
Or use jwt.io for a visual inspector. Do not paste production tokens into third-party websites.
Migrating from OSS to Enterprise Mode
- Set up the external IdP with an MCP Registry application
- Configure claim mappings to include
scopesandresourcesin the access token - Test with a separate registry instance in enterprise mode
- Migrate clients to obtain tokens from the IdP instead of
/v1/auth/login - Switch production: change
auth.modetoenterprise
Note: API tokens created in OSS mode will not work in enterprise mode. Clients must switch to IdP-issued JWTs or recreate API tokens.