SAML2 vs JWT: Understanding OpenID Connect Part 2

This post continues our discussion of OpenID Connect (OIDC). We look at one of the three Authentication Flows defined by the OIDC spec — the Authorization Code Grant Flow.

OIDC Authentication Flows

Where OAuth2 defines Authorization Grants and Extension Grants, the OIDC spec defines Authentication Flows. The concepts are similar. The spec defines three Authentication Flows; these flows differ by the parameters that are passed to the OP, the contents of the responses, and how the response is processed. The differentiator between requests to the authorization endpoint (and the Authentication Flow details) is the return_type parameter. This parameter has several valid values, defined between the OAuth2 spec and OAuth2 Multiple Response Types spec:

Authorization Code Flow: code

Implicit Flow: id_token

Implicit Flow: id_token token

Hybrid Flow: code id_token

Hybrid Flow: code token

Hybrid Flow: code id_token token

So, what does all that mean? Anytime you see a value of “code” in the response_type parameter, an authorization code will be returned in the authorization endpoint response. Any time “id_token” is included in the response_type parameter, an id_token will be included in the response. Anytime “token” is included in the response_type parameter, an access_token will be included in the response from the authorization endpoint. These details impact the structure of the response, what processing steps the Relying Party (RP, Client) take next, and what use cases can be dealt with as you will see below. Note,

  • The OIDC Authorization Code Flow directly extends the OAuth2 Authorization Code Grant. The OIDC Implicit Flow and OIDC Hybrid Flow extend the OIDC Authorization Code Flow.
  • The Authorization Code response_type of code defined by OIDC is different than the response_type of the same name defined by the OAuth2 spec.

All the sample requests and responses that are used in this post are variations on the examples given in the OIDC spec.

Authorization Code Flow (OIDC v1.0 spec,Section 3.1): This is the OIDC version of the OAuth2 Authorization Code Grant. This flow has the response_type set to “code”. The example presented here utilizes all of the functionality available in the Authorization Code Grant to accomplish:

  • Authentication of end user
  • Authentication of Relaying Party (Client, application), optionally.
  • Relaying Party (RP) does not see end user’s credentials (password)
  • The User Agent does not see the Access Token or ID Token
  • SSO to RP
  • Obtain multiple Access Tokens or multi-scope Access Token.
  • Make API (Resource) call to API Provider (Resource Server) with Access Token.
Authorization Code Flow with SSO & API Call (response_type = code)

The steps in the diagram above are walked through next. The end user initiates the application sign in process by typing the web site address into a web browser or similar action — not shown in the diagram. The browser (User Agent) then sends a request to the RP that intercepts the request and sees that it is not part of an authenticated session — also, not shown in the diagram (the spec doesn’t cover this part).

The Authentication Request

The RP redirects the User Agent to the OpenID Provider (OP) — step (A). The resulting request looks something like:

GET /oidc/authorize?
 response_type=code
 &scope=openid%20profile%20email
 &client_id=s6BhdRkqt3
 &state=af0ifjsldkj
 &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1
 Host: server.example.com

The OP will redirect the User Agent to a login workflow that is served by the OP — step (B). The details of how the authentication works are outside the scope of the OIDC spec. We called OIDC an authentication protocol, but it doesn’t define the details of how the user is authenticated. This is no different than how the SAML2 Web Browser SSO Profile works — the actual authentication of the user can be a wide variety of mechanism that depend upon the identity provider (OP) capabilities. The authentication workflow could also contain a check to allow the end user to grant consent to the RP on resources owned by the end user (delegated access granted to the RP on the Resource).

Successful Authentication Response

Upon successful authentication, the OP will redirect the user back to the authorization endpoint with session tracking information set (likely in the form of a session cookie, implementation dependent). The authorization endpoint will return an HTTP Redirect (with Authorization Code) to the RP URL that looks something similar to the following — step (C):

HTTP/1.1 302 Found
 Location: https://client.example.org/cb?
 code=SplxlOBeZQQYbYS6WxSbIA
 &state=af0ifjsldkj

As a result, the User Agent sends the following request to the RP:

GET https://client.example.org/cb?
 code=SplxlOBeZQQYbYS6WxSbIA
 &state=af0ifjsldkj

Authentication Error

If an error occurs during the authentication and consent phase (or anything else leading up to the return of the authorization code), the Authorization Endpoint will return an error similar to the following:

HTTP/1.1 302 Found
  Location: https://client.example.org/cb?
    error=invalid_request
    &error_description=
      Unsupported%20response_type%20value
    &state=af0ifjsldkj

Token Request

At this point, the RP has received the authorization code without ever having seen the end user’s password (or other credentials). Now, the RP needs to obtain an Access Token, ID Token, and other information from the token endpoint. So, the RP sends something like the following request to the token endpoint — step (D):

POST /oidc/token HTTP/1.1
 Host: server.example.com
 Content-Type: application/x-www-form-urlencoded
 grant_type=authorization_code&
 code=SplxlOBeZQQYbYS6WxSbIA&
 redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&
 client_id=s6BhdRkqt3&
 client_secret=blah_blah_blah_1234

The client_secret parameter only needs to be present if the RP is a confidential client (just like with general OAuth2 Authorization Code Grant). There are alternative methods of authenticating the RP to the Token Endpoint described in Section 9 of the OIDC spec; the OAuth2 spec also supports Basic Authentication (it is the preferred method).

Token Response

The response from the token endpoint for the Authorization Code Flow looks something like the following — step (E).

HTTP/1.1 200 OK
 Content-Type: application/json
 Cache-Control: no-store
 Pragma: no-cache
 {
 “access_token”: “SlAV32hkKG”,
 “token_type”: “Bearer”,
 “refresh_token”: “8xLOxBtZp8”,
 “expires_in”: 3600,
 “id_token”: “eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc
 yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5
 NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ
 fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz
 AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q
 Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ
 NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd
 QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS
 K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4
 XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg”
 }

Validation of Tokens

Now, the ID Token (id_token property above) must be validated per Section 3.1.3.7 of the OIDC spec — step (F) — don’t skip this part, this compromises security. For this Authentication Flow, the ID Token will contain an at_hash (Access Token hash) property that can be used to validate the Access Token; this is optional, but recommended — step (G).

In OIDC scenarios, the ID Token is used for the authentication steps on the RP and the Access Token is used to

  • optionally retrieve information from the UserInfo Endpoint
  • make calls to the Resource Server (API Provider in our example)

This is very different from how OAuth2 works.

Access Token Scope

Now, if the Access Token has an audience concept (may not be the case with opaque tokens), then it is possible (probably likely, depending on the scenario), that unique Access Tokens would be needed by the RP to access the UserInfo Endpoint and access a Resource Server. If this is the case, then a second call can be made to the Token Endpoint with the same authorization code requesting a second Access Token with the Resource Server’s audience — assuming that the OP has been configured to allow such access. Another potential approach would be to utilize a scoped Access Token that supports multiple audiences — JWT allows this — but, your OP must support it and there are potential security issues in that the number of actors that can do interesting things with your identity tokens increases (thus, increasing risk).

Next, if the RP needs additional claims, it can contact the OP’s UserInfo Endpoint to obtain them as described previously — steps (H) & (I). It is interesting to note that the RP has the ID Token and it can contain any claim that could be returned by the UserInfo Endpoint. But, the OP vendors tend to put some restrictions on this feature’s flexibility.

Call To Resource Server

Next, the RP can access the Resource on the Resource Server (let’s call it an API on an API Provider) by making a call to the API with the access token in the Authorization header as we have explored before — step (J). In the Understanding OAuth2 post, it was assumed that the Access Token was a JWT token; the JWT spec defines how to validate the Access Aoken on the Resource Server (API Provider). If the Access Token is an opaque token, then the Resource Server must communicate with the OP using mechanisms defined outside of the spec (and likely implementation dependent). In October, 2015, the OAuth 2.0 Token Introspection (RFC 7662) was published that “defines a method for a protected resource to query
an OAuth 2.0 authorization server to determine the active state of an
OAuth 2.0 token and to determine meta-information about this token.” Basically, it defines an Authorization Server endpoint that can validate a token and retrieve information about it (the Token Introspection Endpoint). There is some overlap between the OIDC UserInfo Endpoint and this new endpoint, but the OAuth2 Token Introspection Endpoint provides the ability to validate an opaque Access Token (though, this isn’t its primary purpose). However the Access Token is validated, this step must be done before request processing can proceed on the Resource Server (API Provider) — Step (K).

Finally, the Resource Server can pass the Access Token it received to the UserInfo endpoint to obtain an ID Token with the relevant (and allowed) Claims for the end user — steps (L) & (M). Processing of the API request can proceed and a response can be returned to the RP (acting as the API Consumer in this scenario).

The same scenario we have just walked through is described in the sequence diagram below.

Summary

That concludes our review of the OIDC Authorization Code Grant Authentication Flow.

In the next post, we explore the last two Authentication Flows: the Implicit Flow and Hybrid Flow.

Please leave questions or comments below.