developers

Anatomy of an OAuth 2.0 Authorization Request

Dive into the structure of an OAuth 2.0 authorization request to understand its key parameters and why this knowledge is crucial for developers, even when using SDKs.

While modern SDKs and libraries abstract much of the complexity of OAuth 2.0, a good understanding of the underlying authorization request structure remains essential for developers. When issues arise, such as unexpected redirects, token rejection, or other error messages, having insight into the raw request helps in troubleshooting.

This mostly depends on what type of developer you are, of course. It's like opening the hood of your car to diagnose a problem. You don't have to be a mechanic or even an engineer to understand every little detail of your car's engine. But being able to determine whether the engine is overheating because of insufficient coolant or the car won't start because a battery cable has come loose can help you speed up the troubleshooting process, sometimes even without the need for expert intervention.

Let's take a look at what's under the hood of an OAuth 2.0 authorization request.

The Authorization Request Under X-Ray

I assume you have a basic understanding of OAuth 2.0. If not, you can read a quick introduction to the authorization framework and its flows. If you want to go more in depth, check out OAuth 2.0 and OpenID Connect: The Professional Guide.

While this article mentions OAuth 2.0, you will also find details related to OpenID Connect and other OAuth extensions. This is because it’s very common to see the two protocols interconnected. After all, OpenID Connect is nothing but an OAuth 2.0 specialization.

It all starts when a client application redirects a user's browser to the authorization server to begin the authorization process. The authorization request is usually a GET HTTP request sent to the authorization endpoint of the authorization server, although it may be a POST HTTP request too.

The following picture shows a typical authorization request:

Diagram of an OAuth 2.0 authorization request showing the endpoint and parameters like client_id, response_type, and scope.

As you can see from the picture above, the authorization request structure expresses what the client application wants to achieve. You can highlight the main elements of its structure with something vaguely reminiscent of the 5 Ws of journalism:

  • Who is making the request?
  • What artifact is requested?
  • How is the artifact returned?
  • Where will the artifact be sent?
  • Why is the artifact needed?

In addition to these fundamental elements of the request, there are other elements that help improve its security posture.

The Core of an Authorization Request

With reference to the authorization request example mentioned in the previous section, let’s break down the elements that compose it.

The authorization endpoint

The first element of the request we analyze is the authorization server URL: https://my-tenant.auth0.com/authorize.

This is an ordinary URL that includes the authorization server’s domain (my-tenant.auth0.com) and the authorization endpoint (/authorize). As in any common HTTP request, this URL identifies the server’s endpoint to which the client is sending its authorization request.

In the URL example, I’ve used an Auth0 subdomain as the authorization server domain, but in general it will be the domain where the authorization server is reachable. In the context of Auth0, you can use your custom domain, for example.

Regarding the authorization endpoint, while the /authorize endpoint is pretty common, this name is not required by the specifications, which leaves OAuth 2.0 server implementors free to use the name they prefer.

The client ID

The parameter client_id responds to the question, Who is making the request? It identifies the client application that submits the authorization request. The authorization server generates this ID for your application and uses it to retrieve its specific configuration settings.

This is a required parameter.

The response type

The parameter response_type answers the question, What artifact is requested? It specifies the type of authorization flow desired and determines what information will be returned by the authorization server (the artifact).

The parameter can take one of the predefined values code or token. The value code indicates that the client application wants to receive an authorization code, and consequently use the Authorization Code flow or its variant with PKCE. The value token requests an access token and refers to the Implicit flow, which is generally no longer recommended except in very specific cases.

The parameter response_type can also have a custom value as a sequence of strings separated by spaces. For example, OpenID Connect, which is an extension of OAuth 2.0, defines the value id_token and some combinations of values.

This parameter is required.

The response mode

The parameter response_mode tells the authorization server how the requested artifact should be returned. This parameter has been added later to the original OAuth 2.0 specification, and its possible values are:

  • fragment. This value tells the authorization server to attach the requested artifact as a fragment to the redirect_uri (see below). In other words, you will get for example an authorization code as follows:

    https://my-app.com/callback#we7fhHdi2rl6Adywyy1p0ff

    The value fragment is associated with the Implicit flow and is not recommended for several reasons, including information leaking and size limitations.

  • query. This value tells the authorization server to return the requested artifact as a parameter of the query string of the redirect_uri. The following is an example of authorization code returned as a parameter in the query string:

    https://my-app.com/callback?code=we7fhHdi2rl6Adywyy1p0ff

    Returning the artifact as a parameter of the query string has similar security concerns as the fragment option, although techniques like Proof Key for Code Exchange (PKCE) help mitigate the risks.

  • form_post. This value requests the authorization server to return the artifact as an HTML form value and send it via POST. This value has been added to overcome the security concerns affecting the fragment and query modes.
    The following is an example of response from the authorization server:

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Cache-Control: no-cache, no-store
Pragma: no-cache

<html>
 <head><title>Submit This Form</title></head>
 <body onload="javascript:document.forms[0].submit()">
  <form method="post" action="https://my-app.com/callback">
    <input type="hidden" name="state"
     value="DcP7csa3hMlvybERqcieLHrRzKBra"/>
    <input type="hidden" name="id_token"
     value="eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiw
       iYXVkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLC
       ...
       0uVAwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA"/>
  </form>
 </body>
</html>

The response_mode parameter is optional, so what is the default response mode from the authorization server when the parameter is not specified? It depends on the response type sent by the client application. The following table summarizes the default response mode corresponding to each response_type value:

response_typedefault response_mode
codequery
tokenfragment
id_tokenfragment
code tokenfragment
code id_tokenfragment
id_token tokenfragment
code id_token tokenfragment

Note that the upcoming OAuth 2.1 specification will deprecate the token response type, so all the response types containing token will be deprecated as well.

The redirect URI

The redirect_uri parameter specifies where the authorization server will send the artifact. This is a very important parameter that defines the exact address in your application where the artifact is expected to be returned. Since this URL will receive sensitive data (authorization code or tokens), it is imperative to use HTTPS. OAuth 2.0 and OpenID Connect are strict about redirect_uri validation: the URI specified in the request must be an exact match of a pre-registered value on the authorization server. This strictness prevents malicious actors from hijacking tokens by redirecting them to an unintended location.
This parameter is required.

The scopes

The scope parameter explains why the client application is submitting this request. Consider that OAuth 2.0 (and consequently OpenID Connect) enables a client to work on behalf of a user, so the client must specify the user permissions it needs to perform its activities.

The value of the scope parameter is a sequence of strings separated by spaces expressing the needed permissions. OAuth 2.0 does not define the syntax of these strings, so you can freely define them. However, OpenID Connect defines a few reserved scopes.

This parameter is optional, however it is highly recommended to use it to enforce the principle of least privilege. If you don’t specify the scope parameter in your authorization request, the resulting behavior is up to the specific authorization server.

The Security Parameters

The parameters described in the previous section are the core parameters of an authorization request. They reflect the essential elements the authorization server needs to formulate a response after the user’s approval.

However, additional parameters are crucial too to guarantee the security of the request/response transaction. Let’s explore the most common ones.

The state

The state parameter has a unique value generated by the client application. Its primary function is to maintain session state between the client’s request and the server’s response, and to mitigate Cross-Site Request Forgery (CSRF) attacks. The authorization server must include the same value in its response to the client to ensure that it’s related to the original request.

This parameter is not mandatory but recommended.

The nonce

The nonce parameter has been introduced by the OpenID Connect specifications and it acts as an alternative to the state parameter for preventing CSRF and token injection attacks. Its unique value is generated by the client at request time. The authorization server will include the exact value of the nonce parameter in the nonce claim of the ID token. By comparing the value of the parameter with the value contained in the ID token, the client application is guaranteed that the ID token comes from its original request.

This parameter is required for the Implicit flow and in some cases of the Hybrid flow, while it’s optional in other flows.

The PKCE protection

An authorization request can include the code_challenge and code_challenge_method parameters. These parameters are needed to implement Proof Key for Code Exchange (PKCE), an OAuth 2.0 extension that prevents authorization code interception attacks.

To implement PKCE, the client application generates a unique code_verifier value and sends its hashed version (code_challenge) to the authorization server. The client also includes the code_challenge_method parameter to let the authorization server know what hashing algorithm it used. The possible values for the code_challenge_method parameter are:

  • plain, which means no hash,
  • S256, which means that the SHA256 algorithm has been used.

The S256 value is the recommended value.

When the client application will exchange the authorization code for an access token, it will provide the code_verifier to prove it is the original requester.

PKCE also acts as a protection measure against CSRF attacks, so it may be used as an alternative to the state and the nonce parameters, assuming that the authorization server supports it.

The code_challenge parameter is required for public client like mobile and browser apps using the Authorization Code flow and unable to secure client secrets. While optional for confidential clients, it’s highly recommended.

Additional Parameters

The parameters discussed so far can be considered the minimum essential parameters for formulating an authorization request and ensuring its security. However, you may encounter other parameters whose necessity and meaning depend on specific contexts. We won't examine all the other possible parameters you might find in an authorization request, but we'll simply mention a few to give you an idea of what to expect

The prompt

The prompt parameter has been introduced by OpenID Connect and tells how the authorization server should interact with the user during authentication. Its possible values are:

  • none. The authorization server must not display an authentication window. If the user is not already authenticated, an error will be returned.
  • login. The authorization server prompts the user for authentication even if they are already authenticated.
  • consent. The authorization server prompts the user for their consent before returning back to the application client.
  • select_account. The authorization server prompts the user to select an account if they have multiple of them.
  • create. The authorization server shows the user creation form instead of the login form.

Values for the prompt parameter can be combined. For example, you can find prompt=login consent, which tells the authorization server to always show the login and the consent forms. Of course, you can’t combine other values with none.

The login hint

The login_hint parameter tells the authorization server what username to use to authenticate the user. Its value is used by the authorization server to prepopulate the login form. For example, you can find login_hint=john.doe@example.com, telling the authorization server to show a login form with the username set to john.doe@example.com.

The request URI

The request_uri parameter has been introduced by the Pushed Authorization Requests (PAR) specification. This is a special parameter because it essentially replaces all other parameters except client_id. It is used in specific contexts where a high level of security is required. To learn more, I recommend reading this article about Pushed Authorization Requests (PAR).

In short, PAR allows the client application to pre-register an authorization request and receive a request identifier from the authorization server. The request_uri parameter allows you to specify the identifier of the already registered request and thus express the authorization request, as in the following example:

https://my-tenant.auth0.com/authorize?
client_id=32tqG0PwnSoA2Ly4sw
&request_uri=urn%3Aietf%3Aparams%3Aoauth...4ltcg4eY28s

The authorization server will use the identifier specified in the request_uri parameter to detect the registered authorization request and consider the associated parameters.

Want to get up to speed with OAuth2 and OpenID Connect?

Download the free ebookOauth2 OpenID Connect Professional Guide

Conclusion

Understanding the anatomy of the OAuth 2.0 authorization request is a foundational skill for any developer working with modern authentication and authorization. While SDKs simplify much of the implementation, knowing the purpose of each parameter—from client_id and response_type to scope and nonce—empowers you to troubleshoot effectively, design secure applications, and adapt to evolving standards like OAuth 2.1 and OpenID Connect. This knowledge moves you beyond simply using a library to truly mastering the underlying security flows that protect your applications and users.