OAuth 2.0 / OpenID Connect Authentication
LavinMQ supports OAuth 2.0 and OpenID Connect authentication using JWT tokens. Clients connect by providing a JWT token as the password, and LavinMQ validates the token and extracts permissions from it.
Quick Start
Minimal configuration:
[main]
auth_backends = oauth
[oauth]
issuer = https://your-oauth-provider.example.com
This enables OAuth authentication and tells LavinMQ where to fetch public keys. By default, LavinMQ uses the sub and client_id claims for the username. To customize which claims are used, set preferred_username_claims.
Configuration
All OAuth settings go in the [oauth] section of /etc/lavinmq/lavinmq.ini.
Configuration Variables
| Variable | Description |
|---|---|
| issuer |
Required. Your OAuth provider's issuer URL. LavinMQ uses this to discover the JWKS endpoint and validate token issuers. The .well-known/openid-configuration endpoint must be accessible.
Example: issuer = https://oauth-provider.example.com
|
| preferred_username_claims |
Comma-separated list of JWT claims to check for the username. LavinMQ tries each in order and uses the first non-empty value. The username is used for logging and display only—authorization is based on scopes.
Default: preferred_username_claims = sub,client_id
Example: preferred_username_claims = preferred_username,sub,email
|
| resource_server_id |
Identifier for extracting nested roles and as fallback for audience validation. Use this when your tokens have roles in resource_access[resource_server_id].roles, or when you want automatic scope prefix filtering using {resource_server_id}.
Example: resource_server_id = lavinmq-service
|
| additional_scopes_keys |
Custom JWT claim containing permissions. Use this if your OAuth provider stores permissions in a non-standard claim name.
Example: additional_scopes_keys = permissions
|
| scope_prefix |
Prefix to filter scopes. Only scopes starting with this prefix are used, and the prefix is removed before parsing. Useful when tokens contain permissions for multiple services. If not set, uses {resource_server_id}. as prefix if resource_server_id is configured.
Example: scope_prefix = lavinmq:
|
| verify_aud |
Enable/disable audience claim validation. Keep this true to ensure tokens are intended for LavinMQ. Set to false only if your OAuth provider doesn't include aud claims.
Default: verify_aud = true
|
| audience |
Expected audience value. Required when verify_aud is true and tokens contain aud claims. Falls back to resource_server_id if not set.
Example: audience = lavinmq-api
|
| jwks_cache_ttl |
How long (in seconds) to cache public keys. Lower values mean more frequent key fetches. Higher values reduce network traffic but delay key rotation detection. Your OAuth provider's Cache-Control header overrides this setting.
Default: jwks_cache_ttl = 3600
|
Authentication Backends
Configure multiple backends in the [main] section:
# OAuth only
auth_backends = oauth
# Try OAuth first, fallback to local
auth_backends = oauth,local
# Try local first, fallback to OAuth
auth_backends = local,oauth
Backends are tried in order until one succeeds.
Permissions
Scope Format
Permissions follow this format: {permission_type}:{vhost}/{pattern}
- permission_type:
configure,read, orwrite - vhost: URL-encoded virtual host name (
%2ffor the default/vhost,*for all) - pattern: Resource pattern (
*is replaced with.*internally)
Examples:
configure:%2f/* # Configure all resources in default vhost
read:%2f/* # Read from default vhost
write:%2f/* # Write to default vhost
configure:production/* # Configure in production vhost
read:*/* # Read from all vhosts
Note: For MQTT connections, permission checks are not enforced by default. You must enable them explicitly:
[mqtt]
permission_check_enabled = true
Administrative Tags
Use tag:{tag_name} for administrative permissions:
tag:management
How Scopes Are Extracted
LavinMQ looks for permissions in these token claims (in order):
1. resource_access[resource_server_id].roles (if resource_server_id is configured)
{
"resource_access": {
"lavinmq-service": {
"roles": ["configure:%2f/*", "read:%2f/*", "write:%2f/*"]
}
}
}
2. scope (standard OAuth claim, space-separated)
{
"scope": "configure:%2f/* read:%2f/* write:%2f/*"
}
3. Custom claim (if additional_scopes_keys is configured)
{
"permissions": ["configure:%2f/*", "read:%2f/*", "write:%2f/*"]
}
All sources are combined—users get the union of permissions from all claims.
Connecting Clients
Clients connect by providing the JWT token as the password:
- Username: Any value (informational only)
- Password: JWT token
The token must be valid, unexpired, and contain the required claims and scopes.
Token Lifecycle
Expiration
LavinMQ monitors token expiration. When a token expires, the connection is automatically closed and the client must reconnect with a new token.
Token Refresh
Clients can update their token without reconnecting using the AMQP update-secret frame.
How to refresh:
- Before the current token expires, get a new token from your OAuth provider
- Send
update-secretframe with the new token - LavinMQ validates the new token and updates permissions
- Connection continues with new token
Note: Token refresh is only supported for AMQP connections. MQTT clients must reconnect with a new token.
Public Keys
LavinMQ automatically fetches and caches public keys from your OAuth provider’s JWKS endpoint. Keys are refreshed in the background before they expire.
Requirements:
- LavinMQ must be able to reach
{issuer}/.well-known/openid-configuration - LavinMQ must be able to reach the JWKS endpoint (discovered from above)
- OAuth provider must use HTTPS with valid certificates
Key rotation: When your OAuth provider rotates keys, LavinMQ automatically fetches the new keys on the next refresh cycle. Ensure your jwks_cache_ttl is shorter than your provider’s key rotation grace period.
Limitations
Token Support
- RS256 only: Other algorithms are not supported
- JWT format only: Opaque tokens and other formats are not supported
- OIDC discovery required: OAuth provider must support
.well-known/openid-configuration
Permissions
- Permissions are extracted at authentication time
- To update permissions, use token refresh or reconnect
- All permissions must be in the JWT token
Token Refresh
- AMQP only (MQTT clients must reconnect)
- Username cannot change between tokens
- Not all client libraries support
update-secret
Ready to take the next steps?
Managed LavinMQ instance via CloudAMQP
LavinMQ has been built with performance and ease of use in mind - we've benchmarked a throughput of about 1,000,000 messages/sec . You can try LavinMQ without any installation hassle by creating a free instance on CloudAMQP. Signing up is a breeze.
Get started with CloudAMQP ->Help and feedback
We welcome your feedback and are eager to address any questions you may have about this piece or using LavinMQ. Join our Slack channel to connect with us directly. You can also find LavinMQ on GitHub.