By Thiago Duda
Introduction to OAuth 2.0
OAuth (Open Authorization) is an authorization protocol that allows applications to securely access third-party resources on behalf of their users. One of its main advantages is its ability to facilitate integration between systems without the need to share the owner’s credentials of the requested resource, using access tokens as intermediaries. Notably, it’s widely used in login via social networks, allowing users to access different applications with their Google, Facebook or other platform accounts, without the need to create credentials for each application.
Before we discuss vulnerabilities, it’s essential to understand the fundamental components and how they interact during the authorization process. The first component we’ll cover is the roles, which define the main actors in the authorization flow. These are:
- Resource Owner: is the user/entity that owns the data, or resources that will be requested and granted if authorized;
- Resource Server: a server that protects and grants access to data, based on the validity of the access tokens generated by the authorization server and the authorizations made by the resource owner;
- Client: is the application that requests access to the resources/data allowed by the user;
- Authorization Server: is the server responsible for issuing access tokens after the user has duly authenticated and consented.
Grant Types: What are they and what are they for?
There are four main grant types in OAuth 2.0, each suitable for different use cases:
- Authorization Code: this is the one most commonly used in web applications. The client application redirects the user to the authorization server, where they authenticate and consent to the data being shared. The authorization server issues an authorization code, which is exchanged for an access token by the client. This method is considered secure because the exchange of tokens takes place in the back-end of the client application, without exposing the token directly to the browser;
- Implicit: commonly used in SPAs and mobile applications. Here, the access token is issued directly to the client without an intermediary authorization code. This method, however, is less secure compared to the Authorization Code, as the token is delivered directly to the user’s browser, increasing the possibility of interception;
- Resource Owner Password Credentials (ROPC): used in cases where the client and the authorization server belong to the same domain. The resource owner provides their credentials (username and password) directly to the client, which uses them to obtain an access token. Although simple, this approach is considered insecure and obsolete in many modern scenarios;
- Client Credentials: this type of concession is used in machine-to-machine (M2M) scenarios, in which the client requests an access token directly from the authorization server, without involving an end user. This is common in service integrations and internal automations, where there’s no interaction with users.
The aim of this article is to focus on the grant type commonly found in web applications — Authorization Code, and to address the main security problems that can arise from inadequate implementation. So, before we delve into how it works, it’s important to understand what each parameter in the request to start the flow represents.
Each parameter has a specific role. We’re going to detail the main ones, with a practical example of how they usually appear in a real scenario:
- client_id: this parameter is the unique identifier of the client application requesting authorization. It’s provided by the authorization server when the client application registers. The client_id ensures that the server knows exactly which application is requesting access and is essential for associating the authorization request with the correct client.
- redirect_uri: defines the address to which the authorization code issued by the Authorization Server will be sent.
- response_type: indicates the type of response the client expects from the authorization server. In the case of the Authorization Code flow, the value of this parameter must be code, informing the server that the client wishes to receive an authorization code as a response. Through this parameter, we can identify which grant type is being used by the application.
- scope: defines the access levels that the client is requesting in relation to the user’s resources. Each scope specifies a set of permissions, such as access to profile data, for example.
- state: this parameter is used to protect against Cross-Site Request Forgery (CSRF) attacks and guarantee the integrity of the flow. The client application generates a unique, random value that will be included in the request. When the server redirects the user back, the client checks that the returned state value matches the original value, mitigating possible attacks. Although not strictly mandatory, its absence or inadequate implementation can lead to vulnerabilities.
These are the main parameters that form the basis of the authorization request in the Authorization Code flow. Implementing them correctly is essential to avoid failures and guarantee reliable authorization.
Authorization Code workflow

As can be seen in the image above, the authorization flow is initiated by the user’s intention to use an OAuth service. Therefore, the client application starts the authorization flow by asking the user to grant access to their data — within the predefined scope, through the chosen OAuth service.
This first stage usually starts with the following request:

Once the user has given their consent, the OAuth server directs them back to the client application, sending the confirmation code. The request below illustrates this step:

After these initial steps, communication becomes server-to-server, thus adding another layer of security to the token exchange. Thus, the client application server will provide the code obtained in the previous step to the OAuth service server which, in turn, will respond with the access token that will be used to request the user’s resources.

Finally, an API call is made to the OAuth service, which validates the access token and, if valid, returns the requested data.

Common vulnerabilities:
After building the necessary foundation to understand the main components of OAuth and the Authorization Code Grant Type, we will next address the main vulnerabilities inherent in their inadequate implementation.
Open Redirect:
One of the most well-known vulnerabilities in the protocol, and also in web applications, is arbitrary redirection, which occurs when the redirect_uri URL provided by the client is not properly handled by the authorization server. This allows a possible attacker to trick the user into going to a malicious application after authentication, making it possible to capture access tokens.

In a real scenario, an attacker can manipulate the redirect_uri parameter to obtain the victim’s Authorization Code. Thus, when the victim accesses the page and starts the login process via OAuth, the code will be captured by the attacker, who can then provide it to the callback address and obtain the token.
The following flow illustrates the scenario in which the victim starts the OAuth authorization process with the redirect_uri parameter modified to an attacker control address:

Note that the request with the victim’s authorization code is sent to the attacker’s application:

The attacker then starts the authentication flow via OAuth and, when sending the code, replaces the original value with the value that was previously captured:

Finally, note that the attack has been carried out, as the attacker has authenticated himself in the victim’s account:

Mitigation:
To mitigate this vulnerability, redirect URLs should be restricted to a predefined set of trusted domains — allow listing, on the authorization server. This ensures that redirects only occur to secure locations. In addition, validation must be done on the server side, preventing this information from being easily manipulated. On the other hand, it’s important to monitor access to the authorization server, ensuring that the redirection address allowed has not been manipulated in the event of a compromise. It’s important to note that some of the best-known OAuth providers, such as — Google, Facebook etc — already handle this parameter adequately. However, in the event of authorization server credentials being compromised, an attacker could add domains they control to the list of trusted domains, which would enable attacks to be conducted.
CSRF – Improper implementation of the state parameter:
The absence or inadequate implementation of the state parameter in OAuth authentication flows can expose applications to Cross-Site Request Forgery attacks, enabling an attacker to manipulate authorization requests and hijack user accounts. This vulnerability occurs when at least one of the following criteria is met:
- the state parameter is not used;
- it’s set to a fixed or predictable value;
- there is no proper validation on the server;
- the value can be reused in multiple requests.
The following steps are used in the attack scenario:
- the attacker starts the profile binding flow in the vulnerable application and obtains the valid authorization link, with absence or improper implementation of the state parameter;
- he shares this link with the victim via phishing or a malicious application;
- if the victim accesses the link, their account in the application will be linked to the attacker’s OAuth profile;
- finally, when the attacker uses the “Login with OAuth” option, he will be authenticated to the victim’s account.
To illustrate a practical scenario, imagine that an application allows users to link social network profiles to their accounts using OAuth. In the correct authorization flow, the application must generate a unique value for the state parameter, which will be validated by the server when it receives the authorization response. However, if this parameter is missing or poorly implemented, an attacker can exploit this flaw to link the victim’s account to their own social profile.
The image below illustrates the application with the characteristics described above. Note that the application has a feature that allows the user to link a social network profile to their account and then use it to authenticate themselves in the application.

Note that, in the example, the user wiener is linked to the social profile peter.wiener. This means that if you choose “Login with OAuth” using the peter.wiener account, you will authenticate with the wiener account. Thus, once the application does not correctly implement the state parameter, the attacker can share an account link with the victim. When the victim accesses this link, their account is linked to the attacker’s social profile.
To exploit this flaw, the attacker can initiate the flow of linking a profile to their account, using their OAuth provider credentials and, at the moment the authorization code is exchanged, intercept the request and use the captured value to carry out the attack. The image below illustrates the attacker’s code capture:

Once the code had been obtained, the attacker could use an iframe in a malicious application and induce the victim to access it. Below is the payload used to demonstrate the attack:

Finally, when the victim accesses the malicious application, their account will be linked to the attacker’s profile. From this moment on, when the attacker chooses the “Login with OAuth” option, they will have access to the victim’s account. The image below shows the attack scenario:

Mitigation:
To avoid the risks mentioned above, it’s essential to adopt good practices during implementation. The first measure is to use the state parameter dynamically, generating a unique and non-predictable value for each request. It’s also fundamental to validate the parameter correctly. In addition, you can implement an expiration mechanism for the parameter values. They should have a limited time of validity and, if a response is received from the authorization server after this period, the request should be rejected. This prevents the reuse of old values, which could be captured by attackers.
Possibility of account takeover (Pre-Account Takeover):
Popularly referred to as Pre-Account Takeover, the scenario we’ll discuss next arises from the absence of registration validation by applications that implement both OAuth and login/registration forms for session management. This absence occurs when the application allows an e-mail to be registered without requiring prior verification of its ownership, for example through a confirmation link sent to the user. This makes it possible for an attacker to register an account using someone else’s e-mail address and then, when the victim tries to recover or create their real account, they end up assuming an already compromised profile.
The diagram below illustrates how Pre-Account Takeover can be conducted in order to obtain sensitive data from the victim:

As seen in the image above, step (1) consists of registering with the vulnerable application, using the target victim’s e-mail address, which is registered with the OAuth service used by the application. Because there is no registration validation, the account will be created automatically, even if the attacker has no control over the e-mail address used.
In step number (2), the victim uses OAuth to log in to the application, using the e-mail address as the user key, so when logging in via both OAuth and the form, the account accessed will be the same. This is an important fact to note, since some applications can differentiate accounts based on the mechanism used to create them.
Next, in step (3), the victim — already authenticated — uses the application and updates registration data, fills in sensitive information such as bank details, etc.
Finally, in the last stage, the attacker accesses the account and has access to all the data that the victim, unaware of the account’s compromise, has filled in. In more critical scenarios, such as e-commerce applications, the attack can have an even greater impact if the user saves credit card data, has a balance available for purchases, etc.
Possibility of account takeover
Unlike the previous scenario, the current scenario consists of registering the victim – already registered with the target application – with the OAuth provider, considering that they aren’t registered with it (email service, for example). To make it more intuitive, imagine that the victim is registered with the application, using the e-mail address [email protected]. However, they are not registered with mail-service.com, and the application allows them to log in using OAuth from the aforementioned service. Therefore, the attacker would register at mail-service.com using the victim’s e-mail address, and finally use OAuth to log in to the target user’s account, thus carrying out the Account Takeover.
The diagram below aims to make it easier to understand the attack scenario described above:

Note that, in step number (1), the victim registered for the application using [email protected]. However, they don’t have a profile on the mail.com service, or have chosen to delete their account, for example. Therefore, the attacker registered with the service using the same e-mail address that the victim used to register with the application, as observed in step number (2). Finally, the attacker used the “Login with OAuth” option to authenticate himself in the application and gain access to the victim’s account.
Mitigation:
To mitigate the aforementioned problems, the application needs to implement a registration validation mechanism, with the aim of validating that the user who is registering really is who they say they are. In addition, if the user is already registered and the victim chooses to authenticate via the OAuth service accepted by the application, it’s recommended that notifications be sent to the user whenever a login method other than the usual one is performed on their account, allowing them to unlink if they don’t recognize the access.
Conclusion:
OAuth is a powerful tool that facilitates integration and communication between applications, bringing efficiency and security to resource sharing. However, like any technology, its inadequate implementation can open the doors to serious vulnerabilities that compromise the security of users and applications.
In this article, we address some of the flaws related to OAuth, such as arbitrary redirection, inadequate implementation of the state parameter, account hijacking and token leakage. We also highlight best practices to mitigate these risks. In addition, it’s important to note that, although it wasn’t the focus of this publication, other grant types can also present security problems if not implemented correctly. Therefore, it’s essential to ensure that the implementation of OAuth, regardless of the grant type used, is done in accordance with security standards and recommendations.
References:
BURGESS, PortSwigger Research. OAuth: Exploiting web security. Available at: https://portswigger.net/web-security/oauth
HARDT, Dick. The OAuth 2.0 Authorization Framework. IETF, 2012. RFC 6749. Available at: https://datatracker.ietf.org/doc/html/rfc6749
PORT SWIGGER. OAuth: Forced OAuth Profile Linking Lab. Available at: https://portswigger.net/web-security/oauth/lab-oauth-forced-oauth-profile-linking
CLOUD SECURITY ALLIANCE. OAuth Token: What it is, How it Works, and its Vulnerabilities. Available at: https://cloudsecurityalliance.org/blog/2024/01/09/oauth-token-what-it-is-how-it-works-and-its-vulnerabilities
PORT SWIGGER. OAuth: Authentication Bypass via OAuth Implicit Flow Lab. Available at: https://portswigger.net/web-security/oauth/lab-oauth-authentication-bypass-via-oauth-implicit-flow
COUPA SOFTWARE. How to Mitigate OAuth2 Vulnerabilities. Available at: https://www.coupa.com/blog/how-to-mitigate-oauth2-vulnerabilities/