KEYCLOAK-8297 Documentation for audience

This commit is contained in:
mposolda 2018-09-24 10:17:11 +02:00 committed by Marek Posolda
parent 205e8961f9
commit c56a6a2cdb
16 changed files with 148 additions and 3 deletions

View file

@ -18,3 +18,14 @@ Since these are symmetric signatures only Keycloak is able to verify the signatu
application to use the token introspection endpoint to verify tokens.
Thanks to https://github.com/tnorimat[tnorimat] for contributing a signficant part of this work.
= Better Audience Support for OpenID Connect clients
It is now possible to specify the audiences in the tokens issued for OpenID Connect clients. There is also support for verification
of audience on the adapter side.
= Minor improvements
* Added LocaleSelector SPI, which allows to change the way how the locale will be resolved for a particular request. Thanks to https://github.com/knutz3n[knutz3n]
* Added an authenticator to automatically link Identity Provider identity to an existing account after first Idp authentication. Thanks to https://github.com/slominskir[slominskir]

View file

@ -29,3 +29,7 @@ keycloak-config-file::
Configure alternative class for Role principals attached to JAAS Subject.
Default value is `org.keycloak.adapters.jaas.RolePrincipal`. Note: The class is required to have a constructor with a single `String` argument.
`scope`::
This option is only applicable to the `DirectAccessGrantsLoginModule`. The specified value will be used as the OAuth2 `scope`
parameter in the Resource Owner Password Credentials Grant request.

View file

@ -21,6 +21,7 @@ This is what one might look like:
"bearer-only" : false,
"enable-basic-auth" : false,
"expose-token" : true,
"verify-token-audience" : true,
"credentials" : {
"secret" : "234234-234234-234234"
},
@ -249,3 +250,9 @@ ignore-oauth-query-parameter::
redirect-rewrite-rules::
If needed, specify the Redirect URI rewrite rule. This is an object notation where the key is the regular expression to which the Redirect URI is to be matched and the value is the replacement String.
`$` character can be used for backreferences in the replacement String.
verify-token-audience::
If set to `true`, then during authentication with the bearer token, the adapter will verify whether the token contains this
client name (resource) as an audience. The option is especially useful for services, which primarily serve requests authenticated
by the bearer token. This is set to `false` by default, however for improved security, it is recommended to enable this.
See link:{adminguide_link}#_audience[Audience Support] for more details about audience support.

View file

@ -17,7 +17,7 @@ to impersonate a user. Here's a short summary of the current capabilities of {p
* A client can exchange an external token for a {project_name} token.
* A client can impersonate a user
Token exchange in {project_name} is a very loose implementation of the link:https://www.ietf.org/id/draft-ietf-oauth-token-exchange-14.txt[OAuth Token Exchange] specification at the IETF.
Token exchange in {project_name} is a very loose implementation of the link:https://www.ietf.org/id/draft-ietf-oauth-token-exchange-15.txt[OAuth Token Exchange] specification at the IETF.
We have extended it a little, ignored some of it, and loosely interpreted other parts of the specification. It is
a simple grant type invocation on a realm's OpenID Connect token endpoint.
@ -72,7 +72,7 @@ NOTE: We currently only support OpenID Connect and OAuth exchanges. Support f
A successful response from an exchange invocation will return the HTTP 200 response code with a content type that
depends on the `requested-token-type` and `requested_issuer` the client asks for. OAuth requested token types will return
a JSON document as described in the link:https://www.ietf.org/id/draft-ietf-oauth-token-exchange-14.txt[OAuth Token Exchange] specification.
a JSON document as described in the link:https://www.ietf.org/id/draft-ietf-oauth-token-exchange-15.txt[OAuth Token Exchange] specification.
[source,json]
----

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View file

@ -38,6 +38,7 @@ include::topics/clients.adoc[]
include::topics/clients/client-oidc.adoc[]
include::topics/clients/oidc/confidential.adoc[]
include::topics/clients/oidc/service-accounts.adoc[]
include::topics/clients/oidc/audience.adoc[]
include::topics/clients/client-saml.adoc[]
include::topics/clients/saml/idp-initiated-login.adoc[]
include::topics/clients/saml/entity-descriptors.adoc[]
@ -110,6 +111,7 @@ include::topics/threat/compromised-codes.adoc[]
include::topics/threat/open-redirect.adoc[]
include::topics/threat/password-db-compromised.adoc[]
include::topics/threat/scope.adoc[]
include::topics/threat/audience-limit.adoc[]
include::topics/threat/sql.adoc[]
include::topics/admin-cli.adoc[]

View file

@ -98,6 +98,7 @@ cannot contain a space character in it). The value `openid` is the meta-value us
it for this example. The token will contain mappers and role scope mappings from the client scopes `profile`, `email` (which are
default scopes) and `phone` (an optional client scope requested by the scope parameter).
[[_client_scopes_evaluate]]
==== Evaluating Client Scopes
The tabs `Mappers` and `Scope` of the client contain the protocol mappers and role scope mappings declared solely for this client.

View file

@ -1,3 +1,4 @@
[[_client_installation]]
=== Generating Client Adapter Config

View file

@ -0,0 +1,111 @@
[[_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. This is described below in more details.
As an example, let us assume that you have a bearer-only client `good-service`. Set up {project_name} for audience support like this:
* Log in to the admin console. Go to the _Client Scopes_ tab on the left and click _Create_.
* Choose the _Audience template_.
* Select `good-service` as the requested audience
.Creating Audience Client Scope
image:{project_images}/audience_client-scope-creating.png[]
* When the client scope is created, you should confirm that it contains:
** An audience protocol mapper, which is used for adding the `good-service` as an audience to the access token. See the figure below.
** Role scope mappings for all the client roles of `good-service` client.
.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.

View file

@ -1,6 +1,6 @@
[[_service_accounts]]
=== Service Accounts
==== Service Accounts
Each OIDC client has a built-in _service account_ which allows it to obtain an access token.
This is covered in the OAuth 2.0 specifiation under <<_client_credentials_grant,Client Credentials Grant>>.

View file

@ -0,0 +1,6 @@
=== Limit Token Audience
In environments where the level of trust among services is low, it is a good practice to limit the audiences on the token. The
motivation behind this is described in the https://tools.ietf.org/html/rfc6819#section-5.1.5.5[OAuth2 Threat Model] document and
more details are in the <<_audience, Audience Support section>>.

View file

@ -5,4 +5,6 @@ By default, each new client application has an unlimited `role scope mappings`.
for that client will contain all the permissions the user has. If the client gets compromised and the access token
is leaked, then each system that the user has permission to access is now also compromised. It is highly suggested
that you limit the roles an access token is assigned by using the <<_role_scope_mappings, Scope menu>> for each client.
Or alternatively, you can set role scope mappings at the Client Scope level and assign Client Scopes to your client by using the
<<_client_scopes_linking, Client Scope menu>>.