Note: We’ll focus on the “authorization code” and “implicit” grant types as these are by far the most common.
For any OAuth grant type, the client application has to specify
which data it wants to access and what kind of operations it wants
to perform. It does this using the scope parameter of
the authorization request it sends to the OAuth service.
As the name of the scope is just an arbitrary text string, the format can vary dramatically between providers.
scope=contacts
scope=contacts.read
scope=contact-list-r
scope=https://oauth-authorization-server.com/auth/scopes/user/contacts.readonlyWhen OAuth is used for authentication, however, the standardized OpenID Connect scopes are often used instead.
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
client_id: Mandatory parameter containing the
unique identifier of the client application. This value is generated
when the client application registers with the OAuth service.redirect_uri: The URI to which the user’s browser
should be redirected when sending the authorization code to the
client application. This is also known as the “callback URI” or
“callback endpoint”. Many OAuth attacks are based on exploiting
flaws in the validation of this parameter.response_type: Determines which kind of response
the client application is expecting and, therefore, which flow it
wants to initiate. For the authorization code grant type, the value
should be code.scope: Used to specify which subset of the user’s
data the client application wants to access. Note that these may be
custom scopes set by the OAuth provider or standardized scopes
defined by the OpenID Connect specification.state: Stores a unique, unguessable value that is
tied to the current session on the client application. The OAuth
service should return this exact value in the response, along with
the authorization code. This parameter serves as a form of CSRF
token for the client application by making sure that the request to
its /callback endpoint is from the same person who
initiated the OAuth flow.When a user sends an initial request to an authorization server, they are redirected to a login page to access their account via an OAuth provider, such as a social media account.
After logging in, the user is presented with a list of data that the client application wants to access, based on the scopes defined in the authorization request. The user can choose whether or not to grant this access.
Once the user approves the access, future sessions with the same OAuth service will be automatic, allowing the user to quickly access the client application without needing to log in or give consent again.
If the user consents to the requested access, their browser will
be redirected to the /callback endpoint that was
specified in the redirect_uri parameter of the
authorization request. The resulting GET request will
contain the authorization code as a query parameter. Depending on
the configuration, it may also send the state parameter
with the same value as in the authorization request.
GET /callback?code=a1b2c3d4e5f6g7h8&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com
Once the client application receives the authorization code, it
needs to exchange it for an access token. To do this, it sends a
server-to-server POST request to the OAuth service’s
/token endpoint. All communication from this point on
takes place in a secure back-channel and, therefore, cannot usually
be observed or controlled by an attacker.
POST /token HTTP/1.1
Host: oauth-authorization-server.com
…
client_id=12345&client_secret=SECRET&redirect_uri=https://client-app.com/callback&grant_type=authorization_code&code=a1b2c3d4e5f6g7h8
In addition to the client_id and authorization
code, you will notice the following new parameters:
client_secret: The client application must
authenticate itself by including the secret key that it was assigned
when registering with the OAuth service.grant_type: Used to make sure the new endpoint
knows which grant type the client application wants to use. In this
case, this should be set to authorization_code.The OAuth service will validate the access token request. If everything is as expected, the server responds by granting the client application an access token with the requested scope.
{
"access_token": "z0y9x8w7v6u5",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid profile",
…
}Now the client application has the access code, it can finally
fetch the user’s data from the resource server. To do this, it makes
an API call to the OAuth service’s /userinfo endpoint.
The access token is submitted in the
Authorization: Bearer header to prove that the client
application has permission to access this data.
GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
The resource server should verify that the token is valid and that it belongs to the current client application. If so, it will respond by sending the requested resource i.e. the user’s data based on the scope of the access token.
{
"username":"carlos",
"email":"carlos@carlos-montoya.net",
…
}The client application can finally use this data for its intended purpose. In the case of OAuth authentication, it will typically be used as an ID to grant the user an authenticated session, effectively logging them in.
The implicit grant type is much simpler. Rather than first obtaining an authorization code and then exchanging it for an access token, the client application receives the access token immediately after the user gives their consent.
You may be wondering why client applications don’t always use the implicit grant type. The answer is relatively simple - it is far less secure. When using the implicit grant type, all communication happens via browser redirects - there is no secure back-channel like in the authorization code flow. This means that the sensitive access token and the user’s data are more exposed to potential attacks.
The implicit grant type is more suited to single-page
applications and native desktop applications, which cannot easily
store the client_secret on the back-end, and therefore,
don’t benefit as much from using the authorization code grant
type.
The implicit flow starts in much the same way as the
authorization code flow. The only major difference is that the
response_type parameter must be set to
token.
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
The user logs in and decides whether to consent to the requested permissions or not. This process is exactly the same as for the authorization code flow.
If the user gives their consent to the requested access, this is
where things start to differ. The OAuth service will redirect the
user’s browser to the redirect_uri specified in the
authorization request. However, instead of sending a query parameter
containing an authorization code, it will send the access token and
other token-specific data as a URL fragment.
GET /callback#access_token=z0y9x8w7v6u5&token_type=Bearer&expires_in=5000&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com
As the access token is sent in a URL fragment, it is never sent directly to the client application. Instead, the client application must use a suitable script to extract the fragment and store it.
Once the client application has successfully extracted the access
token from the URL fragment, it can use it to make API calls to the
OAuth service’s /userinfo endpoint. Unlike in the
authorization code flow, this also happens via the browser.
GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
The resource server should verify that the token is valid and that it belongs to the current client application. If so, it will respond by sending the requested resource i.e. the user’s data based on the scope associated with the access token.
{
"username":"carlos",
"email":"carlos@carlos-montoya.net"
}The client application can finally use this data for its intended purpose. In the case of OAuth authentication, it will typically be used as an ID to grant the user an authenticated session, effectively logging them in.