Using the following symbols:
RS
: Resource-Server (holds user's resources, provides authentication/authorization)A
: A client application relying on RS for authentication/authorizationU
: A user of A owning resources on RS
The application A
registers to RS
and acquires its own credentials as a pair of (client_id, client_secret)
. These
are to be used from A
to directly authenticate to RS
.
As a part of registration, A
declares a set of legal redirect (callback) URLs. These are to be used from RS
to redirect a user U
back to the application (in a web application scenario).
In this type of flow (authorization_code
), a user U
interacts with a web application A
behind a browser.
The RS
advertises 2 URLs:
authorization-endpoint
: the service endpoint that handles authorizationtoken-endpoint
: the service endpoint that handles token exchange/renewal.
The application A
builds a URL of the following form and redirects user U
to RS
:
https://<RS>/<authorization-endpoint>?
response_type=code # The response will be an authorization code (grant)
client_id=${client_id} # The client_id identifying application A
redirect_uri=${redirect_uri} # Must be one of redirection URLs as registered from A
scope=${scope} # A RS-specific scope describing the kind of access requested. This is usually represented as set of URIs.
state=${state} # An opaque (to RS) value used by A to maintain state between the request and callback. This is mainly used as an anti-forgery measure (to ensure authorization was indeed initiated by U).
The user is redirected to RS
and grants (or denies) its permission to A
to use the set of resources represented by given scope
.
After user's consent, the RS
redirects user U
back to application A
using the requested redirection_uri
and appending a couple of GET
parameters. The code
is a short-lived token that serves the only purpose to be later exchanged for a real access token. In order to for A
to acquire a real access token, one more step will be required (because A
has also to prove its identity!).
https://<A>/<redirection-path>?
code=${code} # the authorization code (also called authorization grant) that can be later used to acquire an access token.
state=${state} # same value included in 1st redirection (from A to RS)
At this point, A
may check state
to ensure that user U
actually initiated this conversation.
Now, A
exchanges code
acquired at step 1.1
with an access token, by posting a proper request to RS
. This part of communication is happening in the back-channel (i.e. between A
and RS
, no participation of user U
).
POST https://<RS>/<token-endpoint>?
grant_type=authorization_code # what are we trying to exchange with an access token
code=${code} # the code we acquired from redirection from RS to A
redirect_uri=${redirect_uri} # the redirection URL of A (as in 1st redirection)
client_id=${client_id} # The client_id identifying application A
client_secret=${client_secret} # The secret authenticating A to RS
The above request will yield a JSON object with at least the following fields access_token
, token_type
, expires_in
. An example:
{
"access_token": "...", // The access token to be used from A
"refresh_token": "...", // An optional token that can be used to later acquire a new access token
"token_type": "Bearer",
"expires_in": 3600, // seconds to expire
"scope": "...", // accepted scope (meaningful only if user accepted a subset of requested scope)
...
}
The application A
, after it has acquired an access_token
, can access resources owned by U
(whatever is described in given scope
) by using RS
API. To do so it must accompany every API request with an Authorization
header of following format:
Authorization: Bearer ${access_token}
After some time, the access_token
will expire. If response from 1.2
also included a refresh_token
field, then A
may use this value to acquire a new access token (without user's intervention).
The request (also happening in the back channel) is similar to what was sent in 1.2
. It has the following format:
POST https://<RS>/<token-endpoint>?
grant_type=refresh_token # we present a refresh token, and expect a (new) access token
refresh_token=${refresh_token} # the refresh token as the value extracted from the response to 1.2
redirect_uri=${redirect_uri} # the redirection URL of A (as in 1st redirection)
client_id=${client_id} # The client_id identifying application A
client_secret=${client_secret} # The secret authenticating A to RS
In this flow (password
) the application Α
and user U
coincide in terms of trust.
This grant type (via resource owner's password) defines a simplified authentication flow where the client appilication A
holds the user's password and directly requests an access token. Obviously, this grant type can only be used by trusted clients (e.g. a local desktop or a shell application).
Directly request an access token authenticating both as the client and the user:
POST https://<RS>/<token-endpoint>?
grant_type=password # we hold the user's password
username=${username} # The username identifying user U
password=${password} # The password for user U
client_id=${client_id} # The client_id identifying application A
client_secret=${client_secret} # The secret authenticating A to RS
scope=${scope} # A RS-specific scope describing the kind of access requested
Exactly as described in A.2
, just send the Authorization
header to request protected resources from RS
.
Todo
See: