145 lines
9.7 KiB
Text
145 lines
9.7 KiB
Text
[[_audience]]
|
|
|
|
==== Audience Support
|
|
|
|
The typical environment where the {project_name} is deployed generally consists of a set of _confidential_ or _public_ client
|
|
applications (frontend client applications) which use {project_name} for authentication.
|
|
|
|
There are also _services_ (called _Resource Servers_ in the OAuth 2 specification), which serve requests from frontend client
|
|
applications and provide resources. These services typically require an _Access token_ (Bearer token) to be sent to them to
|
|
authenticate for a particular request. This token was previously obtained by the frontend application when it tries to log in
|
|
against {project_name}.
|
|
|
|
In the environment where the trust among services is low, you may encounter this scenario:
|
|
|
|
. A frontend client called `my-app` is required to be authenticated against {project_name}.
|
|
|
|
. A user is authenticated in {project_name}. {project_name} then issued tokens to the `my-app` application.
|
|
|
|
. The application `my-app` used the token to invoke the service `evil-service`. The application needs to invoke `evil-service` as
|
|
the service is able to serve some very useful data.
|
|
|
|
. The `evil-service` application returned the response to `my-app`. However, at the same time, it kept the token previously sent to it.
|
|
|
|
. The `evil-service` application then invoked another service called `good-service` with the previously kept token. The invocation
|
|
was successful and `good-service` returned the data. This results in broken security as the `evil-service` misused the token to
|
|
access other services on behalf of the client `my-app`.
|
|
|
|
This flow may not be an issue in many environments with the high level of trust among services. However in other environments, where
|
|
the trust among services is lower, this can be problematic.
|
|
|
|
NOTE: In some environments, this example work flow may be even requested behavior as the `evil-service` may need to retrieve
|
|
additional data from `good-service` to be able to properly return the requested data to the original caller (my-app client).
|
|
You may notice similarities with the Kerberos Credential Delegation. As with the Kerberos Credential Delegation, an unlimited
|
|
audience is a mixed blessing as it is only useful when a high level of trust exists among services. Otherwise, it is
|
|
recommended to limit audience as described next. You can limit audience and at the same time allow the `evil-service` to
|
|
retrieve required data from the `good-service`. In this case, you need to ensure that both the `evil-service` and `good-service`
|
|
are added as audiences to the token.
|
|
|
|
To prevent any misuse of the access token as in the example above, it is recommended to limit _Audience_ on the token and configure
|
|
your services to verify the audience on the token. If this is done, the flow above will change, like this:
|
|
|
|
. A frontend client called `my-app` is required to be authenticated against {project_name}.
|
|
|
|
. A user is authenticated in {project_name}. {project_name} then issued tokens to the `my-app` application. The client application
|
|
already knows that it will need to invoke service `evil-service`, so it used `scope=evil-service` in the authentication request
|
|
sent to the {project_name} server. See <<_client_scopes, Client Scopes section>> for more details about the _scope_ parameter.
|
|
The token issued to the `my-app` client contains the audience, as in `"audience": [ "evil-service" ]`, which declares that the
|
|
client wants to use this access token to invoke just the service `evil-service`.
|
|
|
|
. The `evil-service` application served the request to the `my-app`. At the same time, it kept the token previously sent to it.
|
|
|
|
. The `evil-service` application then invoked the `good-service` with the previously kept token. Invocation was not successful
|
|
because `good-service` checks the audience on the token and it sees that audience is only `evil-service`. This is expected behavior
|
|
and security is not broken.
|
|
|
|
If the client wants to invoke the `good-service` later, it will need to obtain another token by issuing the SSO login with the
|
|
`scope=good-service`. The returned token will then contain `good-service` as an audience:
|
|
|
|
[source,json]
|
|
----
|
|
"audience": [ "good-service" ]
|
|
----
|
|
and can be used to invoke `good-service`.
|
|
|
|
===== Setup
|
|
|
|
To properly set up audience checking:
|
|
|
|
* Ensure that services are configured to check audience on the access token sent to them by adding the flag _verify-token-audience_
|
|
in the adapter configuration. See link:{adapterguide_link}#_java_adapter_config[Adapter configuration] for details.
|
|
|
|
* Ensure that when an access token is issued by {project_name}, it contains all requested audiences and does not contain any
|
|
audiences that are not needed. The audience can be either automatically added due the client roles as described
|
|
in the <<_audience_resolve, next section>> or it can be hardcoded as described <<_audience_hardcoded, below>>.
|
|
|
|
[[_audience_resolve]]
|
|
===== Automatically add audience
|
|
|
|
In the default client scope _roles_, there is an _Audience Resolve_
|
|
protocol mapper defined. This protocol mapper will check all the clients for which current token has at least one client role
|
|
available. Then the client ID of each of those clients will be added as an audience automatically. This is especially useful if
|
|
your service (usually bearer-only) clients rely on client roles.
|
|
|
|
As an example, let us assume that you have a bearer-only client `good-service` and the confidential client `my-app`, which you want
|
|
to authenticate and then use the access token issued for the `my-app` to invoke the `good-service` REST service. If the following
|
|
are true:
|
|
|
|
* The `good-service` client has any client roles defined on itself
|
|
|
|
* Target user has at least one of those client roles assigned
|
|
|
|
* Client `my-app` has the role scope mappings for the assigned role
|
|
|
|
then the `good-service` will be automatically added as an audience to the access token issued for the `my-app`.
|
|
|
|
NOTE: If you want to ensure that audience is not added automatically, do not configure role scope mappings directly
|
|
on the `my-app` client, but instead create a dedicated client scope, for example called `good-service`, which will contain the role scope mappings
|
|
for the client roles of the `good-service` client. Assuming that this client scope will be added as an optional client scope to
|
|
the `my-app` client, the client roles and audience will be added to the token just if explicitly requested by the `scope=good-service` parameter.
|
|
|
|
NOTE: The frontend client itself is not automatically added to the access token audience. This allows for easy differentiation between
|
|
the access token and the ID token, because the access token will not contain the client for which the token was issued as an audience. So in
|
|
he example above, the `my-app` won't be added as an audience. If you need the client itself as an audience, see the
|
|
<<_audience_hardcoded, hardcoded audience>> option. However, using the same client as both frontend and REST service is not recommended.
|
|
|
|
[[_audience_hardcoded]]
|
|
===== Hardcoded audience
|
|
|
|
For the case when your service relies on realm roles or does not rely on the roles in the token at all, it can be useful to use hardcoded
|
|
audience. This is a protocol mapper, which will add client ID of the specified service client as an audience to the token.
|
|
You can even use any custom value, for example some URL, if you want different audience than client ID.
|
|
|
|
You can add protocol mapper directly to the frontend client, however than the audience will be always added. If you want more fine-grain
|
|
control, you can create protocol mapper on the dedicated client scope, which will be called for example `good-service`.
|
|
|
|
.Audience Protocol Mapper
|
|
image:{project_images}/audience_mapper.png[]
|
|
|
|
* From the <<_client_installation, Installation tab>> of the `good-service` client, you can generate the adapter
|
|
configuration and you can confirm that _verify-token-audience_ option will be set to true. This indicates that the adapter will
|
|
require verifying the audience if you use this generated configuration.
|
|
|
|
* Finally, you need to ensure that the `my-app` frontend client is able to request `good-service` as an audience in its tokens.
|
|
On the `my-app` client, click the _Client Scopes_ tab. Then assign `good-service` as an optional (or default) client scope. See
|
|
<<_client_scopes_linking, Client Scopes Linking section>> for more details.
|
|
|
|
* You can optionally <<_client_scopes_evaluate, Evaluate Client Scopes>> and generate an example access token. If you do, notice
|
|
that `good-service` will be added to the audience of the generated access token only if `good-service` is included in the _scope_
|
|
parameter in the case you assigned it as an optional client scope.
|
|
|
|
* In your `my-app` application, you must ensure that _scope_ parameter is used with the value `good-service` always included when
|
|
you want to issue the token for accessing the `good-service`.
|
|
See the link:{adapterguide_link}#_params_forwarding[parameters forwarding section], if your application uses the servlet
|
|
adapter, or the link:{adapterguide_link}#_javascript_adapter[javascript adapter section], if your application uses the
|
|
javascript adapter.
|
|
|
|
NOTE: If you are unsure what the correct audience and roles in the token will be, it is always a good idea to
|
|
<<_client_scopes_evaluate, Evaluate Client Scopes>> in the admin console and do some testing around it.
|
|
|
|
NOTE: Both the _Audience_ and _Audience Resolve_ protocol mappers add the audiences just to the access token by default. The ID Token
|
|
typically contains only single audience, which is the client ID of the client for which the token was issued. This is a requirement
|
|
of the OpenID Connect specification. On the other hand, the access token does not necessarily have the client ID of the client,
|
|
which was the token issued for, unless any of the audience mappers added it.
|
|
|
|
|