Gateway Authentication
Every gateway request is authenticated before any tool is executed. almyty enforces a default-deny policy — gateways with no auth configured reject all requests.
Authentication Methods
| Method | Header / Parameter | Use Case |
|---|---|---|
| None | — | Public/development gateways (must be explicitly configured) |
| API Key | X-API-Key header or api_key query param | Machine-to-machine |
| Bearer Token | Authorization: Bearer <token> | Programmatic access |
| Basic Auth | Authorization: Basic <base64> | Legacy integrations |
| JWT | Authorization: Bearer <jwt> | Signed, self-contained tokens |
| OAuth 2.1 | Authorization: Bearer <oauth-token> | Full authorization code flow with PKCE |
How Enforcement Works
Every request to /mcp/*, /utcp/*, or /a2a/* passes through the same
enforcement pipeline:
- Resolve the organization and gateway from the URL
- Load all active auth configs for that gateway
- If no auth configs exist → reject (401)
- Try each required auth method in order — first success wins
- If all fail → reject with the last error (401 or 403)
- If valid → execute the request
Discovery endpoints (.well-known/*, agent cards) are always public.
Configuring Authentication
Via the UI
- Open the gateway detail page
- Click the Authentication tab
- Click Add Auth Method
- Select the type and configure
Via the API
curl -X POST https://api.almyty.com/gateways/{id}/auth \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "api_key",
"isRequired": true,
"configuration": {
"keyHeader": "X-API-Key"
}
}'API Key Authentication
API keys are stored as SHA-256 hashes in the database. The raw key is only shown once at creation time.
Generate a Key
curl -X POST https://api.almyty.com/gateways/{id}/auth/api-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Production Key",
"scopes": ["tools:read", "tools:execute"],
"expiresAt": "2027-01-01T00:00:00Z"
}'Response (key shown once):
{
"id": "key-uuid",
"name": "Production Key",
"key": "almyty_gw_abc123...",
"scopes": ["tools:read", "tools:execute"],
"expiresAt": "2027-01-01T00:00:00Z"
}Using the Key
# Via custom header (default: X-API-Key)
curl https://api.almyty.com/mcp/acme/my-tools \
-H "X-API-Key: almyty_gw_abc123..."
# Via query parameter (default: api_key)
curl "https://api.almyty.com/mcp/acme/my-tools?api_key=almyty_gw_abc123..."Validation
- Key is hashed with SHA-256 and looked up in the database
- Checks: key exists, is active, not expired
- Supports gateway-specific keys and org-wide keys
- Optional: min/max key length, regex format validation
lastUsedAtis updated on each successful request
Manage Keys
# List keys
curl https://api.almyty.com/gateways/{id}/auth/api-keys \
-H "Authorization: Bearer $TOKEN"
# Revoke a key
curl -X DELETE https://api.almyty.com/gateways/{id}/auth/api-keys/{keyId} \
-H "Authorization: Bearer $TOKEN"Bearer Token Authentication
Bearer tokens use the same infrastructure as API keys — they're hashed with
SHA-256 and looked up in the ApiKey table.
curl https://api.almyty.com/mcp/acme/my-tools \
-H "Authorization: Bearer almyty_gw_abc123..."Basic Auth
Decodes the Authorization: Basic header, looks up the user by email,
and verifies the password with bcrypt.
curl https://api.almyty.com/mcp/acme/my-tools \
-H "Authorization: Basic $(echo -n 'user@example.com:password' | base64)"The authenticated user's organization membership and roles are included in the auth result.
JWT Authentication
Verifies JWT signature using the secret configured on the auth method
(or falls back to the server's JWT_SECRET).
curl https://api.almyty.com/mcp/acme/my-tools \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."The JWT payload must contain sub or userId. Scopes are read from
payload.scopes (array) or payload.scope (space-separated string).
Configure the secret when adding JWT auth to a gateway:
curl -X POST https://api.almyty.com/gateways/{id}/auth \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "jwt",
"isRequired": true,
"configuration": {
"secret": "your-jwt-signing-secret"
}
}'OAuth 2.1 (MCP OAuth)
almyty implements a full OAuth 2.1 authorization server per the MCP specification (opens in a new tab), supporting:
- Authorization Code flow with PKCE (S256 required)
- Dynamic Client Registration (RFC 7591)
- Token Revocation (RFC 7009)
- Authorization Server Metadata (RFC 8414)
- Protected Resource Metadata (RFC 9728)
Discovery
Every MCP gateway with OAuth enabled exposes metadata endpoints:
# Authorization server metadata
curl https://api.almyty.com/mcp/acme/my-tools/.well-known/oauth-authorization-server
# Protected resource metadata
curl https://api.almyty.com/mcp/acme/my-tools/.well-known/oauth-protected-resourceClient Registration
Clients register dynamically before starting the OAuth flow:
curl -X POST https://api.almyty.com/mcp/acme/my-tools/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My MCP Client",
"redirect_uris": ["http://localhost:3000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}'Response:
{
"client_id": "mcp_client_abc123...",
"client_name": "My MCP Client",
"redirect_uris": ["http://localhost:3000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"client_id_issued_at": 1711234567
}Redirect URIs must use HTTPS, except localhost and 127.0.0.1 for
development.
Authorization Code Flow
Step 1 — Redirect the user to the authorize endpoint:
GET https://api.almyty.com/mcp/acme/my-tools/authorize
?response_type=code
&client_id=mcp_client_abc123...
&redirect_uri=http://localhost:3000/callback
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&scope=tools:read tools:execute
&state=random-state-valuecode_challenge_method=S256is mandatory (OAuth 2.1 requires PKCE)- If the user is not logged in, they're redirected to the login page first
- After authentication, an authorization code is generated (10-minute lifetime)
Step 2 — Exchange the code for tokens:
curl -X POST https://api.almyty.com/mcp/acme/my-tools/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code\
&code=auth_code_here\
&redirect_uri=http://localhost:3000/callback\
&client_id=mcp_client_abc123...\
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"Response:
{
"access_token": "almyty_at_...",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "almyty_rt_...",
"scope": "tools:read tools:execute"
}- Access tokens expire in 1 hour
- Refresh tokens expire in 30 days
- Both are stored as SHA-256 hashes (never plaintext)
Step 3 — Use the access token:
curl -X POST https://api.almyty.com/mcp/acme/my-tools \
-H "Authorization: Bearer almyty_at_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}'Refreshing Tokens
curl -X POST https://api.almyty.com/mcp/acme/my-tools/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token\
&refresh_token=almyty_rt_...\
&client_id=mcp_client_abc123..."Refresh tokens are rotated — the old refresh token is revoked when a new one is issued.
Revoking Tokens
curl -X POST https://api.almyty.com/mcp/acme/my-tools/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=almyty_at_...&client_id=mcp_client_abc123..."Revoking a refresh token also revokes all associated access tokens. Per RFC 7009, this endpoint always returns 200.
Security Properties
- PKCE mandatory — Only S256, no
plain - One-time codes — Authorization codes are marked used after exchange
- Replay detection — Reused codes trigger a warning
- Token rotation — Old refresh tokens are revoked on refresh
- Hash-only storage — Tokens stored as SHA-256 hashes, never plaintext
IP Restrictions
Any auth method can include IP restrictions:
{
"validationRules": {
"allowedIpRanges": ["10.0.0.0/8", "192.168.1.100"]
}
}Supports CIDR notation and individual IPs. Requests from non-whitelisted IPs are rejected.
Required Headers
Force clients to include specific headers:
{
"validationRules": {
"requiredHeaders": ["X-Request-Id", "X-Client-Version"]
}
}Multiple Auth Methods
A gateway can have multiple auth methods. Each method is either required or optional:
- Required methods are tried in order — the first one that matches wins
- Optional methods provide fallback — if all are optional and none match, the request is rejected
- You can combine methods for gradual migration (e.g., add OAuth while keeping API keys)
Key Scopes
| Scope | Description |
|---|---|
tools:read | List and view tool definitions |
tools:execute | Execute/invoke tools |
gateway:read | View gateway configuration |
Error Responses
On authentication failure, gateways return:
{
"error": "Unauthorized",
"message": "Invalid or expired API key",
"statusCode": 401
}MCP endpoints include a WWW-Authenticate header pointing to the
OAuth metadata URL, per the MCP spec:
WWW-Authenticate: Bearer resource_metadata="https://api.almyty.com/mcp/acme/my-tools/.well-known/oauth-protected-resource"