Authorization code grant
- Endpoints
-
-
/oauth2/userinfo (OpenID Connect [OIDC])
The authorization code grant flow for OAuth 2.0 and OIDC lets a confidential client, such as a web application running on a server, exchange an authorization code for an access token to get authorized access to protected resources.
The authorization code grant is secure because:
-
It is a two-step process:
-
The resource owner authenticates to the authorization server and authorizes the client to access the protected resource. As confirmation, the client receives a temporary authorization code from the server.
-
The authorization server validates the authorization code and exchanges it for an access token.
-
-
The authorization server delivers the access token directly to the client, usually over HTTPS. Neither the access token nor the client secret is exposed publicly, which protects confidential clients.
The authorization code grant flow
OAuth 2.0
-
The client, usually a web-based service, receives a request to access a protected resource. To access the resource, the client requires authorization from the resource owner.
-
The client redirects the resource owner’s user-agent to the authorization server.
-
The authorization server authenticates the resource owner, confirms resource access, and gathers consent if required.
-
The authorization server redirects the resource owner’s user agent to the client.
-
During the redirection process, the authorization server appends an authorization code.
-
The client receives the authorization code and authenticates to the authorization server to exchange the code for an access token.
Note that this example assumes a confidential client. Public clients are not required to authenticate.
-
If the authorization code is valid, the authorization server returns an access token (and a refresh token, if configured) to the client.
-
The client requests access to the protected resource from the resource server.
-
The resource server contacts the authorization server to validate the access token.
-
The authorization server validates the token and responds to the resource server.
-
If the token is valid, the resource server lets the client access the protected resource.
OIDC
-
The end user wants to use the services provided by the relying party (RP). The RP, usually a web-based service, requires an account to provide those services.
The end user issues a request to share their information with the RP.
-
To access the end user’s information in the OpenID provider (OP), the RP requires end user consent.
The RP redirects the end user’s user-agent...
-
...to the OP.
-
The OP authenticates the end user, confirms resource access, and gathers consent if necessary.
-
The OP redirects the end user’s user-agent to the RP.
-
During the redirection process, the OP appends an authorization code.
-
The RP authenticates to the OP and exchanges the authorization code for an access token and an ID token.
Note that this example assumes a confidential client. Public clients are not required to authenticate.
-
If the authorization code is valid, the OP returns an access token and an ID token to the RP.
-
The RP validates the ID token and its claims.
The RP can use the ID token subject ID claim as the end user’s identity.
-
If the RP requires additional claims, it sends a request to the /oauth2/userinfo endpoint with the access token for authorization.
-
If the access token is valid, the
/oauth2/userinfo
endpoint returns any additional claims.The RP can use the subject ID and the additional claims to identify the end user.
Demonstrate the authorization code grant flow
Perform these steps to get an authorization code and exchange it for an access token:
Prepare the demonstration
Complete these steps to prepare the authorization code grant flow demonstration:
-
Create an application owner profile and record the username and password.
-
Register a client application.
-
In the Advanced Identity Cloud admin UI, go to Applications and select + Custom Application.
-
Select the sign-in method as OIDC - OpenId Connect and application type as Web.
-
Create the application, providing the following details:
- Name
-
myClient
- Owners
-
<application-owner>
- Client ID
-
myClient
- Client Secret
-
forgerock
-
Switch to the Sign On tab and under General Settings, set these fields to have the following values:
- Sign-in URLs
-
https://www.example.com:443/callback
- Scopes
-
write
(for OAuth 2.0)
openid
andprofile
(for OIDC)
-
Save your changes.
-
-
Create a resource owner profile and record the username and password.
Get an authorization code using a browser
-
The client redirects the resource owner’s user-agent to the authorization server’s /oauth2/authorize endpoint, including the following query parameters:
-
client_id
:myClient
-
response_type
:code
-
scope
:write
(OAuth 2.0);openid
andprofile
(OIDC) -
redirect_uri
:https://www.example.com:443/callback
https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/authorize ?client_id=myClient &response_type=code &scope=write &state=abc123 &redirect_uri=https://www.example.com:443/callback
For OIDC, set
scope=openid profile
instead.The URL is split and spaces added for readability purposes.
The
scope
parameter is optional if default values are configured in the authorization server or the client.The
state
parameter is included to protect against CSRF attacks but is also optional. -
-
The resource owner authenticates to the authorization server. In this demonstration, they sign in using the default journey configured for the realm.
By default, client applications in Advanced Identity Cloud use implied consent. If Advanced Identity Cloud is configured to require explicit consent, the authorization server presents the resource owner with a consent screen. To continue the flow, the resource owner must select
Allow
to grant consent.The authorization server redirects the resource owner to the URL specified in the
redirect_uri
parameter. -
Inspect the URL in the browser.
It contains a
code
parameter with the authorization code issued by the authorization server.For example:
https://www.example.com/callback?code=<authorization-code>&iss...
-
Follow the steps to get an access token.
Get an authorization code using REST
-
Authenticate as the resource owner.
For example:
$ curl \ --request POST \ --header 'Content-Type: application/json' \ --header 'X-OpenAM-Username: <resource-owner-username>' \ --header 'X-OpenAM-Password: <resource-owner-password>' \ --header "Accept-API-Version: resource=2.0, protocol=1.0" \ 'https://<tenant-env-fqdn>/am/json/realms/root/realms/alpha/authenticate' {"tokenId":"<tokenId>","successUrl":"/enduser/?realm=/alpha","realm":"/alpha"}
-
As the client, call the /oauth2/authorize endpoint to request the authorization code. Provide the resource owner’s SSO token in a cookie and the following parameters:
-
scope
:write
(OAuth 2.0);openid
andprofile
(OIDC) -
response_type
:code
-
client_id
:myClient
-
csrf
:<tokenId>
-
redirect_uri
:https://www.example.com:443/callback
-
decision
:allow
For example:
$ curl --dump-header - \ --request POST \ --cookie "<session-cookie-name>=<tokenId>" \ --data "scope=write" \ --data "response_type=code" \ --data "client_id=myClient" \ --data "csrf=<tokenId>" \ --data "redirect_uri=https://www.example.com:443/callback" \ --data "state=abc123" \ --data "decision=allow" \ "https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/authorize"
For OIDC, set
scope=openid profile
instead.The
scope
parameter is optional if default values are configured in the authorization server or the client.The
state
parameter is included to protect against CSRF attacks but is also optional.If the authorization server is able to authenticate the user and the client, it returns an HTTP 302 response with the authorization code appended to the redirection URL:
HTTP/2 302 ... location: https://www.example.com:443/callback?code=<authorization-code>&iss=https%3A%2F%2Fopenam.example.com%3A8443%2Fam%2Foauth2%2Frealms%2Froot%2Frealms%2Falpha&state=abc123&client_id=myClient ...
-
-
Follow the steps to get an access token.
Exchange an authorization code for an access token
As the client, call the /oauth2/access_token endpoint to exchange the authorization code for an access token. Provide the following parameters:
-
Authentication credentials:
myClient:forgerock
-
grant_type
:authorization_code
-
code
:<authorization-code>
-
redirect_uri
:https://www.example.com:443/callback
For example:
$ curl \ --request POST \ --user 'myClient:forgerock' \ --data "grant_type=authorization_code" \ --data "code=<authorization-code>" \ --data "redirect_uri=https://www.example.com:443/callback" \ "https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/access_token"
This example uses
--user '<client_id>:<client_secret>'
to authenticate the client, the default for Advanced Identity Cloud OAuth 2.0 client applications.For information about the different ways to authenticate confidential clients, refer to Client application authentication.
The
redirect_uri
and client parameters must match those used for the authorization code request, or the authorization server will not validate the code.For OAuth 2.0, the authorization server returns an access token; for example:
{ "access_token": "<access-token>", "refresh_token": "<refresh-token>", "scope": "write", "token_type": "Bearer", "expires_in": 3599 }
For OIDC, the OP returns an access token and an ID token; for example:
{ "access_token": "<access-token>", "refresh_token": "<refresh-token>", "scope": "openid profile", "id_token": "<id-token>", "token_type": "Bearer", "expires_in": 3599 }
If the RP does not require the access token, revoke it.
By default, PingOne Advanced Identity Cloud also issues a refresh token whenever it issues access tokens.
Additional OIDC claims
An RP can request additional claims about the end user with the access token at the /oauth2/userinfo endpoint:
$ curl \
--request GET \
--header "Authorization Bearer <access-token>" \
"https://<tenant-env-fqdn>/am/oauth2/realms/root/realms/alpha/userinfo"
{
"name": "<resource-owner-display-name>",
"family_name": "<resource-owner-family-name>",
"given_name": "<resource-owner-given-name>",
"sub": "<resource-owner-id>",
"subname": "<resource-owner-id>"
}