In this two-part blog series, we will discuss the overview of Oauth, commonly used grant types, Oauth entities, how common grant type works, and some misconfigurations around the same. In this part, we will primarily focus on understanding the OAuth. Before jumping to the vulnerabilities, it is good to know more about OAuth.
What is OAuth 2.0?
OAuth 2.0 is a delegation protocol that lets someone who controls a resource; allow a software application to access that resource on their behalf without impersonating them. The application requests authorization from the resource owner and receives tokens that it can use to access the resource. This happens without the application needing to impersonate the person who controls the resource since the token explicitly represents a delegated right of access.
We need to understand the different definitions for different entities in the OAuth flow. For example, a user wants to login into the Cobalt-test platform using Cobalt. The entities associated with this flow are:
Resource Owner: The resource owner is the user/entity granting access to their protected resource. In our example, it can be the user's data associated with the Cobalt account, such as username, first & last name, etc.
Client Application: The client application requests authorization from the resource owner. In our example, this would be the Cobalt-test application via which the user is trying to login.
Authorization Server: The authorization server is the server issuing access tokens to the client application (Colbalt-test application) after successfully authenticating the resource owner and obtaining authorization. In our example, Cobalt will be the Authorization server.
Resource Server: The resource server is the server handling authenticated requests after the client application has obtained an access token from the authorization server on behalf of the resource owner. In our example, this would be Cobalt.
Note: Often, the Authorization server and Resource server are the same entity and can be called OAuth provider.
client_id: The client_id is a mandatory parameter containing the unique public identifier of the client application. This value is generated when the client application registers with the OAuth service.
response_type: Determines which kind of response the client application expects and, therefore, which flow it wants to initiate. For the authorization code grant type, the value should be code.
Scope: The scope is the requested level of access the client application requests from the resource owner.
redirect_uri: The redirect_uri is the URL the user is redirected to after the authorization is complete. This is also known as the "callback URI" or "callback endpoint." This should match the redirect URL the user has previously registered with the service.
State: The state parameter stores a unique, random, and non-guessable value tied to the client application's current session. According to RFC, it is optional, but this parameter serves as a CSRF Token for the client application by ensuring that the request to its callback endpoint is from the same person who initiated the OAuth flow.
grant_type: The grant_type parameter denotes the grant type and which token will be returned. We will discuss more on this in the later part of the blog.
Code: This code is the authorization code received from the authorization server, which will be in the query string parameter "code" in this request. This code is used in conjunction with the client_id and client_secret by the client application to fetch the access_token.
access_token: The access_token is the token that the client application uses to make API requests on behalf of a resource owner.
refresh_token: The refresh_token allows an application to obtain a new access_token without prompting the user.
High-Level Workflow of the OAuth
This is the high-level flow of OAuth flow.
OAuth Grant Types
Various grand types are used in the OAuth workflow. Let’s discuss them in detail.
Authorization Code Grant
As you become familiar with the basics, you will see that the authorization code grant type is more straightforward than it initially appears.
A series of browser-based HTTP requests initiate the flow between the client application and OAuth service using request redirects. Users are asked for consent before access is granted. If the client accepts, they receive an authorization code. The client application exchanges the OAuth code to obtain the relevant user data with the OAuth service provider.
This workflow diagram shows the steps involved in obtaining authorization codes:
The authorization flow starts when the OAuth client directs the resource owner's user agent to the authorization endpoint. There are several components to an OAuth client, including its client identifier, the requested scope, the local state, and the redirect URL. Once the access is granted or denied, the authorization server sends the user-agent back to the redirect URI.
The authorization server authenticates resource owners through the user-agent header and determines whether access is granted or denied.
If the resource owner grants permission, the user agent is directed back to the OAuth client. A redirection URI contains an authorization code and any local state (previously provided).
Access tokens are requested from the authorization server through the token endpoint. OAuth client authenticates using the client credentials and includes authorization codes from previous steps. A redirect URI also contains the authorization code for verification in the OAuth client.
The authorization server validates client credentials and the authorization code. Additionally, the server verifies that the redirection URI received matches the one sent in Step 3. If valid, the authorization server responds with an access token.
Implicit Grant Type
Implicit grants are comparatively more straightforward. An access token is issued immediately once the user consents rather than first receiving an authorization code. Typically, the web browser handles redirection-based flow between the OAuth client and the resource owner. Redirections should also be available from the authorization server to the OAuth client.
This workflow diagram shows the steps involved in implicit flow:
The authorization flow starts when the OAuth client directs the resource owner's user agent to the authorization endpoint. There are several components to an OAuth client, including its client identifier, requested scope, local state, and redirectURL. The authorization server sends the user-agent back to the redirection URI as soon as access is granted or denied.
Authorization servers authenticate resource owners through the user agent and determine whether access is granted or denied.
User-agent is redirected to the client using the redirection URI provided earlier if the resource owner grants access. The redirect URI includes the access token in the URI fragment.
The user-agent requests the web server without fragments using the redirectinstructions. Fragment information is kept locally by the user agent.
The web server returns an HTML document with an embedded script. Web pages access the redirection URI including fragments retained by the user-agent. The access token and other parameters can also be extracted from this fragment.
User agents run a script from the web server that extracts access tokens and passes them to clients.
Resource Owner Credentials Grant Type
The Resource Owner Credentials grant type uses the username and password credentials of a resource owner (user) to authorize and access protected resources/data from the Resource Server.
The workflow mentioned above diagram illustrates the steps involved in the resource owner credentials flow:
The username and password are provided by the resource owner to the client.
The OAuth client uses the token endpoint on the authorization server to request an access token. Client credentials are used to authenticate, along with credentials received from the resource owner.
The authorization server issues an access token and, optionally, a refresh token after the credentials of the resource owner and client has been successfully validated.
Client Credentials Grant Type
The Client Credentials grant type uses the client_id and the client_secret of a Client to authorize and access protected resources/data from the resource server.
The workflow mentioned above diagram shows the steps involved in the client credentials flow:
An OAuth client requests an access token from the token endpoint by authenticating it with its client credentials.
Upon validating the client credentials, the authorization server issues an access token.
Refresh Token Grant
The Refresh Token grant flow is specially used to gain a new access_token from the authorization server by providing the refresh_token to the token endpoint.
Note: A big difference is that the token is never sent to the protected resource. Rather than involving the resource owner in the process, the client requests new access tokens using the refresh token.
This workflow diagram shows the steps involved in the refresh token flow:
OAuth clients authenticate to the authorization server using the client credentials and present authorization grants to obtain the access token.
The authorization server validates client credentials and authorization grants. The authorization server issues an access token and a refresh token (if needed).
The OAuth client requests protected resources from the resource server using the access token.
The resource server validates the access token. Requests are served if the access token is valid.
Continue to follow steps 3 and 4 until the access token expires. When the access token expires, the OAuth client skips to Step 7. In any other case, the OAuth client will attempt to access another protected resource.
The resource server returns an error if the access token is invalid.
The OAuth client requests a new access token by authenticating its client credentials and presenting the refresh token to the authorization server.
The authorization server validates client credentials and refresh token; if valid, new access and refresh tokens are issued to the client.
It is also possible to extend grant types - the OAuth 2.0 Assertion Profile specifies how assertions can be used for grants. However, other specifications define grant types for SAML2 and JWT tokens specifically.
Using this method, a client can ask for a new access token from the token endpoint, which has been initially obtained from a different authorization server (but one that is trusted).
Let’s discuss the workflow mentioned above in detail.
Obtain user assertions: Users provide assertions. The client application can use a user assertion or create from scratch. A user assertion consists of a JSON web token (JWT) that should be signed by a trusted client using its private key.
Request an access token: An access token is obtained in exchange for the user assertion. As part of the authorization process, the client application sends a request to the authorization server, which includes the user's assertion and either the client's credentials or the client's assertion.
Receive an access token from the authorization server: Using the client id (identifier) and secret, the authorization server authenticates the client, determines whether the client is authorized to make this request, and validates the assertion and other parameters the user provides. The authorization server will return an access token if everything is verified successfully.
Use the access token to make a service request: By using the access token, the OAuth client communicates with the resource server through the REST API.
Proof of Key for Code Exchange
The PKCE protocol is intended for native applications since the end-user will leave the application's security context, authenticate in the browser, and then return to the application securely.
Let’s understand the above-mentioned diagram, which demonstrates how PKCE works.
The authorization Server receives the auth request and the code challenge from the native application through the system browser.
Upon successful authorization, the server will show the end user the login screen.
A successful login will prompt the authorization server for consent to use the requested information.
Upon successful consent from the end user, the authorization server stores the code verifier.
In the query parameter of the redirect URI, the authorization server will pass on the authorization code along with the registered URI.
The end-user will hit the token endpoint with the authorization code and code_verifier (the random string generating the code challenge). code_verifier and code_challenge are verified by the authorization server during this request.
A successful request will result in the end-user receiving the access_token and id_token from the token endpoint. The id_token usually identifies the user, while access_token is used for resource access.
After the above step, the access_token can access protected resources from the resource server. The resource server validates the access_token provided by the authorization server before resources are available.
Access will be granted if the access_token is validated.
The Dynamic Client Registration Protocol
The dynamic client registration protocol (DCRP) consists of a simple HTTP request and response from the authorization server. The client's proposed metadata information is contained in a JSON body of HTTP POST request. Metadata can include display names, redirect URIs, scopes, and many other aspects of the client's functionality.
A client application can register with an OpenID provider according to the OpenID specification. The client application can use POST requests to the /registration endpoint to register itself if dynamic client registration is supported. Config files and documentation usually specify this endpoint's name.
Determining the OAuth authentication process
In most cases, you can recognize when an application uses OAuth authentication. The option to log in with your account from a different website (such as Google, Apple, or Facebook) implies that OAuth is in use.
Check the HTTP messages while proxying your traffic through Burp Suite to identify OAuth authentication when you use this login option. Several query parameters are used for OAuth, regardless of what OAuth grant type. The parameters client_id, redirect_uri, and response_type are particularly important. The following is an example of an authorization request:
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Vulnerabilities in OAuth: How do they occur?
Due to the OAuth specification's flexibility, various authentication vulnerabilities arise. While most implementation is optional, a few components are required for basic functionality. The configuration settings ensure users' data is secure. There is plenty of room for bad practices.
One of the other key issues with OAuth is the general lack of built-in security features. Setting up OAuth is complex and can quickly go wrong if you are not accustomed to it.
We know this is a very long theory blog post, but before diving into the vulnerabilities, it is good to have some exposure to the flow of the OAuth. We will see you in the next part of this series with vulnerabilities very soon.