The OAuth Flows You Need for Modern Security
A few years back, I wrote the article “8 Vital OAuth Flows and Powers” for Nordic APIs.
Since then, a lot has happened around the OAuth and OpenID specifications. Some flows have been updated, others have been deprecated and some new ones have been added. With so many changes, now is a good time to update the list to match what OAuth and OpenID Connect have to offer in 2022.
What Has Changed?
Over the past several years, much work has occurred in the Open Banking space, driving the development of new specifications. The OAuth working group has built a Security Best Current Practice (BCP) that outlines ways to mitigate known attack vectors against the OAuth protocol. For example, the BCP advises against using the implicit ilow and the resource owner password credential (ROPC) grant. The OAuth 2.1 specification is taking this further by deprecating the implicit grant and removing ROPC altogether.
The Financial-grade API (FAPI) working group of the OpenID Foundation has finalized numerous specifications that add new flows. These profile specifications also tell implementers what parts they need to adhere to certain security levels. With all this in mind, let’s explore the most relevant flows of today.
Authorization Code Grant
The Authorization Code grant, or Code Flow, is still a widely used OAuth flow. Now that the BCP recommends that public clients use Code Flow with Proof Key for Code Exchange (PKCE) instead of implicit flow, it will probably even see wider adoption.
To obtain a token using the Code Flow, the client sends an authorization request to the OAuth server by simply redirecting the browser to the server. The OAuth server ensures the user is authenticated and prompts the user to approve the delegation. When the user approves, a short-lived code is issued to the client. This code can be considered a one-time password. The client receives this code and can now use it in an authenticated backend call — outside of the browser — and exchange it for the token.
One thing to mention here is that the user only enters their credentials to the OAuth server. The user doesn’t have to give the credentials to the app — they simply enter them to the server they already know and trust. This is one of the main things that OAuth set out to solve.
The other benefit is that the token never passes the browser, which makes it harder to steal. Since the call to exchange the token is authenticated, the server can ensure it delivers the token to the correct client.
Usually, the Code Flow will also allow you to receive a refresh token, which enables the client to get new access tokens without involving the user. This can be processed even after the access token expires.
The Code Flow was initially conceived to only be used by private clients, since the client must authenticate itself when exchanging the code. However, the landscape has since changed, and browsers now support more mechanisms to make it possible to use it together with public clients, if using the PKCE extension.
The Code Flow has been heavily extended to accommodate more complex use cases, like Open Banking and other high-security use cases. This article describes many of the possibilities for the Code Flow protocol.
Proof Key for Code Exchange
While not technically a flow, Proof Key for Code Exchange (PKCE) is the key to using the Code Flow with public clients without a secret. PKCE is an extension to the authorization and token request of the Code flow.
When PKCE is used, the client starts by generating a secret key unique for this flow. In the authorization request, it adds a hash of the secret key to a parameter called code_challenge, along with the name of the method used to hash the key in a separate parameter, code_challenge_method.
The flow continues as usual, but when it reaches the token request, the client adds the parameter code_verifier containing the cleartext value of the secret key. The authorization server (AS) will then hash this value using the provided code_challenge_method and compare the value to what was sent in the initial request. If the values match, the AS knows that the client making the call is the same as the one that made the authorization request and can issue the tokens.
PKCE does not make the client confidential — it’s still public. But since the AS can rely on the redirect Uniform Resource Identifier (URI) and knows that both requests reside from the same source, the Code Flow can be used instead of the implicit flow. The Code Flow has the benefits of preventing replay attacks and keeping tokens outside the URL, so it should be seen as a security improvement.
Pushed Authorization Requests
Another extension to the Code Flow is Pushed Authorization Requests (PAR). PAR is a specification that the Open Banking community has heavily driven.
PAR moves the authorization request of the Code Flow out of the browser and uses the back channel to send the request. The response to this request is a URI that is then used to redirect to the AS, replacing all of the parameters in the Code Flow. This POST request may be authenticated, so it has the benefit of knowing who the client is upfront. This means the AS doesn’t have to rely solely on the redirect URI for security and won’t have to start the authentication and consent process for a potentially illegitimate request.
Running in the back channel also means that requests containing personally identifiable information (PII) are hidden from the URL.
Client Credentials Flow
In the Client Credentials flow, there is no user. This flow is strictly for server-to-server communication — for example, a server must access an API as itself. Therefore, no browser is involved, and a private client is required. To retrieve an access token, the client simply passes its credentials to the OAuth server and receives the token.
This flow may achieve a higher security grade by using a secure client authentication method like Mutual TLS or Client JWT Assertions. No refresh token is issued in this flow since the client can obtain a new access token using its credentials.
Introspection is the process of asking an OAuth server if a token is valid. Access tokens are usually passed around by reference, meaning they do not mean anything to anyone but the OAuth server. The introspection clients are usually an API or an API gateway of sorts.
Introspection is a simple authenticated call where you send in a token and receive a response including data belonging to the token, such as the expiry time and subject. Introspection is a key player in the Phantom Token pattern, which is widely used to hide token contents from those that should not be allowed to read it.
Revocation is one of the great powers of OAuth. Before OAuth, a user that gave away its credentials to an application had no means of retracting that consent. The only way would be to change the password, which might carry worse side effects than disallowing the app to access the user’s account. With OAuth, the user can decide to recall the consent whenever they want by revoking the token. OAuth offers two options for revocation:
- You can revoke the access token, which could be viewed as ending the current session. If there is a refresh token, it would still be valid.
- Revoking the refresh token would make the refresh token invalid, along with any active access tokens that came with it.
The client performs the actual revocation in an authenticated call. Even though they’re authenticated, public clients may be allowed to perform revocation.
Dynamic Client Registration
Dynamic Client Registration (DCR) continues to play a large role in API platforms. It’s beneficial in the mobile AppAuth pattern, but has also been proven useful in Open Banking scenarios. For instance, in PSD2, the European Open Banking regulation, all third parties are issued a trusted certificate to use when communicating with the connected banks.
This certificate may then be used for authenticating while registering your client and as the client authentication later in the selected flows. This makes it easier for the banks to manage authentication since all they need to configure are the trusted issuers — the clients will register themselves and take care of their own configuration.
Dynamic Client Registration Management (DCRM)
Open Banking brings a new requirement that the AppAuth pattern usually does not have: changing a client’s configuration after registration. Since mTLS is heavily used, systems must be able to update the certificates used. The
redirect_uri points to a website rather than a protocol definition in your mobile app, which might change often.
For this case, there is a sibling spec to DCR, Dynamic Client Registration Management (DCRM). DCRM defines an API for a client to update its settings, rotate secrets and even delete the registration. This makes the burden of maintaining config for the clients a lot smaller since the clients themselves may maintain it.
OAuth and OpenID Connect should be considered the de facto standard for identity federation and access delegation, but these standards are still evolving relatively quickly. Missing features are continuously being added, requirements are changing, and clarifications are being made. As such, it can be challenging to keep up with all the developments.
At Curity, we do our best to continuously publish resources on what’s new and updated. Check out our resource library regularly for glimpses of what’s being added. Subscribe to the RSS feed, or use the What’s New page to see the latest articles.