Security Best Practices for OAuth 2.0

This post will follow the guidelines and best practices detailed in the Internet Engineering Task Force article entitled "OAuth 2.0 Security Best Current Practice". In relation to the open source IAM software Keycloak.

Security Best Practices for OAuth 2.0
Photo by FLY:D / Unsplash

The OAuth framework has gained significant traction in our industry. Making it the leading method of authorization. OAuth provides groundwork for authorizing end-users as well as non-human services through the use of different grant types. With such a flexible and widely used authoritative framework, it is important to keep up to date with the current best practices in order to keep our users and services secure.

This post will follow the guidelines and best practices detailed in the Internet Engineering Task Force article: OAuth 2.0 Security Best Current Practice.

In addition, we will cover these topics as it applies to the open source IAM software, Keycloak. You should have some basic knowledge of Keycloak, such as creating realms, as well as have some basic understanding of OAuth.

How May OAuth be Attacked?

OAuth implementations are being attacked through a few weaknesses and anti-patterns.

This includes the usage of some of the legacy grant types, such as implicit grants and password grants. As well as bypassing intrinsic security defenses in place, like with so called "Open Redirectors". Finally, the infrastructure and configuration of the authorization server (i.e. Keycloak) side may allow leakage of state, authorization codes, or at worst access tokens.

OAuth is being used in environments requiring higher security standards. Such as in the banking industry as well as in Government and other public services. Understanding the different types of grants and how to configure them to be as secure as possible is critical to these industries.

We will look at mitigating these known methods of attack below.

OAuth Metadata

The first recommendation is to publish OAuth Metadata, such as defined in RFC8414. And that clients make use of this metadata to configure themselves instead of relying on static configurations.

Keycloak does this automatically by exposing the OAuth metadata in a standard location:

This includes important fields and authorization server capabilities, such as the authorization_endpoint and token_endpoint. In addition, the jwks_uri, which links to a URI which lists the public keys used by Keycloak to sign JWTs.

To learn more about the use of this metadata and connecting client libraries, see the Keycloak documentation below:

https://www.keycloak.org/docs/latest/securing_apps/#endpoints

Implicit Grants

With Implicit grants, the access token is passed through in the authorization response directly. This means that the tokens would be passed in through the redirection URLs directly. Exposing greater attack surfaces and other attack vectors such as:

  • An access token may be saved in browser history.
  • If an attacker is able to redirect to a URI under their control (such as through an “open redirector”), they are able to get direct access to the access token.

Thus, this grant type should not be used. And instead authorization code grant type should be used instead.

Resource Owner Password Credentials Grant

The Resource Owner Password Credentials (ROPC) grants, also known as Direct access grants in Keycloak, is another legacy grant type that should not be used in production environments.

This legacy grant type was originally intended to be used to migrate existing legacy applications and services, that were already handling user credentials directly, to the OAuth framework.

ROPC has numerous security problems, including:

  • Insecurly exposes credentials of the resource owner to the client.
  • Users are trained to enter their credentials in places other than the authorization server.
  • Causes problems when trying to implement two-factor auth, WebAuthn, WebCrypto, or any other multi-step authentication process.

Authorization Code Grant

Authorization Code grants are initiated by browser based applications, generally by human or human-like user agents. The user agent is redirected to the authorization server, where they will be authenticated and authorized, and redirected back to the application with an "authorization code". Which can be exchanged with the authorization server for an identity and access token.

In Keycloak, we can create a new client with "Standard flow" checked in order to enable this grant type.

Wildcard Redirect URIs

Registering redirect URIs is a fundamental validation layer with the authorization code grant. The authorization server will detect that a valid redirect URI is passed in when performing this auth flow.

OAuth is being used in much more dynamic environments. At inception, OAuth had actually assumed more static relationships between the client, authorization server, and resource servers.

Wildcard redirect URI patterns emerged as a means of allowing more dynamic client configurations. Unfortunately, these ambiguous patterns pose a problem due to the more complex implementation and are more error prone to manage.

Let's look at the following redirection URI in Keycloak:

This * suffix allows any URI prefixed with "https://somesite.example/" to be allowed to redirect to after a successful login. Valid URIs that match this rule include:

If one is not familiar with how a particular OAuth implementation validates these redirect URI patterns, this may open the clients to exploitation.

For example, let's assume someone is wanting to allow redirects to a particular host and to any port on that host. They might incorrectly consider the following URI pattern:

  • https://somesite.example*

What they might not realize is that this may open them up to redirects to a host under an attackers control, such as: "https://somesite.example.attacker".

In general, the IETF article states that one should prefer to use static redirect URIs, without any wildcard, because of issues outline above.

There is one exception. For port numbers in localhost redirect URIs for native apps. In Keycloak, you may use the special redirect URI "http://127.0.0.1". Which will also allow a redirect uri to localhost on any port.

Keycloak is looking to improve this user experience by the introduction of specific client policies. So there would be a client policy that would enable, for instance, wildcard matching only on subdomains or matching only on URL fragments.

You can see the ongoing discussion in the community below.

Clients policies for wildcards in redirect-uris · keycloak/keycloak · Discussion #9278
Keycloak currently allows wildcards in the redirect-uri by default, and allows any scheme to be used in redirect URLs as well. Some examples of valid redirect-uris that can be configured for client...

Open Redirectors

An open redirector is a specific endpoint that forwards a users browser to any arbitrary URI. For example, a URI may be put in a query parameter in which is used as a forwarding redirection URI to the authorization server:

Open redirectors allow an attacker to construct URIs pointing to resources that they may control. Exposing the authorization code, in the case when authorization code grant is used, or the access token, if the legacy implicit grant is used.

Proof Key for Code Exchange

It is advised for public clients using authorization code flow to use Proof Key for Code Exchange (PKCE).

Although PKCE was designed as a mechanism to protect native apps, this advice applies to all kinds of OAuth clients, including web applications. For confidential clients (where the "client authentication" option is enabled in Keycloak), the use of PKCE is also recommended. As it may prevent CSRF and mitigate authorization code interception attacks.

PKCE can be enforced, and configured to prevent PKCE downgrade attacks, for a client in Keycloak by enabling a "Proof Key for Code Exchange Code Challenge Method" under the "Advanced Settings". The use of PKCE mode "S256" as the code challenge method is highly encourage.

Client Credentials Grants

Client credentials grants are often used to interconnect services. And are generally authorized using a client id and client secret.

In Keycloak, these are often considered Service Accounts. And enabling this grant type can be done by checking the "Client authentication" (which generates a confidential client secret), as well as checking the "Service accounts roles" during client creation.

Recommendations for Client Credentials

By default, Keycloak generates a random client secret that is sufficiently complicated. The IETF article recommended to use asymmetric methods, such as:

Keycloak supports both of these types of client authentication. Certificates for signed JWTs can be specified by importing, generating, or queried by defining a JWKS URL, in the "Keys" tab.

X509 Certificates may be configured by following:

Preventing Impersonation of Resource Owners

Resource servers may make access control decisions based on the identity provided by the authorization server.

It may be possible to impersonate a particular resource owner as a service account. If for example, client id were to be able to be specified by the client. It may be possible to impersonate a particular resource user by a certain service account.

The administrators of an authorization server should not allow clients to influence their client id or any claim that could cause confusion with a genuine resource owner.

If this cannot be avoided, authorization servers must provide other means for the resource server to distinguish between the two types of access tokens.

One may configure service accounts in Keycloak to have any client id they wish. It is up to the administrator to establish a robust client id generation schema that prevents this type of impersonation.

Security of Refresh Tokens

The purpose of a refresh token is to allow the access token to be short lived. And allow for requesting of new access tokens, once they have expired.

As advised by the IETF article, issuing a refresh token is optional and should be done at the discretion of the authorization server. In addition, the following conditions should be followed:

The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token.  The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client.  If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included by the client in the request.

If refresh tokens are issued, those refresh tokens must be bound to the same scope and resource servers (see below regarding audience restricted access tokens) as originally consented by the resource owner.

Refresh tokens for public clients must be either sender-constrained or use refresh token rotation, as described below, in order to detect compromise:

The authorization server issues a new refresh token with every access token refresh response. The previous refresh token is invalidated but information about the relationship is retained by the authorization server. If a refresh token is compromised and subsequently used by both the attacker and the legitimate client, one of them will present an invalidated refresh token, which will inform the authorization server of the breach. The authorization server cannot determine which party submitted the invalid refresh token, but it will revoke the active refresh token. This stops the attack at the cost of forcing the legitimate client to obtain a fresh authorization grant.

Access Tokens

Access Token Privilege Restrictions

Access tokens represent the authorized privileges and accesses granted on behalf of a user. And as such, must be kept confidential and as short lived as possible.

According to the current best practices, the privileges associated with an access token should be restricted to the minimum required for the particular application or use case. In addition, the resource servers should validate that a given access token is meant for a particular means requested. This should be done through the following:

  • Through audience-restricted access tokens. And/or through the use of client scopes.
  • Or through authorization_details of RFC9396. Which defines a structured mechanism for specifying fine-grained authorization requirements.

Authorization Request and Access Token Leakage

Contents of authorization request or response URIs may be leaked unintentionally through the Referer header. Suppression of the Referer header should be done by applying an appropriate Referrer Policy. Keycloak will automatically set the policy to no-referrer in order to prevent this type of leakage.

An authorization code may end up in a user's browser history. Attackers may learn state from the authorization server if it contains links or third-party content. Or a client may leak the authorization response, if it includes third-party content.

To mitigate the effects of a leaked token, sender constrained access tokens and audience-restriction should be enforced.

Sender-Constrained Access Tokens

A sender-constrained access token provides methods to prevent misuse of leaked access tokens.

A sender-constrained access token scopes the applicability of an access token to a certain sender. This sender must prove that they were the original recipient of the token for the acceptance of that token at the resource server.

The resource server is the one to preform proof of possession check. This can be done using mutual-TLS client authentication and certificate-bound access tokens.

Audience Restricted Access Tokens

The audience may be associated with a particular access token. In order to restrict the token to ideally one or maybe more resource servers. Audience restrictions limit the impact of token leakage.

In deployments where the authorization server knows the URLs of all resource servers, the authorization server may just refuse to issue access tokens for unknown resource server URLs.

Audience restrictions have benefits beyond token leakage mitigation. But allows the authorization server to create access tokens with differing claims based on the resource server specified.

Keycloak clients can be configured to provide audience through mappers. See the documentation around Audience Support for more information.

Authorization Server Infrastructure

It is recommended to use end-to-end TLS. Which means complete encryption between client, authorization server, and any reverse proxy server in-between.

A reverse proxy must sanitize any inbound requests to ensure the authenticity and integrity of all header values relevant for the security of the authorization servers. For example, the X-Forwarded-For header may be used to indicate the address of a connecting client.

A list of headers that should be sanitizing, when operating Keycloak in one of its proxy modes, can be found below:

Cross-Origin Resource Sharing (CORS) may be enabled on the following endpoints:

  • Token endpoint
  • Authorization server metadata endpoint
  • jwks_uri endpoint
  • Dynamic client registration endpoint

CORS should not be used on the authorization endpoint. As the client never accesses this endpoint directly, and only ever redirects users to it.

Keycloak allows one to specify any number of CORS origins through the "Web origins" client configuration.

And there is open discussion for customization of the Access-Control-Allow-Headers returned by preflight requests in the issue below:

[CORS] Allow Access-Control-Allow-Headers customization · Issue #12682 · keycloak/keycloak
Description Allow customization for CORS Access-Control-Allow-Headers Discussion https://keycloak.discourse.group/t/customizing-access-control-allow-headers/7672 Motivation Keycloak has to support ...

Cross Site Request Forgery

The IETF article states:

Clients must prevent Cross-Site Request Forgery (CSRF). CSRF refers to requests to the redirection endpoint that do not originate at the authorization server, but a malicious third party.

A client may rely on the CSRF protection provided by PKCE. In which case, PKCE must be enforced and not be able to downgrade (see above for directions on configuring that on a client level in Keycloak).

Keycloak prevents CSRF attacks against the login and user account portions, using the state parameter, as described in the documentation:

Clickjacking

An attacker may embed the authorization endpoint user interface in an innocuous context, such as an iframe.

IETF article states that the authorization servers must prevent clickjacking attacks.

  • By specifying the X-Frame-Options HTTP response header.
  • By utilizing Content Security Policy (CSP) level 2 [CSP-2] or greater.
  •  Older browsers do not all support the required CSP levels to prevent iframe clickjacking. So JavaScript based framebusting techniques should be used.
  • Authorization servers should allow for configuring specific allowed origins for particular clients.

Keycloak prevents Clickjacking by setting CSP and X-Frame-Options headers as described in:

Conclusion

As we have seen, the OAuth framework provides a lot of types of grants for the many use cases applications need in order to authorize and authenticate users and services.  Unfortunately, there are a lot of configuration options which may allow for insecure methods of exchange between user agent, resource server, or authorization server. But we have at IETF's article regarding OAuth 2.0 best practices. And have applied some of this knowledge to prevent common attack attempts.

Hope this has been informative and insightful. For more information, refer to these wonderful and insightful RFC documents below.