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.
Table of Contents
How it works
- Prerequisites
- Authorization Flow
- Variables configurable in rabbitmq.conf
- Token validation
- Token expiration and refresh
- Scope-to-Permission translation
- Topic Exchange scopes
- Scope and tags
Basic usage
- Configure OAuth 2.0 provider's issuer
- Configure signing keys
- Use a different token field for the scope
- Preferred username claims
- Discovery Endpoint params
- Rich Authorization Request
Advanced usage
- Use default OAuth 2.0 provider
- Configure OAuth 2.0 provider's end_session_endpoint
- Configure multiple resource servers
- Configure multiple OAuth 2.0 providers
Examples for Specific Identity Providers
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
If your provider exposes a different path and/or requires some extra http parameters for the OpenId discovery endpoint, you can configure them as follows:
auth_oauth2.discovery_endpoint_path = my/custom/path
auth_oauth2.discovery_endpoint_params.appid = some-app-id
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 endpoint returns the provider's configuration including all of its endpoints, most importantly the JWKS endpoint
When you configure RabbitMQ with one of two previous endpoints, RabbitMQ must make a HTTP request (or two, if you 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. |
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 either discover endpoints such as jwks_uri and/or where to redirect RabbitMQ management users to login and get a token. |
auth_oauth2.jwks_uri | The URL of the JWKS endpoint. According to the JWT Specification, the endpoint URL must be https. Optional if you set auth_oauth2.issuer . If this URL is set, it overrides the jwks_uri discovered via the discovery endpoint. |
auth_oauth2.jwks_url | This variable is deprecated and you should use instead auth_oauth2.jwks_uri . In RabbitMQ 4.2.0, this variable will be removed. In the meantime, RabbitMQ supports it until you change your configuration. |
auth_oauth2.token_endpoint | The URL of the OAuth 2.0 token endpoint. Optional if you set auth_oauth2.issuer . If this URL is set, it overrides the token_endpoint discovered via the discovery 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_uri , token_endpoint , or the discovery endpoint. |
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_uri 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.
Given the below configuration, the scope associated with the permission read:*/*
is api://read:*/*
.
...
auth_oauth2.scope_prefix = api://
...
To use an empty string as prefix, use this configuration:
...
auth_oauth2.scope_prefix = ''
...
Scope Aliases
Scope aliases are necessary when scopes in the RabbitMQ format cannot be configured on the identity provider (IDP) side
A scope alias is a mapping between a custom JWT token scope and a set of RabbitMQ-specific scopes. A custom scope can also be defined as any scope which is not recogonized by RabbitMQ's OAuth 2 subsystem.
Scope aliases are necessary when scopes in the RabbitMQ format cannot be configured on the identity provider (IDP) side. Instead, a set of names is configured on the IDP side, and mapped to a set of scoped that RabbitMQ can parse and use.
For instance, let's consider an identity provider with the following two roles:
admin
developer
These roles should be mapped to the following RabbitMQ scopes:
admin
torabbitmq.tag:administrator rabbitmq.read:*/
developer
torabbitmq.tag:management rabbitmq.read:*/* rabbitmq.write:*/* rabbitmq.configure:*/*
The following rabbitmq.conf
example performs the aforementioned mapping using scope aliases. The mapping can be one-to-one or one-to-many:
# ...
# the "admin" role above
auth_oauth2.scope_aliases.admin = rabbitmq.tag:administrator rabbitmq.read:*/
# the "developer" role above
auth_oauth2.scope_aliases.developer = rabbitmq.tag:management rabbitmq.read:*/* rabbitmq.write:*/* rabbitmq.configure:*/*
# ...
Sometimes an alias may have to use special characters and symbols including the separator character, .
.
In those cases, configure the scope aliases as follows:
# ...
auth_oauth2.scope_aliases.1.alias = api://admin
auth_oauth2.scope_aliases.1.scope = rabbitmq.tag:administrator rabbitmq.read:*/
auth_oauth2.scope_aliases.2.alias = api://developer.All
auth_oauth2.scope_aliases.2.scope = rabbitmq.tag:management rabbitmq.read:*/* rabbitmq.write:*/* rabbitmq.configure:*/*
# ...
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 following 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_uri = 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 Сonfiguration
Each auth_oauth2.resource_servers.<id/index>.
entry has the following variables shown in the table below. 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. |
scope_aliases | Configure scope aliases |
preferred_username_claims | List of the JWT claims to look for the username associated with the token separated by commas. |
oauth_provider_id | The identifier of the OAuth Provider associated to this resource. RabbitMQ uses the signing keys issued by this OAuth Provider to validate tokens whose audience matches this resource's id. |
All available configurable parameters for each OAuth 2 provider is documented in a separate section.
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_uri = http://some_url
. By default, the index
is the resource server's id. However, you can override it via the id
variable 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 Сonfiguration
Each auth_oauth2.oauth_providers.{id/index}
entry has the following sub-keys.
Key | Documentation |
---|---|
issuer | URL of OAuth Provider. It is used to build the discovery endpoint URL and/or to redirect RabbitMQ Management users to login and get a token. |
discovery_endpoint_path | The path used for the OpenId discovery endpoint. Default value is .well-known/openid-configuration |
discovery_endpoint_params | List of HTTP query parameters sent to the OpenId discovery endpoint. |
token_endpoint | The URL of the OAuth 2.0 token endpoint. Optional if you configured issuer . |
jwks_uri | The URL of the JWKS endpoint. According to the JWT Specification, the endpoint URL must be https. This variable is optional if you set issuer . |
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. |
signing_keys | Local filesystem paths to the signing key files. The files must exist and be readable. |
default_key | ID of the default signing key. |
algorithms | Used to restrict the list of enabled algorithms. |
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. This validation can be disabled by setting the auth_oauth2.verify_aud
setting set to false
.
When verification is enabled, this aud
field must either match the resource_server_id
value or, in case of a list, it must contain the resource_server_id
value.
Token expiration and refresh
In RabbitMQ, token expiration and refresh processes vary between AMQP 1.0 and AMQP 0.9.1 protocols.
For AMQP 1.0, if the latest token on an existing connection expires, RabbitMQ disconnects the
client. To prevent disconnection, the client can proactively refresh the token before it expires.
The client can set a new token by sending an
HTTP-over-AMQP 1.0
request. This request uses a PUT
operation on the /auth/tokens
endpoint, with the token included
in the body as a
binary
AMQP value.
RabbitMQ’s AMQP 1.0 clients support token refresh in
Java,
.NET, and
Erlang.
For AMQP 0.9.1, when a token expires on an existing connection, the broker refuses further
operations after a limited time, but does not disconnect the client. To refresh the token, the
client can use the AMQP 0.9.1
update-secret
protocol method if supported by the
client. For an example, see the
Java client documentation. If the
client does not support update-secret
, it must disconnect and reconnect with a new token.
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:*/x-{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
.
Basic usage
Configure OAuth 2.0 provider's issuer
Before RabbitMQ 3.13, users had to either configure the JWKS endpoint (that is auth_oauth2.jwks_uri
variable) or statically configure the signing keys. Now, users only need to configure the OpenID Provider's issuer URL and from this URL RabbitMQ downloads the OpenID Provider configuration which includes the JWKS endpoint in addition to other endpoints which will be useful in other contexts.
Usually, this issuer URL is the same URL configured in the management plugin (management.oauth_provider_url
). From now on, you only need to configure a single URL, specified by the auth_oauth2.issuer
variable. Except in edge cases where the issuer URL does not host the login page. In those cases, the user configures the login page in the management.oauth_provider_url
variable.
Sample configuration using issuer:
auth_oauth2.resource_server_id = my_rabbit_server
auth_oauth2.issuer = https://my-idp-provider/somerealm
Sample configuration which configures the jwks_uri
rather than the issuer:
auth_oauth2.resource_server_id = my_rabbit_server
auth_oauth2.jwks_uri = "https://my-jwt-issuer/jwks.json
If you have both endpoints configured, RabbitMQ uses jwks_uri
because it does not need to discover it via the issuer
url.
Note about TLS variables for the jwks_uri
or the issuer
url: TLS variable such as the cacertfile
are configured as follows regardless which url you are using:
...
auth_oauth2.https.cacertfile = /opts/certs/cacert.pem
...
VERY IMPORTANT: Since RabbitMQ 3.13, if auth_oauth2.https.peer_verification
variable is not set, RabbitMQ sets it to verify_peer
as long as there are trusted certificates installed in the OS or the user configured auth_oauth2.https.cacertfile
.
Configure signing keys
Currently, it is very rare you configure RabbitMQ with signing keys, when RabbitMQ can automatically download them as explained in the previous section. However, RabbitMQ supports those edge cases where you need to statically configure the signing keys, or when you need to support symmetric signing keys as opposed to the most widely used asymmetric keys.
The following example uses Cloud Foundry UAA as the OAuth 2.0 provider.
To get the signing key from the OAuth 2.0 provider UAA, use the
token_key endpoint
or uaac (the uaac signing key
command).
The following fields are required: kty
, value
, alg
, and kid
.
Assuming UAA reports the following signing key information:
uaac signing key
kty: RSA
e: AQAB
use: sig
kid: a-key-ID
alg: RS256
value: -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq
6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK
IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2
B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF
2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG
QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7
VwIDAQAB
-----END PUBLIC KEY-----
n: ANnT_r0Z_io_kv6BnePZKuvgijQHbggta2i30x-wd6o5mWJuOcg5fl5oCvQjZh15IaPar5oXZLHcw1bHXg5YSiHXCFmnYag83bZ9YY_9tolMK4R9G3eO-YZSnLImfqMv7HYBoAM75pk0JnTKhF6ldgfavShQZqOAIYf-vneMDNax_ZMZdEbzACi3vnWqCByI6JPIQju
HCkEBMPxKwXuEhdnK98EMAnxdalbuHgFTVX8X8v7hLxt0O8dNOT903CvkHGICcWr95YnLUouXcli4BkAL5JJ1oraUSvClS8qRI-Vino-ghfJ6t9LrZ9eRUINCZB6Ks8Igqqnnp_BiD7XiO1c
it translates into the following configuration (in the advanced RabbitMQ config format):
auth_oauth2.resource_server_id = my_rabbit_server
auth_oauth2.signing_keys.a-key-ID = /path-to-signing-key-pem-file
If a symmetric key is used, the configuration looks like this:
[
{rabbitmq_auth_backend_oauth2, [
{resource_server_id, <<"my_rabbit_server">>},
{key_config, [
{signing_keys, #{
<<"a-key-ID">> => {map, #{<<"kty">> => <<"MAC">>,
<<"alg">> => <<"HS256">>,
<<"value">> => <<"my_signing_key">>}}
}}
]}
]},
].
Use a different token field for the scope
By default the plugin looks for the scope
key in the token, you can configure the plugin to also look in other fields using the extra_scopes_source
variable. Values format accepted are scope as string or list
auth_oauth2.resource_server_id = my_rabbit_server
auth_oauth2.additional_scopes_key = my_custom_scope_key
Token sample:
{
"exp": 1618592626,
"iat": 1618578226,
"aud" : ["my_id"],
...
"scope_as_string": "my_id.configure:*/* my_id.read:*/* my_id.write:*/*",
"scope_as_list": ["my_id.configure:*/*", "my_id.read:*/*", "my_id.write:*/*"],
...
}
Preferred username claims
The username associated with the token must be available to RabbitMQ so that this username is displayed in the RabbitMQ Management UI.
By default, RabbitMQ searches for the sub
claim first, and if it is not found, RabbitMQ uses the client_id
.
Most authorization servers return the user's GUID in the sub
claim instead of the user's username or email address, anything the user can relate to. When the sub
claim does not carry a user-friendly username, you can configure one or several claims to extract the username from the token.
Example rabbitmq.conf
configuration:
# ...
auth_oauth2.resource_server_id = rabbitmq
auth_oauth2.preferred_username_claims.1 = user_name
auth_oauth2.preferred_username_claims.2 = email
# ...
In the example configuration, RabbitMQ searches for the user_name
claim first and if it is not found, RabbitMQ searches for the email
. If these are not found, RabbitMQ uses its default lookup mechanism which first looks for sub
and then client_id
.
Discovery endpoint parameters
Some OAuth 2.0 providers requires certain query parameters in the OpenId Discovery endpoint. For instance, Microsoft Entra ID requires a query parameter called appid
when the application uses custom signing keys. The discovery endpoint returns an OpenId configuration tailored for the application that matches the appid
.
For instance, the jkws_uri
endpoint returned in the OpenId configuration has already the query parameters sent to the discovery endpoint.
Here is an example of setting these parameters including the rest of the variables used to build the OpenId discovery endpoint URL.
# ...
auth_oauth2.issuer = https://myissuer.com/v2
auth_oauth2.discovery_endpoint_path = .well-known/authorization-server
auth_oauth2.discovery_endpoint_params.param1 = value1
auth_oauth2.discovery_endpoint_params.param2 = value2
# ...
This is the URL built to access the OpenId Discovery endpoint:
https://myissuer.com/v2/.well-known/authorization-server?param1=value1¶m2=value2
Rich Authorization Request
The Rich Authorization Request extension provides a way for OAuth clients to request fine-grained permissions during an authorization request. It moves away from the concept of scopes that are text labels and instead defines a more sophisticated permission model.
RabbitMQ supports JWT tokens compliant with the extension. Below is a sample example section of JWT token:
{
"authorization_details": [
{
"type" : "rabbitmq",
"locations": ["cluster:finance/vhost:production-*"],
"actions": [ "read", "write", "configure" ]
},
{
"type" : "rabbitmq",
"locations": ["cluster:finance", "cluster:inventory" ],
"actions": ["administrator" ]
}
]
}
The token above contains two permissions under the attribute authorization_details
.
Both permissions are meant for RabbitMQ servers with resource_server_type
set to rabbitmq
.
This field identifies RabbitMQ-specific permissions.
The first permission grants read
, write
and configure
permissions to any
queue and/or exchange on any virtual host whose name matches the pattern production-*
,
and that reside in clusters whose resource_server_id
contains the string finance
.
The cluster
attribute's value is also a regular expression. To match exactly the
string finance
, use ^finance$
.
The second permission grants the administrator
user tag in two clusters, finance
and inventory
. Other supported user tags as management
, policymaker
and monitoring
.
Type field
In order for a RabbitMQ node to accept a permission, its value must match that
node's resource_server_type
variable's value. A JWT token may have permissions
for multiple resource types.
Locations field
The locations
field can be either a string containing a single location or a Json array containing
zero or many locations.
A location consists of a list of key-value pairs separated by forward slash /
character. Here is the format:
cluster:<resource_server_id_pattern>[/vhost:<vhost_pattern>][/queue:<queue_name_pattern>|/exchange:<exchange_name_pattern>][/routing-key:<routing_key_pattern>]
Any string separated by /
which does not conform to <key>:<value>
is ignored. For instance, if your locations start with a prefix, for example vrn/cluster:rabbitmq
, the vrn
pattern part is ignored.
The supported location's attributed are:
cluster
: This is the only mandatory attribute. It is a wildcard pattern which must match RabbitMQ'sresource_server_id
otherwise the location is ignored.vhost
: This is the virtual host you are granting access to. It also a wildcard pattern. If not specified,*
will be used.queue
|exchange
: queue or exchange name pattern. The location grants the permission to a set of queues (or exchanges) that match it. One location can only specify eitherqueue
orexchange
but not both. If not specified,*
will be usedrouting-key
: this is the routing key pattern the location grants the permission to. If not specified,*
will be used
For more information about wildcard patterns, check the section Scope-to-Permission Translation.
Actions field
The actions
field can be either a string containing a single action or a Json array containing zero or many actions.
The supported actions map to either RabbitMQ permissions:
configure
read
write
Or RabbitMQ user tags:
administrator
monitoring
management
policymaker
Rich-Permission to scope translation
Rich Authorization Request permissions are translated into JWT token scopes that use the aforementioned convention using the following algorithm:
For each location found in the locations
where the cluster
attribute matches the current RabbitMQ server's resource_server_id
:
-
For each location found in the
locations
field where thecluster
attribute matches the current RabbitMQ node'sresource_server_id
, the plugin extracts thevhost
,queue
orexchange
androuting_key
attributes from the location. If the location does not have any of those attributes, the default value of*
is assumed. Out of those values, the following scope suffix will be produced:scope_suffix = <vhost>/<queue>|<exchange>/<routing-key>
-
For each action found in the
actions
field:if the action is not a known user tag, the following scope is produced out of it:
scope = <resource_server_id>.<action>:<scope_suffix>
For known user tag actions, the following scope is produced:
scope = <resource_server_id>.<action>
The plugin produces permutations of all actions
by all locations
that match the node's configured resource_server_id
.
In the following RAR example
{
"authorization_details": [
{ "type" : "rabbitmq",
"locations": ["cluster:finance/vhost:primary-*"],
"actions": [ "read", "write", "configure" ]
},
{ "type" : "rabbitmq",
"locations": ["cluster:finance", "cluster:inventory" ],
"actions": ["administrator" ]
}
]
}
if RabbitMQ nodes resource_server_id
is equal to finance
, the plugin computes the following sets of scopes:
finance.read:primary-*/*/*
finance.write:primary-*/*/*
finance.configure:primary-*/*/*
finance.tag:administrator
Advanced usage
Use default OAuth 2.0 provider
As long as you have only one OAuth 2.0 provider, you can skip this advanced usage although you can use it.
Under the basic usage section, you configured the issuer
url or maybe the jwks_uri
along with the TLS variables if needed. This advanced usage configures everything relative to the OAuth provider into a dedicated configuration.
Here is an example configuration that uses issuer
to configure the identity provider's URL:
auth_oauth2.resource_server_id = rabbitmq-prod
auth_oauth2.scope_prefix = rabbitmq.
auth_oauth2.issuer = https://prodkeycloak:8080/realm/prod
auth_oauth2.https.cacertfile = /opts/certs/prodcacert.pem
The equivalent configuration where the identity provider is configured under auth_oauth2.oauth_providers
variable is:
auth_oauth2.resource_server_id = rabbitmq-prod
auth_oauth2.scope_prefix = rabbitmq.
auth_oauth2.default_oauth_provider = prodkeycloak
auth_oauth2.oauth_providers.prodkeycloak.issuer = https://prodkeycloak:8080/realm/prod
auth_oauth2.oauth_providers.prodkeycloak.https.cacertfile = /opts/certs/prodcacert.pem
This latter configuration is more relevant when users present tokens which are issued or signed by different OAuth 2.0 providers. However, one can still use it provided auth_oauth2.default_oauth_provider
is set.
Configure OAuth 2.0 provider's end_session_endpoint
This advanced setting is only required when the OpenId Connect Discovery endpoint does not return an end_session_endpoint
and you want Single Logout functionality. In other words, when the user logs out from the management UI it is also logged out from the OAuth Provider.
If the OpenId Connect Discovery endpoint response does include an end_session_endpoint
,
the management UI uses it over the configured endpoint.
Here is an example configuration that sets end_session_endpoint
:
auth_oauth2.resource_server_id = rabbitmq-prod
auth_oauth2.scope_prefix = rabbitmq.
auth_oauth2.issuer = https://prodkeycloak:8080/realm/prod
auth_oauth2.end_session_endpoint = https://prodkeycloak:8080/realm/prod/logout
The equivalent configuration where the identity provider is configured under auth_oauth2.oauth_providers
variable is:
auth_oauth2.resource_server_id = rabbitmq-prod
auth_oauth2.scope_prefix = rabbitmq.
auth_oauth2.default_oauth_provider = prodkeycloak
auth_oauth2.oauth_providers.prodkeycloak.issuer = https://prodkeycloak:8080/realm/prod
auth_oauth2.oauth_providers.prodkeycloak.end_session_endpoint = https://prodkeycloak:8080/realm/prod/logout
Configure multiple resource servers
Usually, all users that access a RabbitMQ cluster are registered within the same identity provider. Likewise, all tokens targeting the same RabbitMQ cluster also carry the same audience. In other words, all users reference a RabbitMQ cluster with the same resource name which must match the value of the auth_oauth2.resource_server_id
variable.
However, there are some use-cases where RabbitMQ is accessed by users registered in different identity providers or tokens are issued for the same RabbitMQ installation but with different Audience(s). For these use-cases, RabbitMQ OAuth 2.0 plugin and the Management plugin can be configured with multiple OAuth 2.0 resources.
The following is the OAuth 2.0 plugin configuration for two resources with the ids: rabbit_prod
and rabbit_dev
. Both resources (also known as audience) are managed by the same Identity Provider whose issuer is http//some_idp_url
.
auth_oauth2.issuer = http//some_idp_url
auth_oauth2.scope_prefix = rabbitmq.
auth_oauth2.resource_servers.1.id = rabbit_prod
auth_oauth2.resource_servers.2.id = rabbit_dev
auth_oauth2.resource_servers.2.scope_prefix = dev-rabbitmq.
All resource servers share the variables you set so far under auth_oauth2.
such as scope_prefix
. However, they can override them. Here are the list of variables a resource server can override:
id
- This is the actual resource identifier carried in theaudience
field of a token. If omitted, the value is the index, for example, givenauth_oauth2.resource_servers.prod.scope_prefix
variable, theid
would beprod
.scope_prefix
additional_scopes_key
resource_server_type
oauth_provider_id
- This is the identifier of the OAuth provider. It is configured in RabbitMQ. It provides all the variables to contact the authorization server and discover all its endpoints, such as thejwks_uri
to download the signing keys to validate the token. If this variable is omitted, RabbitMQ looks up the default Authorization Provider's id in the variableauth_oauth2.default_oauth_provider
, and if it is also omitted, RabbitMQ usesauth_oauth2.issuer
orauth_oauth2.jwks_uri
to download the signings keys to validate the token.
The list of supported resource servers is the combination of auth_oauth2.resource_servers
and auth_oauth2.resource_server_id
. You can use both or only one of them.
There is an example that demonstrate how to use multiple OAuth 2 resources.
A list of all the configurable variables for each OAuth Provider is documented in a separate section.
Configure multiple OAuth 2.0 providers
It only makes sense to set multiple OAuth 2.0 providers if there are multiple resources configured.
This is the configuration used in the previous section but modified to use multiple OAuth 2.0 providers:
auth_oauth2.scope_prefix = rabbitmq.
auth_oauth2.resource_servers.1.id = rabbit_prod
auth_oauth2.resource_servers.1.oauth_provider_id = prod
auth_oauth2.resource_servers.2.id = rabbit_dev
auth_oauth2.resource_servers.2.oauth_provider_id = dev
auth_oauth2.resource_servers.2.scope_prefix = dev-rabbitmq.
auth_oauth2.oauth_providers.prod.issuer = https://rabbit_prod:8080
auth_oauth2.oauth_providers.prod.https.cacertfile = /opts/certs/prod.pem
auth_oauth2.oauth_providers.dev.issuer = https://rabbit_dev:8080
auth_oauth2.oauth_providers.dev.https.cacertfile = /opts/certs/dev.pem
A list of all the configurable variables for each OAuth Provider is documented in a separate section.
Examples
The RabbitMQ OAuth 2.0 Auth backend examples contain many example configuration files, that can be used to set up several OAuth 2.0 providers, including UAA, Auth0, and Azure, and issue tokens, which can be used to access RabbitMQ resources.