OAuth

Exploring the OAuth Authorization Code Flow: A Comprehensive Guide To Improved Security of Data Sharing

In this article, we'll discuss the potential drawbacks of the bare authorization code flow and explore the various mechanisms that can be implemented to enhance the security of the process. By implementing mechanisms -- such as PAR, PKCE, JAR, and JARM -- we can ensure that users' resources remain secure and protected against unauthorized access.

Background

Over the past decade, the OAuth specification has revolutionized the way third-party applications access user resources. The Authorization Code Flow introduced by OAuth has enabled users to securely grant access to their resources without sharing their passwords. This approach has become a common practice across websites and applications, and has also been adopted as a standard in Open Banking ecosystems.

However, while the authorization flow has proven to be an effective method, it’s not without its drawbacks. Using the bare authorization flow can leave users vulnerable to security risks, such as token leakage and phishing attacks. Therefore, it’s essential to implement additional security mechanisms to improve the security of the authorization flow.

In this article, we’ll discuss the potential drawbacks of the bare authorization flow and explore the various mechanisms that can be implemented to enhance the security of the process. By implementing these mechanisms, we can ensure that users' resources remain secure and protected against unauthorized access.

Flow in Depth

Let’s analyze the authorization code grant flow using the diagram below.

[mermaid-begin]
sequenceDiagram autoNumber participant User participant Client application participant Authorization server activate User User->>Client application: Access activate Client application activate Authorization server Client application->>Authorization server: Request authorization deactivate Client application Authorization server->>User: Display consent User->>Authorization server: Give consent deactivate User Authorization server->>Client application: Issue authorization code activate Client application Client application->>Authorization server: Request token Authorization server->>Client application: Return token deactivate Authorization server deactivate Client application

In step 1) a User is accessing a Client Application. Technically this is not a part of the authorization flow itself, but it visualises when the flow is usually triggered.

In the next step, the client application is redirecting user to authorization endpoint:

GET AUTHORIZE_URL
  ?client_id=$CLIENT_ID
  &scopes=$SCOPES
  &redirect_uri=$REDIRECT_URI
  &response_type=code
  &state=$STATE

Next, the user gives the consent (steps 3-4). Before the consent, the user needs to authenticate, but it’s out of scope of OAuth specification.

In step 5) the user is redirected back to the client application.

GET REDIRECT_URI
  ?code=$CODE
  &state=$STATE

The client application receives short-lived authorization code that is exchanged for access token by making a call to the token endpoint (step 7).

POST TOKEN_URL
  ?client_id=$CLIENT_ID
  &grant_type=authorization_code
  &client_secret=$CLIENT_SECRET
  &code=$CODE
  &redirect_uri=$REDIRECT_URI

Potential Risks Analysis

Let’s consider what is the security of requests made between the application and the authorization server.

  • Authorization request endpoint

    This endpoint does not require authentication. Anyone can trigger the flow. All of the parameters are passed in a plain text and can be logged on a server and stored in the browser history.

  • Redirect to the client application

    Similar to authorization request, code is passed as a plain text parameter.

  • Token request

    This is a backchannel call - the parameters won’t be logged on the server. This endpoint requires client authentication.

Available Security Extensions

There are several security extensions that can improve the security of the authorization flow.

  • PAR

    To enhance the security of authorization requests, a client can use the Pushed Authorization Requests (PAR) mechanism. Instead of passing authorization request parameters to the authorize endpoint in plain text, the client first submits all the parameters to the PAR endpoint and receives a PAR reference ID in return. The client then sends the PAR reference ID and the client identifier to the authorize endpoint.

    By using the PAR mechanism, the authorization request parameters are not logged on the server or available in the browser. The PAR endpoint requires authorization using the same method as token authentication, and all parameters are prevalidated before the user is redirected to the authorization server.

    Additionally, the PAR request URL is short-lived and deleted once used, providing an extra layer of security.

  • PKCE

    The Proof Key for Code Exchange (PKCE) extension was originally designed for apps that cannot store clients securely, such as Single-Page Applications (SPAs) or mobile apps.

    When a client starts the authorization flow with PKCE, it generates a random code_verifier and calculates a hash called code_challenge. The client then passes the code_challenge to the authorize endpoint.

    When the token is exchanged, the client sends the code_verifier to the authorization server. The server recalculates the hash and checks if it matches the original code_challenge to verify the client’s identity.

    PKCE can be used not only for SPA or mobile apps with the token authentication method set to “none,” but also in other authentication methods such as client_secret, private_key_jwt, or mtls.

  • JAR

    To ensure the integrity of the authorization request parameters, they are passed as a signed JSON Web Token (JWT). This provides an additional layer of security, as the parameters cannot be tampered with in transit.

    This extension is commonly used in Open Banking ecosystems, where sensitive data such as intent ID are often passed between parties.

    Furthermore, JWTs can also be encrypted to provide additional protection for sensitive data.

  • JARM

    JARM (JWT Secured Authorization Response Mode) is a mechanism for securely conveying the authorization code from the authorization server to the client. The authorization code is conveyed in a signed JWT called the “response.”

    JARM supports several response modes, one of which is “form_post.jwt.” With this mode, the client receives the response as a POST request instead of a regular GET request, providing an extra layer of security.

    Furthermore, JARM also supports encryption of the response JWT, which can further enhance the security of the authorization code.

Flow revised

Here’s the example how the authorization code can improved.

[mermaid-begin]
sequenceDiagram autoNumber participant User participant Client application participant Authorization server activate User User->>Client application: Access activate Client application activate Authorization server Client application->>Authorization server: PAR with PKCE and JAR Authorization server->>Client application: request URI Client application->>Authorization server: Authorization request with request URI deactivate Client application Authorization server->>User: Display consent User->>Authorization server: Give consent deactivate User Authorization server->>Client application: Issue authorization code using JARM activate Client application Client application->>Authorization server: Request token with PKCE Authorization server->>Client application: Return token deactivate Authorization server deactivate Client application

Instead of redirecting to the authorize endpoint directly, the client application makes a backchannel call to the PAR endpoint. In addition, PKCE is used and parameters are passed as signed JWT (JAR).

Request to PAR endpoint (step 2):

POST PAR_URL
Content-Type: application/x-www-form-urlencoded
  client_id=$CLIENT_ID
  request=$REQUEST_JWT
  code_challenge=$CHALLENGE
  code_challenge_method=$METHOD

Response:

{
  "request_uri": "urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c",
  "expires_in": 60
}

PAR endpoint returns request_uri reference that is passed to the authorize endpoint (step 4):

GET AUTHORIZE_URL
  ?client_id=$CLIENT_ID
  &request_uri=urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c

The authorization code is send to the client as signed and optionally encrypted JWT (step 7).

GET REDIRECT_URI
  ?response=$RESPONSE_JWT

Client exchanges the code using PKCE by making a call to the token endpoint (step 8).

POST TOKEN_URL
  ?client_id=$CLIENT_ID
  &grant_type=authorization_code
  &client_secret=$CLIENT_SECRET
  &code=$CODE
  &redirect_uri=$REDIRECT_URI
  &code_verifier=$VERIFIER

Summary

The article discusses the potential drawbacks of the OAuth specification’s authorization code grant flow. While the flow allows users to grant access to their resources securely, it can leave users vulnerable to security risks such as token leakage and phishing attacks. The article explains the flow in-depth and identifies the potential security risks. To enhance the security of the authorization flow, the article also suggests several security extensions like PAR, PKCE, JAR, and JARM. These extensions can improve the security of the authorization flow by providing an additional layer of security, ensuring the integrity of the authorization request parameters, and making authorization requests more secure.

While using a bare authorization code flow can be a good starting point for securing user data, there are additional measures you can take to further enhance the security of this process. That’s where Cloudentity comes in!

Our platform takes your authorization code flow to the next level by offering support for PAR, PKCE, JAR, and JARM out-of-the-box. These security protocols are designed to provide an additional layer of protection against common vulnerabilities, such as code interception and replay attacks. With Cloudentity, you can rest assured that your users' data is secure and protected from unauthorized access.

By using Cloudentity, you can take advantage of all of these security protocols without having to worry about implementing them yourself. This means you can focus on building your application and providing the best possible user experience, while we take care of the security and data protection.

Updated: Mar 2, 2023