The OAuth 2.0 protocol, published in October 2012 (RFC 6749), initially defined four primary methods for obtaining tokens, known as authorization grants or “flows”:

  • Resource Owner Password Credentials Grant
  • Client Credentials Grant
  • Implicit Grant
  • Authorization Code Grant

Over time, the protocol evolved, leading to the emergence of additional flows and security enhancements:

However, as of 2017, and reinforced by subsequent best practices (source), the Implicit Grant and Resource Owner Password Credentials Grant have been widely deprecated due to inherent security risks.

This evolution has resulted in a complex landscape. When developing a new application, determining the correct and most secure OAuth “flow” to implement can be challenging.

What OAuth Grant Types To Choose From

While the evolution of OAuth might seem complex, the current landscape for choosing an authorization grant type (or “flow”) is, in fact, quite straightforward. As of 2017, current industry best practice dictates the use of primarily two grant types for applications:

  • Authorization Code with PKCE: Recommended for all public and confidential clients (e.g., single-page applications, mobile apps, traditional web apps) where a user is authenticating. This flow significantly enhances security by preventing authorization code interception attacks.
  • Client Credentials: Ideal for machine-to-machine authentication where an application needs to access resources on its own behalf, without a user’s direct involvement.

Follow this diagram to choose what grant-type to use in your application:

graph TD
    A[Which OAuth Flow Should I Choose?] --> B[Is a human user involved in the authentication?]

    B -- No, it's a service or machine --> C[Choose: Client Credentials Flow]
    
    B -- Yes, a user is logging in --> D["Can the application securely store a secret (Client Secret)?"]
  
    D -- "No, cannot store securely (e.g., Mobile App, <br> Single Page Application in browser)" --> E["Choose: Authorization Code Flow with <br>PKCE (without Client Secret)"]

    D --"Yes, can store securely<br>(e.g., Traditional Web Server Application, Backend)" --> F["Choose: Authorization Code Flow with PKCE <br>(with Client Secret)"] 

Authorization Code vs. PKCE and Client Credentials

The fundamental distinction between these two OAuth flows lies in how the authentication process is initiated and who or what is involved:

  • Authorization Code Flow + PKCE: This flow begins by redirecting a user through their web browser to the /authorize endpoint. The user then interacts with a login interface to provide their credentials. After successful authentication, a token is issued to the application.
  • Client Credentials Flow: This flow initiates authentication directly by an application or machine, which posts its client_id and client_secret to the /token endpoint. There is no user interface or browser interaction involved.

Consequently, the Client Credentials Grant is perfectly suited for automation. For instance, APIs or background services can directly send requests to the token endpoint to obtain access. Crucially, this grant is explicitly designed for machine-to-machine authentication and is not intended for human users.

In contrast, the Authorization Code Grant’s user interface is specifically designed to prevent automated access, ensuring interactions are solely human-driven.

Do I Need A client_secret?

The role of the client_secret has significantly evolved, especially with modern OAuth security practices since around 2017.

In the original OAuth 2.0 Authorization Code Flow, a client_secret was always required to exchange the authorization code for tokens. This made it unsuitable for “public clients” like mobile apps or Single Page Applications (SPAs) running in a browser, as they cannot securely store a secret. This limitation also contributed to the earlier use of the less secure Implicit Flow for such clients, which is now obsolete.

With the introduction of PKCE (Proof Key for Code Exchange), the situation became much simpler and more secure. Modern best practices, as consolidated in initiatives like OAuth 2.1, now recommend:

  • Always use the Authorization Code Flow with PKCE for any user-facing application, regardless of whether it’s a “public” (browser/mobile app) or “confidential” (server-side) client.
  • For public clients, PKCE eliminates the need for a client_secret.
  • For confidential clients, you can still use a client_secret in addition to PKCE for an extra layer of security, but PKCE itself provides the crucial protection against authorization code interception attacks.

In various cases, using a client_secret for user authentication is not required if you’re correctly implementing PKCE. However, since OAuth 2.1, using PKCE with the Authorization Code Flow for user authentication is required.

Public vs. Confidential Clients

The difference between a public client and a confidential client in OAuth 2.0 isn’t about how secretive they are, but rather about their ability to securely store a client_secret. This distinction is crucial for maintaining security in the authentication process.

Public Clients

Public clients are applications that cannot securely store a client_secret. This means that if you were to embed a secret within their code, it could be easily extracted by anyone with access to the application’s source code or runtime environment.

Common examples of public clients include:

  • Single-Page Applications (SPAs): Web applications that run entirely in a user’s browser (e.g., React, Angular, Vue.js apps).
  • Mobile Applications: Apps installed on smartphones or tablets (e.g., iOS, Android apps).
  • Desktop Applications: Software installed on a user’s computer.

For these clients, the client_secret cannot be truly kept secret, as the user always has some level of access to the application’s code. This is why flows like the Authorization Code Flow with PKCE are essential for public clients, as they don’t rely on a shared secret for security.

Confidential Clients

Confidential clients are applications that can securely store and protect a client_secret. They typically run on a server that you control, where the secret isn’t exposed to the end-user or the public internet.

Common examples of confidential clients include:

  • Traditional Web Applications: Applications where the majority of the logic (including handling authentication) runs on a server (e.g., PHP, Node.js, Python, Java web apps).
  • Backend Services / APIs: Server-side applications that communicate with other services or APIs.
  • Backend For Frontend Security Pattern: A SPA Hosted in a BFF.

Since these clients can keep their client_secret secure, they can use it as an additional factor to prove their identity to the authorization server, making the authentication process even more robust. While PKCE is still recommended for confidential clients (for added protection against authorization code interception), they have the option to use a client_secret in conjunction with it.

Summary

In summary, choosing the appropriate authorization flow boils down to choosing between:

  • Authorization Code + PKCE
  • Client Credentials

In most cases, the following applies:

  • SPA? Authorization Code + PKCE + Without Client Secret
  • Mobile App? Authorization Code + PKCE + Without Client Secret
  • Server Side Web App? Authorization Code + PKCE + With Client Secret
  • API? Client Credentials
  • Console App? Client Credentials
Disclaimer