Authentication & Authorization

OSS and Enterprise authentication modes, scopes, permissions, API tokens, and HTTP Basic Auth support.

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:

  1. Client sends credentials to POST /v1/auth/login
  2. Registry validates credentials against its local user database
  3. Registry returns a signed JWT with scopes, resources, and organization claims
  4. 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
KeyDescriptionDefault
issuerValue of the iss claim in issued JWTsmcp-registry-oss
audienceValue of the aud claim in issued JWTsmcp-registry
issuer_secretSecret for signing JWTs (must be >= 32 bytes)Required
enable_basicAllow HTTP Basic Auth for API requestsfalse

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:

  1. Client obtains a JWT from the external IdP
  2. Client sends the JWT to the registry in the Authorization header
  3. Registry fetches the public key from the JWKS endpoint (cached)
  4. Registry validates the JWT signature, issuer, audience, and expiration
  5. 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"
KeyDescription
jwks_urlURL to the JWKS endpoint of your IdP
issuerExpected iss claim value (must match exactly, including trailing slash)
audienceExpected 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:

ProviderJWKS URL
Auth0https://{tenant}.auth0.com/.well-known/jwks.json
Oktahttps://{domain}.okta.com/oauth2/default/v1/keys
Azure ADhttps://login.microsoftonline.com/{tenant}/discovery/v2.0/keys
Keycloakhttps://{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

JWT Claims Structure

The registry expects the following claims in JWTs:

Standard Claims

ClaimRequiredDescription
issYesToken issuer (must match configured issuer)
audYesToken audience (must match configured audience)
subYesSubject (user or service identifier)
expYesExpiration time (Unix timestamp)
nbfNoNot before time (recommended)
iatNoIssued at time (recommended)

Custom Claims

ClaimTypeRequiredDescription
scopes[]stringYesPermission scopes granted to the token
resources[]stringYesResource patterns the token can access
orgs[]stringNoOrganization IDs the user belongs to
envs[]stringNoEnvironment 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

ScopeDescriptionTypical Users
mcp:catalog:readBrowse package catalogAll users
mcp:resolveResolve published versions to artifact URLsClients, executors
mcp:resolve:prepublishResolve unpublished versions (draft, ingested)CI/CD pipelines
mcp:publishPublish new versions and update statusPublishers, CI/CD
artifact:downloadDownload manifest and bundle artifactsClients, executors
evidence:readDownload evidence artifactsSecurity tools, auditors
token:createCreate new API tokensAdministrators
token:listList existing API tokensAdministrators
token:deleteDelete (revoke) API tokensAdministrators

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

PatternResourceMatch
org/acme/org/acme/mcp/fooYes
org/acme/org/acme/artifact/sha256:abc/bundleYes
org/acme/org/other/mcp/fooNo
catalogcatalogYes
catalogorg/acme/catalogNo
org/*/mcp/*org/acme/mcp/fooYes
org/*/mcp/*org/other/mcp/barYes
org/*/mcp/*org/acme/catalogNo

Security Best Practices for Resources

  • Prefer specific patterns over wildcards: org/acme/mcp/weather-service is better than org/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"
}

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_in in 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:

  1. Parse the Authorization header (Bearer JWT, Token, or Basic)
  2. Verify signature (HS256 with shared secret for OSS, RS256/EdDSA via JWKS for Enterprise, bcrypt comparison for API tokens)
  3. Check exp and nbf claims (reject expired or not-yet-valid tokens)
  4. Verify iss matches configured issuer
  5. Verify aud matches configured audience
  6. Extract claims (scopes, resources, orgs)
  7. Check scope against the required scope for the endpoint
  8. 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
SettingEffect When true
read_catalogGET /v1/catalog works without authentication (returns only public packages)
download_artifactsArtifacts from public packages can be downloaded without authentication

In both cases, private packages always require authentication.

Troubleshooting

“invalid token” Error (401)

  1. Check the token has not expired (exp claim)
  2. Verify iss matches the configured issuer exactly (including trailing slash in enterprise mode)
  3. Verify aud matches the configured audience
  4. In enterprise mode, verify the JWKS URL is accessible

“not authorized” Error (403)

  1. Decode the token and check the scopes array includes the required scope
  2. Check the resources array includes a pattern matching the requested resource
  3. 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

  1. Set up the external IdP with an MCP Registry application
  2. Configure claim mappings to include scopes and resources in the access token
  3. Test with a separate registry instance in enterprise mode
  4. Migrate clients to obtain tokens from the IdP instead of /v1/auth/login
  5. Switch production: change auth.mode to enterprise

Note: API tokens created in OSS mode will not work in enterprise mode. Clients must switch to IdP-issued JWTs or recreate API tokens.