Using the OpenID Connect Hybrid Flow
OpenID Connect introduces a new grant for obtaining Access Tokens and ID Tokens from the OpenID Provider (Curity), the Hybrid Flow. This flow is a mix of the Authorization Code Flow and the Implicit Flow. It allows the client to receive, for example, the ID token directly from the authorization endpoint, but use the code exchange to obtain the Access and Refresh Tokens via a back channel.
In this tutorial, we’ll explain how this flow works and how to configure it in the Curity Identity Server.
Pre-requisites
This tutorial builds on the configuration setup in the “Setup and Getting Started” section and the “Setup a Username Password Authenticator”. If you haven’t done those steps yet you can visit those guides here:
It’s possible to run this tutorial on a custom setup also, but the URLs may be different, as well as the capabilities configured in the profiles.
Overview
In principal, the flow looks like the implicit flow, but a different value is used for the response_type
parameter. It is
either code token
, code id_token
or code id_token token
:
Authorization Endpoint
Browser redirects to the Authorize endpoint of the OAuth Server.
If the user isn’t authenticated the OAuth Server redirect to the Authentication Service.
The User authenticates, and is redirected back to the OAuth Server.
The OAuth Server redirects back to the client with appropriate parameters in the response, based on the value of the
response_type
request parameter:- If
code token
was used, then the server returns theAuthentication Code
and anAccess Token
. - If
code id_token
was used, then the server returns theAuthentication Code
and anID Token
. - If
code id_token token
was used, the server returns theAuthentication Code
, anID Token
and anAccess Token
.
- If
Token Endpoint
- The authorization code is sent to the token endpoint, like in in the Authorization Code flow.
- Backend tokens are returned - Access Token and ID Token. If the server settings permit, the Refresh Token is returned as well.
RFC Reference
For reference, this flow is specified in section 3.3. of the OpenID Connect standardSetup in Curity
Visit the Profiles screen and click the Token Service. On the left select Clients and click New.
New Client
Give the client an ID (eg. www
for a website client).
Capabilities
Scroll down to the Capabilities section and click Add capabilities.
Select both the Code Flow and Implicit Flow capabilities and click Next.
Redirect URL
The redirect URI is back at the client. If you don’t know what you will use just enter http://localhost/callback
for now. This can be changed later.
This tutorial will manually run the flow so localhost is fine. If you plan to use OAuth.tools for testing purposes click the Add Redirect URIs
button to add the callback for oauth.tools.
Client Authentication
For client authentication select secret
and enter a secret. Make sure to remember it since it cannot be retrieved later again (but can be reset).
User Authentication
For user authentication select the authenticator created in the authenticator tutorial.
Add the openid Scope
We will also run the OpenID Code flow, so add the openid
scope to the client by scrolling down to the Permissions section of the client.
Commit
Make sure to remember to commit the changes in the Changes -> Commit menu.
Requesting an Authorization Code and ID Token
With the default configuration we’ve setup, the server’s authorization endpoint is at https://localhost:8443/oauth/v2/oauth-authorize
.
If you are not using that configuration, determine the URL of your authorization endpoint, and adjust the
following requests accordingly. To use the hybrid flow, an app would create a URL to this endpoint, and redirect the user
to it. The URL looks like this:
https://localhost:8443/oauth/v2/oauth-authorize?client_id=www&response_type=code%20id_token&redirect_uri=https://localhost/callback&scope=openid&nonce=538B0D2A-545A-4113-ACD7-A2C3D315C607
As it is an OpenID request the redirect_uri
and nonce
parameters are required. You also need to ask for at least the
openid
scope in the request.
If things are setup correctly, then the user should be presented with a login form. If this is a first use and no accounts are created, you can click Create Account, and register a new account.
After logging in, the user’s browser will be redirected to the redirect URI of the client application. A fragment will
be added to the URI containing the respective parameters, requested by the response_type
, so in this example: code
and id_token
. A sample response will look like this:
https://localhost/callback#code=YJBtNPyndBUH7ddx5AsF9HAnTgYtnV9l&id_token=eyJraWQiOiItMz...I0ykCw
The ID token is ready to be validated, decoded and used by the client, while the code can be further used to be exchanged for an Access Token, exactly like it would have been done in the authorization code flow.
Redeeming the Code
With the code in hand, the client app can redeem it for an Access Token and possibly other tokens — like a Refresh Token and ID Token. To do this, the client app must make an HTTP POST request to the OAuth server’s token endpoint. The body of this request must include the following:
- A
grant_type
parameter with a value ofauthorization_code
. - A
code
parameter where the value is the authorization code plucked off of the callback request. - A
redirect_uri
parameter with the value used in the request to the authorization endpoint.
All of these must be form URL encoded; JSON isn’t allowed.
This call is authenticated; the client must prove its identity by authenticating. There are various ways that this can be done, but the most common is to use the client ID together with a secret. The ID must be the same one that was used in the request to the OAuth server’s authorization endpoint. The ID and secret can be sent in either of two ways:
- In the form-URL-encoded request body together with the other parameters. In this case, the parameters
client_id
andclient_secret
should be used. - In an
Authorization
request header using the HTTP basic mechanism1.
If the client includes its credentials in both of these locations, they must match; otherwise, Curity will reject the call. A sample HTTP request is shown in the following listing (which includes line breaks for readability’s sake):
POST /oauth/v2/oauth-token HTTP/1.1
Host: localhost:8443
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=xPwTedMCpix4I14rYlrlkXWYnBOxEv4G&
client_id=www&
client_secret=the_secret&
redirect_uri=https%3A%2F%2Flocalhost%2Fcallback
The response will be JSON. If things go well, it will contain an Access Token and an ID Token, as the openid
scope was
used in the authorization request. If so configured, the response will also include a Refresh Token:
{
"access_token": "7efda4da-e316-452c-88a6-4cd2482a6f00",
"refresh_token": "d392c88c-5080-451b-a551-c5119e98195c",
"id_token": "eyJraWQiOiItMzgwNzQ4MTIiLCJ4NXQiOiJNUi1wR1RhODY2UmRaTGpONlZ3cmZheTkwN2ciLCJhbGciOiJSUzI1NiJ9.eyJhdF9oYXNoIjoiNEt1SXFOUmFsWWFlZUFYZnlSM21TdyIsImFjciI6InVybjpzZTpjdXJpdHk6YXV0aGVudGljYXRpb246aHRtbC1mb3JtOmh0bWwxIiwiYXpwIjoid3d3IiwiYXV0aF90aW1lIjoxNTIwMzIxOTEyLCJleHAiOjE1MjAzMjYzNzQsIm5iZiI6MTUyMDMyMjc3NCwianRpIjoiZmI2MGZiNDYtNGYwZS00NmI2LTkyNzQtYzJlZDBhMzE0MDU2IiwiaXNzIjoiaHR0cHM6Ly9zcHJ1Y2U6ODQ0My9-IiwiYXVkIjoid3d3Iiwic3ViIjoiam9obmRvZSIsImlhdCI6MTUyMDMyMjc3NCwicHVycG9zZSI6ImlkIn0.D_vHKt1rRwqIXX5VumzFkweiTKWykx7X6Wv7LLYSgAgNoq67ews6PoLlWTnviMNSYXhPV4xpsEqt4b-lMdG53I8g_tslrxVOI3FOy5mysZIub74wkkE0J6Qgba3s8DlbWhj9h4zIO3MNkhfdURJ2PJ6GY6kwc_8Eril0ilNZ8TU_puT8bQHJ_QWxghY3XpeQHtCyzuVDgVv6q7gfcGoy1JxZaLoXNSh02ZIpp7thVrgEAWAiWo7v46HJFiBNpyPnJfzDRwbTIdPFMEKoHOLjUCczsii_4akCb97IVPz5I3bRWASTyig7P_Q0646cNHsHZM-pan7cl5bYb42JI0ykCw",
"scope": "openid",
"token_type": "bearer",
"expires_in": 299
}
The ID token is intended for the client app whereas the Access Token is for the API and the Refresh Token is to be used with the OAuth server. For more details about how to validate an ID Token see Validating an ID Token.
Testing
Testing the code flow can be done using a browser to begin with and then some HTTP client, like curl
or Postman to
redeem the authorization code. This is a very helpful way to see exactly what is going on. It can also be challenging
and certainly doesn’t work well for demos :-)
Instead we suggest to use the online tool OAuth.tools which is a powerful tool to explore OAuth and OpenID Connect.
Some alternatives that you may want to try are the ones on the developer portal. In particular, the Python example is used a lot and very well maintained.
Contact
If you have questions on anything in this tutorial, don’t hesitate to contact us.
[1]: OAuth allows for other HTTP authentication methods, like digest, to be used; Curity does not support these, but does allow authentication to be performed using the JWT grant type in addition to the client secret in a form post and basic authentication.
Let’s Stay in Touch!
Get the latest on identity management, API Security and authentication straight to your inbox.