OAuth 2.0 Authentication Backend
Overview
This RabbitMQ authentication/authorisation backend plugin lets applications (clients) and users authenticate and authorize using JWT-encoded OAuth 2.0 access tokens.
There's also a companion troubleshooting guide for OAuth 2-specific problems.
This guide covers
How it works
The OAuth 2 plugin must be activated (or pre-configured) before it can be used, like all other plugins:
rabbitmq-plugins enable rabbitmq_auth_backend_oauth2
Then it must be specified as one of the authN and/or authZ backends. The following example enables OAuth 2.0 authentication and authorization backends:
# note that the module name begins with a "rabbit_", not "rabbitmq_", like in the name
# of the plugin
auth_backends.1 = rabbit_auth_backend_oauth2
Next, let's take a look at the workflows the OAuth 2 plugin supports.
Prerequisites
To use the OAuth 2 plugin, all RabbitMQ nodes must be
- Configured to use the rabbit_auth_backend_oauth2 backend.
- Configured with the resource service ID (
resource_server_id
). The RabbitMQ cluster becomes an OAuth 2.0 resource and this is its identifier. - Configured with issuer URL of the OAuth 2.0 provider, or the JWKS URL, or directly with the signing keys that the OAuth 2.0 provider uses to sign tokens
Here is the minimal configuration to support OAuth 2.0 authentication :
To activate it in the Management plugin you need additional configuration.
auth_oauth2.resource_server_id = new_resource_server_id
auth_oauth2.issuer = https://my-oauth2-provider.com/realm/rabbitmq
Based on the previous configuration, JWT Tokens presented to RabbitMQ for authentication must:
- be digitally signed
- have a value in the
aud
field that matchesresource_server_id
value - have scopes that must match the
resource_server_id
value, for examplenew_resource_server_id.read:*/*
Also, the https://my-oauth2-provider.com/realm/rabbitmq/.well-known/openid-configuration
endpoint must return the OpenID Provider Configuration which includes the JKWS url to download the signing keys.
.well-known/openid-configuration is the OpenID standard path for the OpenID Provider Configuration endpoint
More detail is included in the next section about what happens during the authentication and how to configure OAuth 2.0 beyond the basic configuration shown previously.
Authorization Flow
This plugin does not communicate with any OAuth 2.0 provider in order to authenticate user and grants access. Instead, it decodes an access token provided by the client and authorises a user based on the scopes found in the token.
Tokens must be digitally signed otherwise they are not accepted. RabbitMQ must have the signing key to validate the signature. You can either configure the signing keys the OAuth 2.0 provider will use, or configure RabbitMQ with one of the following two endpoints:
- JWKS endpoint - this is the HTTP endpoint that returns the signing keys used to digitally sign the tokens.
- OpenID Provider Configuration endpoint - this is the endpoint that returns the provider's configuration including all its endpoints, such as the JWKS endpoint.
When you configure RabbitMQ with one of two previous endpoints, RabbitMQ must make a HTTP request (or two, if we specify the latter endpoint) to download the signing keys. This is an operation that occurs once for any signing key not downloaded yet. When the OAuth 2.0 provider rotates the signing keys, newer tokens refer to a new signing key which RabbitMQ does not have yet which triggers another download of the newer signing keys.
The token can be any JWT token which contains the scope
and aud
fields.
In chronological order, here is the sequence of events that occur when a client application wants to connect to one of the RabbitMQ's messaging protocols, such as AMQP:
- The Client application requests an access_token from the OAuth 2.0 provider.
- The access token must include scopes supported by RabbitMQ in the
scope
field (it is possible to use a different field for the scopes by setting the name of the new field inauth_oauth2.additional_scopes_key
). - The Client application passes the token as password when connecting to RabbitMQ's messaging protocol. The username field is ignored.
- RabbitMQ validates the token's signature. To validate it, RabbitMQ must have the signing keys or download them from the JWKS endpoint as explained in earlier sections.
- RabbitMQ validates that the token has the audience claim and whose value matches the
resource_server_id
(this operation can be deactivated by settingauth_oauth2.verify_aud
tofalse
). - RabbitMQ translates the scopes found in the token into RabbitMQ permissions (the same permissions used in the RabbitMQ's internal database).
Variables configurable in rabbitmq.conf
Key | Documentation |
---|---|
auth_oauth2.resource_server_id | The Resource Server ID |
auth_oauth2.resource_server_type | The Resource Server Type required when using Rich Authorization Request token format |
auth_oauth2.additional_scopes_key | Configure the plugin to look for scopes in other fields (maps to additional_rabbitmq_scopes in the old format). |
auth_oauth2.scope_prefix | Configure the prefix for all scopes. The default value is auth_oauth2.resource_server_id followed by the dot . character. |
auth_oauth2.preferred_username_claims | List of the JWT claims to look for the username associated with the token separated by commas. |
auth_oauth2.default_key | ID of the default signing key. |
auth_oauth2.signing_keys | Paths to the signing key files. |
auth_oauth2.issuer | The issuer URL of the authorization server that is used to discover endpoints such as jwk_uri and others (https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). |
auth_oauth2.jwks_url | The URL of the JWKS endpoint. According to the JWT Specification, the endpoint URL must be https. |
auth_oauth2.token_endpoint | The URL of the OAuth 2.0 token endpoint. |
auth_oauth2.https.cacertfile | Path to a file containing PEM-encoded CA certificates. The CA certificates are used to connect to any of these endpoints: jwks_url , token_endpoint , or the issuer . |
auth_oauth2.https.depth | The maximum number of non-self-issued intermediate certificates that may follow the peer certificate in a valid certification path. The default value is 10. |
auth_oauth2.https.peer_verification | Configures peer verification. Available values: verify_none , verify_peer . The default value is verify_peer if there are trusted CA installed in the OS or auth_oauth2.https.cacertfile is set. Deprecated: This variable will be soon replaced by auth_oauth2.https.verify . Users should stop using this variable. |
auth_oauth2.https.fail_if_no_peer_cert | Used together with auth_oauth2.https.peer_verification = verify_peer . When set to true , TLS connection will be rejected if the client fails to provide a certificate. The default value is false . |
auth_oauth2.https.hostname_verification | Enable wildcard-aware hostname verification for key server. Available values: wildcard , none . The default value is none . |
auth_oauth2.https.crl_check | Perform CRL verification (Certificate Revocation List) verification. Default value is false. |
auth_oauth2.algorithms | Restrict the usable algorithms. |
auth_oauth2.verify_aud | Whether to verify the token's aud field or not. The default value is true . |
auth_oauth2.resource_servers | Multiple OAuth 2.0 resources configuration. |
auth_oauth2.oauth_providers | Multiple OAuth 2.0 providers configuration. |
auth_oauth2.default_oauth_provider | ID of the OAuth 2.0 provider used for the auth_oauth2.resource_servers , that did not specify any (via the variable oauth_provider_id ) or when auth_oauth2.jwks_url and auth_oauth2.issuer are both missing. |
Resource Server ID
A RabbitMQ cluster must have at least one resource server identifier configured. If it has just one resource, this is configured in the auth_oauth2.resource_server_id
variable and it is mandatory.
If the RabbitMQ cluster has more than one OAuth resource then they are configured under auth_oauth2.resource_servers.<index>
and in this case auth_oauth2.resource_server_id
variable is not mandatory.
RabbitMQ uess the resource server identity for these two purposes:
- To validate the token's audience (
aud
) whose value must contain the resource server identifier. This validation can be disabled though. - To initiate the OAuth 2.0 Authorization Code flow in the Management UI. This is the flow used to authenticate a user and to get its access token. RabbitMQ must include the resource server identifier in the request's attribute called
resource
.
Scope prefix
OAuth 2.0 tokens use scopes to communicate what set of permissions particular client are granted. The scopes are free form strings.
By default, resource_server_id
followed by the dot (.
) character is the prefix used for scopes to avoid scope collisions (or unintended overlap).
However, in some environments, it is not possible to use resource_server_id
as the prefix for all scopes. For these environments, there is a new variable called scope_prefix
which overrides the default scope prefix. Empty strings are allowed.
Given the below configuration, the scope associated with the permission read:*/*
is api://read:*/*
.
...
auth_oauth2.scope_prefix = api://
...
Signing keys files
The following configuration declares two signing keys and configures the kid of the default signing key. For more information check the section Configure Signing keys.
auth_oauth2.resource_server_id = new_resource_server_id
auth_oauth2.additional_scopes_key = my_custom_scope_key
auth_oauth2.preferred_username_claims.1 = username
auth_oauth2.preferred_username_claims.2 = user_name
auth_oauth2.default_key = id1
auth_oauth2.signing_keys.id1 = test/config_schema_SUITE_data/certs/key.pem
auth_oauth2.signing_keys.id2 = test/config_schema_SUITE_data/certs/cert.pem
auth_oauth2.algorithms.1 = HS256
auth_oauth2.algorithms.2 = RS256
JWKS endpoint
The followibng configuration sets the JWKS endpoint from which RabbitMQ downloads the signing keys using the configured CA certificate and TLS variables.
auth_oauth2.resource_server_id = new_resource_server_id
auth_oauth2.jwks_url = https://my-jwt-issuer/jwks.json
auth_oauth2.https.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem
auth_oauth2.https.peer_verification = verify_peer
auth_oauth2.https.depth = 5
auth_oauth2.https.fail_if_no_peer_cert = true
auth_oauth2.https.hostname_verification = wildcard
auth_oauth2.algorithms.1 = HS256
auth_oauth2.algorithms.2 = RS256
Multiple Resource Servers configuration
Each auth_oauth2.resource_servers.<id/index>.
entry has the following variables. Except for the variables id
and oauth_provider_id
, if a resource does not configure a variable, RabbitMQ uses the variable configured at the root level. For instance, if the resource auth_oauth2.resource_servers.prod
does not configure preferred_username_claims
variable, RabbitMQ uses the value configured in auth_oauth2.preferred_username_claims
for the resource prod
.
Key | Documentation |
---|---|
id | The Resource Server ID |
resource_server_type | The Resource Server Type required when using Rich Authorization Request token format. |
additional_scopes_key | Configure the plugin to look for scopes in other fields (maps to additional_rabbitmq_scopes in the old format). |
scope_prefix | Configure the prefix for all scopes. The default value is auth_oauth2.resource_server_id followed by the dot . character. |
preferred_username_claims | List of the JWT claims to look for the username associated with the token separated by commas. |
oauth_provider_id | The OAuth Provider Identifier associated to this resource. RabbitMQ uses the signing keys issued by this OAuth Provider to validate tokens which whose audience matches this resource's id. |
Usually, a numeric value is used as index
, for example auth_oauth2.resource_servers.1.id = rabbit_prod
. However, it can be any string, for example auth_oauth2.resource_servers.rabbit_prod.jwks_url = http://some_url
. By default, the index
is the resource server's id. However, you can override it via the id
attribute like in auth_oauth2.resource_servers.1.id = rabbit_prod
.
Here is an example which configures two resources (prod
and dev
) which are used by the users and clients managed by
the same identity provider whose issuer url is https://my-idp.com/
:
auth_oauth2.issuer = https://my-idp.com/
auth_oauth2.resource_servers.1.id = prod
auth_oauth2.resource_servers.2.id = dev
See the advanced usage section called Multiple Resource Servers for more information on how to configure them.
Multiple OAuth Providers configuration
Each auth_oauth2.oauth_providers.<id/index>.
entry has the following variables.
Key | Documentation |
---|---|
issuer | URL of OAuth Provider. RabbitMQ uses this URL to build the OpenId Connect Discovery endpoint by appending the path .well-known/openid-configuration to this URL |
token_endpoint | The URL of the OAuth 2.0 token endpoint. |
jwks_uri | The URL of the JWKS endpoint. According to the JWT Specification, the endpoint URL must be https. Warning: RabbitMQ uses for each OAuth Provider the variable name jwks_uri used by the OpenId Connect Discovery Specification rather than jwks_url . |
https.cacertfile | Path to a file containing PEM-encoded CA certificates used to connect issuer and/or jwks_uri URLs |
https.depth | The maximum number of non-self-issued intermediate certificates that may follow the peer certificate in a valid certification path. The default value is 10. |
https.verify | Configures peer verification. Available values: verify_none , verify_peer . The default value is verify_peer if there are trusted CA installed in the OS or auth_oauth2.https.cacertfile is set. |
https.fail_if_no_peer_cert | Used together with auth_oauth2.https.peer_verification = verify_peer . When set to true , TLS connection will be rejected if the client fails to provide a certificate. The default value is false . |
https.hostname_verification | Enable wildcard-aware hostname verification for key server. Available values: wildcard , none . The default value is none . |
https.crl_check | Perform CRL verification (Certificate Revocation List) verification. Default value is false. |
Here is an example which configures two resources (prod
and dev
) where each resource is managed by two distinct identity providers:
auth_oauth2.scope_prefix = rabbitmq.
auth_oauth2.resource_servers.1.id = prod
auth_oauth2.resource_servers.1.oauth_provider_id = idp_prod
auth_oauth2.resource_servers.2.id = dev
auth_oauth2.resource_servers.2.oauth_provider_id = idp_dev
auth_oauth2.oauth_providers.idp_prod.issuer = https://idp_prod.com
auth_oauth2.oauth_providers.idp_dev.issuer = https://idp_dev.com
See the advanced usage section called Multiple OAuth Providers for more information on how to configure them.
Token validation
When RabbitMQ receives a JWT token, it validates it before accepting it.
Must be digitally signed
The token must carry a digital signature and optionally a kid
header attribute which identifies the key RabbitMQ should
use to validate the signature.
Must not be expired
RabbitMQ uses this field exp
(exp) to validate the token if present.
It contains the expiration time after which the JWT MUST NOT be accepted for processing.
Audience must have/match the resource_server_id
The aud
(Audience) identifies the recipients and/or resource_server of the JWT. By default, RabbitMQ uses this field to validate the token although you can deactivate it by the verify_aud
variable set to false
. When it set to true
, this attribute must either match the resource_server_id
variable or in case of a list, it must contain the resource_server_id
.
Token expiration and refresh
On an existing connection, the token can be refreshed by the update-secret AMQP 0.9.1 method. Please check your client whether it supports this method (for example documentation for the Java client). Otherwise the client has to disconnect and reconnect to use a new token.
If the latest token expires on an existing connection, after a limited time the broker will refuse all operations (but it won't disconnect).
Scope-to-Permission translation
Scopes are translated into permission grants to RabbitMQ resources for the provided token.
The current scope format is <permission>:<vhost_pattern>/<name_pattern>[/<routing_key_pattern>]
where
<permission>
is an access permission (configure
,read
, orwrite
)<vhost_pattern>
is a wildcard pattern for vhosts token has access to.<name_pattern>
is a wildcard pattern for resource name<routing_key_pattern>
is a wildcard pattern for routing key in topic authorization
Wildcard patterns are strings with optional wildcard symbols *
that match
any sequence of characters.
Wildcard patterns match as following:
*
matches any stringfoo*
matches any string starting with afoo
*foo
matches any string ending with afoo
foo*bar
matches any string starting with afoo
and ending with abar
There can be multiple wildcards in a pattern:
start*middle*end
*before*after*
To use special characters like *
, %
, or /
in a wildcard pattern,
the pattern must be URL-encoded.
These are the usually permissions examples:
read:*/*
(read:*/*/*
) - read permissions to any resource on any vhostwrite:*/*
(write:*/*/*
) - write permissions to any resource on any vhostread:vhost1/*
(read:vhost1/*/*
) - read permissions to any resource on thevhost1
vhostread:vhost1/some*
- read permissions to all the resources, starting withsome
on thevhost1
vhostwrite:vhost1/some*/routing*
- topic write permissions to publish to an exchange starting withsome
with a routing key starting withrouting
read:*/*/*
andwrite:*/*/*
- queue binding permissions required to bind a queue on a topic exchange with any routing key
See the wildcard matching test suite and scopes test suite for more examples.
Scopes, by default, are prefixed with resource_server_id
followed by the dot (.
) character if scope_prefix
is not configured. For example, if resource_server_id
is "my_rabbit", a scope to enable read from any vhost will
be my_rabbit.read:*/*
.
If scope_prefix
is configured then scopes are prefixed as follows: <scope_prefix><permission>
. For example,
if scope_prefix
is api://
and the permission is read:*/*
the scope would be api://read:*/*
Topic Exchange scopes
The previous section explained, in detail, how permissions are mapped to scopes. This section explains more specifically what scopes you need in order to operate on Topic Exchanges.
To bind and/or unbind a queue to/from a Topic Exchange, you need to have the following scopes:
- write permission on the queue and routing key ->
rabbitmq.write:<vhost>/<queue>/<routingkey>
for example
rabbitmq.write:*/*/*
- read permission on the exchange and routing key ->
rabbitmq.write:<vhost>/<exchange>/<routingkey>
for example
rabbitmq.read:*/*/*
To publish to a Topic Exchange, you need to have the following scope:
- write permission on the exchange and routing key ->
rabbitmq.write:<vhost>/<exchange>/<routingkey>
for example
rabbitmq.write:*/*/*
OAuth 2.0 authorisation backend supports variable expansion when checking permission on topics.
It supports JWT claims whose value is a plain string, plus the vhost
variable.
For example, a user connected with the token below to the vhost prod
should have
a write permission on all exchanges starting with x-prod-
, and any routing key starting with u-bob-
:
{
"sub" : "bob",
"scope" : [ "rabbitmq.write:*/q-{vhost}-*/u-{sub}-*" ]
}
Scope and tags
Users in RabbitMQ can have tags associated with them. Tags are used to control access to the management plugin.
In the OAuth context, tags can be added as part of the scope, using a format like <resource_server_id>.tag:<tag>
. For example, if the resource_server_id
is "my_rabbit", a scope to grant access to the management plugin with
the monitoring
tag will be my_rabbit.tag:monitoring
.