SAML2 vs JWT: Understanding OAuth2

This blog post continues the SAML2 vs JWT series. In the last post, we discussed JSON Web Tokens. Now, we are going to move on to OAuth2 and OpenID Connect, which provides some structure and protocol around the use of JWT. These protocols are used, along with JWT, to build the JWT use cases this series covers. We will start with OAuth2. We got a glimpse of OAuth2 calls in the Apigee and Azure Active Directory Integration — A JWT Story post. There we saw examples of the Authorization Code Grant and the Resource Owner Password Credentials Grant in practice. This post will look at those grants and the others in more detail.

What is OAuth 2.0?

The OAuth 2.0 specification defines an authorization protocol (a protocol that is focused on what actors have access to — not who the actor is). This is specifically geared towards allowing a resource owner (user, most likely) to grant third-party applications and websites access to resources (which could be just about anything). Think of use cases such as a mobile app that can post pictures to Facebook where you authorize the app access to Facebook once and, from that point onward you can simply post pictures from the app to Facebook. As an authorization protocol, the details of how authentications works are largely undefined; in fact, in some cases the details of how the credentials are passed from the Client or User to Identity Provider aren’t specifically defined (that leaves a lot of room for things to get a little weird or, at least, custom). The OAuth2 spec defines four actors: Resource Owner (User, most likely), Client, Authorization Server, and Resource Server (API, website, etc). Obviously the User, Client and the Resource Server should trust the Authorization Server.

Resource Owner: any entity that is capable of granting access to a Resource; this could be a person (referred to as the end-user in the spec) or a variety of other things — this is essentially the Principal (thing that can be authenticated) from our earlier discussions.

Client: application that wants access to the Resource; this could be a Mobile App, Single Page Application (SPA), traditional server-side Web Application component, desktop application or a couple of other things.

Authorization Server: the Identity Provider (this should be a familiar concept at this point) — if not, start at the beginning.

Resource Server: contains the Resource that the Client wants to access (again, an API, web server, etc).

OAuth 2.0 is composed of the following specifications (from here):

OAuth 2.0 Core

OAuth 2.0 Extensions

There is also an OAuth2 Token Exchange spec that is of particular interest — we will revisit this spec during a future delegation and impersonation post.

The Authorization Grants

Authorization Grants are the protocol mechanism the OAuth2 and extension specs define for the actors listed above to interact with one another. This section describes each one.

Authorization Code Grant (Section 4.1 of the OAuth2 Spec): This is the big one most people think of that involves end-users and authorizing third-party applications to access that end-user’s resources without exposing the end-user’s credentials to the third-party application. This is generally referred to as three-legged OAuth. Although, there is no backwards compatibility between OAuth 1.0 and OAuth2, three-legged OAuth is the theme that ties the two together. The three “legs” refer to (1) the Resource Owner (User), (2) the Client, and (3) the Resource Server. This is an example of an interactive login — meaning that there must be an end user that can move through the login workflow (authentication steps).

The end user initiates the flow by clicking on a login link, button, or similar action. The application redirects the User Agent to the IdP (authorization endpoint) — step (A) in the diagram below. The IdP redirects the user to the authentication workflow (most likely a series of screens) that is not defined in the scope of the spec — step (B). If authentication of the end-user is successful and the user grants access to the requested resource, the IdP returns an Authorization Code and redirects the user to the Client application — step (C). The Client then exchanges this Authorization Code for an access token — steps (D) & (E). From there, the access token can be used to access the desired Resource(s) on the Resource Server — step (F). If the access token is being used for repeated API access that doesn’t have any other type of session management (like a session cookie), then it can be used until it expires (this assumes no one-time use or similar policy on the token). When the access token expires, a refresh token (which would be cached on the Client) can be used to obtain a new access token. This grant is geared towards confidential clients (server-side application components or something else that can protect the client secret); although, it can be used with public clients such as SPA Web Apps or Mobile Apps. The following illustration adapted from the OAuth2 spec shows this grant.

The Authorization Code Grant consists of the following calls (these examples came from the OAuth2 spec):

GET /oauth2/authorize?
  response_type=code&
  client_id=s6BhdRkqt3&
  state=xyz&
  redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 
 HTTP/1.1
 Host: idp.example.com

Outside of the OAuth2 spec, the authorization endpoint will redirect the user to some form of login workflow. This could be on the same identity provider (Authorization Server) or could be a different one that has a federation relationship with this Authorization Server. That federation relationship between our Authorization Server and the third-party IdP could be based upon SAML Browser Profile (SAML-P), OpenID Connect, WS-Federation, or other protocols.

Eventually, the user agent will be passed an authorization code. That authorization code will be passed to the Client via a browser redirect to the redirect_uri endpoint. This response from the authorization endpoint will look something like (again, example call from the spec):

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

The Client, will use the authorization code in the following call to the token endpoint (once more, example from the spec)

POST /token HTTP/1.1
 Host: idp.example.com
 Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
 Content-Type: application/x-www-form-urlencoded
 grant_type=authorization_code&
 code=SplxlOBeZQQYbYS6WxSbIA
 &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

The response from the token endpoint will look something like (example from the spec):

HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache

     {
       "access_token":"Jkdkdkld984dpslcmvjuf...",
       "token_type":"bearer",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
     }

An interesting note on how this scenario can differ in practice. If the Authorization Code Grant is being used by a mobile app (with say AAD and the ADAL library, which would be a Public Client), then ADAL library will trigger the last call to the token endpoint and from the perspective of the Authorization Server, all of this traffic is coming from the same place (the mobile device). If the grant is being used by a traditional web application, the final call to the token endpoint will be made by the server component (which would be a Confidential Client), not the user agent (browser). Thus, the authorization endpoint call and the token endpoint calls will appear to come from different places.

The steps described above are outlined in the following sequence diagram. Special thanks to Karen Larson for assisting with the sequence diagrams.

Implicit Grant (Section 4.2 of the Spec): This grant is very similar to the Authorization Code Grant except that instead of an Authorization Code being returned to the Client, the access token is returned directly to the Client following the end-user authentication (and authorization to the Resource). This grant exists for scenarios where the Client cannot keep its credentials secret such as with a SPA Web Application or Mobile app. There is an overlap in the supported scenarios between this grant and the Authorization Code Grant — we will look more at that below. This is also an interactive login.

In steps (D) — (F) things get a bit funky to obtain a web page that contains a script (probably Javascript) that can be used to parse out the access token. In all likelihood, this script will already be loaded in the user agent that is running the SPA application or running on behalf of the mobile application. Refresh tokens are not used with this Grant. We have this diagram adapted from the OAuth2 spec:

The call the Client makes to the authorization endpoint will look something like (example from the spec):

GET /oauth2/authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
 &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
 Host: idp.example.com

This will initially result in a redirect to some type of login workflow just like with the Authorization Code Grant. Upon successful completion of this authentication step, the response will look something like (example from the spec):

HTTP/1.1 302 Found
     Location: http://example.com/cb#
  access_token=2YotnFZFEjr1zCsicMWpAA&
  state=xyz&token_type=example&expires_in=3600

You’ll notice that the access_token and other details are passed to the Client in a URI Fragment in an HTTP redirect— not as part of a response message body or query parameters.

The following sequence diagram describes the Implicit Grant steps that were described above.

Resource Owner Password Credentials Grant (Section 4.3 of the Spec): This grant involves the Client application asking for the username and password directly from the end-user rather than directing the user to a login page hosted by the Authorization Server (or other Identity Provider) like in the first two Grants — step (A). The Client then passes the client credentials (client identifier + client secret in the case of Confidential Client or just client identifier in the case of a Public Client) and end-user credentials to the Authorization Server — step (B). If authentication is successful, then an access token will be returned — a refresh token can also be used — step (C). Finally, the access token can be used to access the Resource Server — step (D). This is a non-interactive login, which makes it useful for things like batch jobs or containers that use a generic or system account for authenticating against the Identity Provider. From the OAuth2 spec, we have this diagram:

Compared to the first two Authorization Grants, this grant is a simple, single-call to the token endpoint that looks similar to the following (example from the spec):

POST /oauth2/token HTTP/1.1
 Host: idp.example.com
 Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
 Content-Type: application/x-www-form-urlencoded
 grant_type=password&
 username=johndoe&
 password=A3ddj3w

The response will look more-or-less like the response from the Authorization Code Grant above.

The following sequence diagram further captures the steps described above.

Client Credentials Grant (Section 4.4 of the Spec): This Grant does not authenticate an end-user, it just authenticates the Client; similar to the Resource Owner Password Grant, it is not an interactive login. It can only be used by a confidential Client. This is what is known as two-legged OAuth. If validation of the client credentials is successful, then an access token is returned that represents the Client. This is a simple, yet effective, way of managing the authentication step when the authorization decision only depends upon the calling application and not the end user. The following diagram was adapted from the OAuth2 spec:

The request for a client credentials grant call to the token endpoint will look like (example from the spec):

POST /oauth2/token HTTP/1.1
 Host: idp.example.com
 Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
 Content-Type: application/x-www-form-urlencoded
 grant_type=client_credentials

The response will also look like the response from the Authorization Code Grant above, but in this case the access_token describes the Client (an application) rather than the end user.

The following sequence diagram captures the steps described above.

Refresh Tokens (Section 1.5 of the spec):The OAuth2 Core spec also describes how to obtain a new access token as part of the same authenticated session with the IdP using the refresh token when the original has expired (for Authorization Grants that support Refresh Tokens). The refresh token can be given to the token endpoint to obtain the new access token (as described here).

OAuth2 Extension Grants

The OAuth2 Core Spec defines a mechanism for defining Extension (additional) Grants that can be used within the OAuth2 Framework. The Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grant specification builds on this to provide “a framework for the use of assertions with OAuth 2.0 in the form of a new client authentication mechanism and a new authorization grant type. Mechanisms are specified for transporting assertions during interactions with a token endpoint; general processing rules are also specified.” The Assertion Framework for OAuth 2.0 provides a “common framework for OAuth 2.0 to interwork with other identity systems using assertions and to provide alternative client authentication mechanisms.” The use of the word Assertions here basically means Bearer Tokens (JWT, SAML v2.0 Bearer Tokens, etc). At the time of this writing, Azure Active Directory supports the JWT Profile for Authorization Grants for On-Behalf-Of functionality similar to that described in the OAuth2 Token Exchange spec using JWTs that were issued by AAD. Azure Active Directory also supports the SAML 2.0 Bearer Assertion Profile for Authorization Grants for On-Behalf-Of functionality similar to that described in the OAuth2 Token Exchange Spec, but with SAML2 it can take input tokens that are issued by remote Identity Providers — this is what was used in the “API Management and Security for COTS Applications” blog post.

SAML 2.0 Bearer Assertion Profiles (Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants spec): This specification defines how to use SAML2 Bearer Tokens as the authentication mechanism for requesting an OAuth2 access token or for client authentication. When being used for requesting an access token, the SAML Assertion most likely describes an end user that was authenticated with another Identity Provider that supports SAML2 Bearer Tokens. This represents yet another use of SAML 2.0, but we didn’t explore it in the first part of this series. The OAuth2 call to the token endpoint for the Authorization Grant would look something like (example from the spec):

POST /oauth2/token HTTP/1.1
 Host: idp.example.com
 Content-Type: application/x-www-form-urlencoded grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Asaml2-bearer&
assertion=PHNhbWxwOl…[omitted for brevity]…ZT4&
client_id =blahblah1234&
resource=https://some.endpoint.com/

From a practical standpoint, every IdP I am familiar with would require the client_id parameter. Most IdPs would likely require a Confidential Client that would have a client secret that would also be included in this request (not shown). The resource parameter would be needed to tell the IdP what audience should be used with the resulting access token (which would be a JWT for our purposes). The scope parameter can also be used to request a specific audience. The way that resource and audience are used in these request will most likely be IdP-implementation dependent.

If you will recall from our earlier API Management and Perimeter Security for COTS Applications, we had the system depicted in the next diagram depicting an out-of-the-box mobile application and backend API with a custom perimeter security layer added to meet the needs of a large enterprise organization.

Another thing to remember when using Azure Active Directory in this scenario is that the token presented to AAD should have its audience set to the AAD itself. AAD will reject a token that is presented to it that has a different audience, even if the token is a valid one.

If we add a little bit of technical detail to this same picture, we get the following diagram. Notice the API Gateway box makes a WS-Trust call to the Active Directory Federation Server box. The resulting SAML2 Bearer Token (with the audience set to the Azure AD value) is then placed into an OAuth2 call to the Azure Active Directory endpoint that looks more-or-less the same as the example call given above. Note, that the Azure AD trusts the ADFS server in this scenario.

JWT Profile for Authorization Grants (JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants spec): This spec defines how to use JWT tokens as the authentication mechanism for requesting an OAuth2 access token or for client authentication. When being used for requesting an access token, the JWT most likely describes an end user that was authenticated with another Identity Provider (that supports JWT) — this should sound very familiar to the description of using SAML 2.0 above. The call to the token endpoint will look something like (from the spec):

POST /oauth2/token HTTP/1.1
 Host: idp.example.com
 Content-Type: application/x-www-form-urlencoded
 grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&
 assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0.
 eyJpc3Mi[…omitted for brevity…].
 J9l-ZhwP[…omitted for brevity…]&
 client_id=blahblah1234&
 resource=https://some.endpoint.com/

The discussion about resource, client_id, and scope parameters listed above apply here as well:

Token Exchange Grant (OAuth 2.0 Token Exchange spec): This specification defines

  • how to request and obtain Security Tokens from an OAuth2 Authorization Server
  • how one party can act on behalf of another party or enabling one party to delegate authority to another party

This type of functionality is inspired by the WS-Trust specifications Act-As (impersonation) and On-Behalf-Of (delegation) functionality. This spec can work with assertions, bearer tokens, such as JWT or SAML2 Bearer Tokens. This spec references the OAuth2 Assertion Framework, but does get a bit creative. If you haven’t been in an environment where delegation and impersonation concepts from the WS-Trust spec have been used, the ideas will likely seem a bit abstract — delegation and impersonation will be the subject of a future post. For completeness, an example of an On-Behalf-Of call is given below.

The request to the token endpoint would look similar to (example from the spec):

POST /oauth2/token HTTP/1.1
 Host: idp.example.com
 Content-Type: application/x-www-form-urlencoded
 grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
 &audience=urn%3Aexample%3Acooperation-context
 &subject_token=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0.eyJhdWQiOiJ…
 &subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt
 &actor_token=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0.eyJhdWQiOiJodHRw
 &actor_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt

For comparison, this is what the OAuth2 On-Behalf-Of call for Azure Active Directory looks like:

POST https://login.microsoftonline.com/common/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: login.microsoftonline.com
Content-Length: 1000
resource=https%3A%2F%2Fgraph.windows.net&
client_id=blahblah1234&
client_secret=blahblahblahsecret1234& grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer& assertion=AbEf[…Omitted for brevity…]KdP3&
requested_token_use=on_behalf_of&
scope=openid

There are some notable differences between these two requests. It appears that Microsoft decided to implement the concept of delegation directly on top of the JWT Profile for Authorization Grants described in the last section together with an advanced delegation model based upon the Client application definitions (within AAD).

Message Authentication Code (MAC) Tokens (OAuth 2.0 Message Authentication Code (MAC) Tokens spec): This one does not use the OAuth2 Assertion Framework. This is an extension of the Authorization Code Grant that allows a MAC to be used. It “describes how to use MAC Tokens in HTTP requests to access OAuth 2.0 protected resources. An OAuth client willing to access a protected resource needs to demonstrate possession of a cryptographic key by using it with a keyed message digest function to the request.” This RFC is still in draft and there are no plans to go forward with it — we’re not going to explore it further.

JWT as the OAuth2 Access Token

Note, I jump ahead to OpenID Connect briefly in this section. We will be covering that topic next.

It is important to call out that neither the OAuth2 spec family nor the OpenID Connect spec family explicitly requires that the OAuth2 access token be a JWT. Both specs are intentionally vague about what an access_token should be (opaque token, bearer token, custom token, etc). For the purposes of what I am trying to achieve in this series of posts, there is an assumption that the OAuth2 access token is a JWT. This brings us into the closest possible alignment with how SAML2 is used with SOAP Web Services, SSO and other use cases. Now, all discussion regarding the use of bearer tokens in the Web Application Security vs API Security Series applies here. As with anything in the application security realm, there are trade offs.

An alternative implementation that is hinted at in the OpenID Connect specification is that the OAuth2 access token is an opaque token that can be passed to downstream actors (APIs, other Resource Servers, etc) and then exchanged for the user’s information in the form of a JWT from the UserInfo Endpoint. Use of this mechanism vs using the JWT as the access token is the same difference as passing the JWT (user information) by value versus passing the JWT by reference. This is analogous to using the SAML2 SAML Artifact Profile. Passing around references that must be resolved at run-time and potentially cached have their own intricacies that must be weighed — the same is true of SAML2’s SAML Artifact.

In using JWT as the OAuth2 access token, one is potentially creating a situation where sensative information could be exposed to a Client, User Agent, or end-user. This can be managed, but does require a certain level of discipline. Another drawback is that OpenID Connect and OAuth2 specs set out to separate the concepts of refresh tokens, User Information Claim Sets, and access tokens — by doing this we are undoing those last two design goals.

In the Azure Active Directory (AAD) OpenID Connect implementation, the default configuration of the id_token is a JWT with no digital signature (algorithm set to None) and the access_token is a JWT that is digitally signed.

It is also possible and perfectly legitimate to use JWT as the OAuth2 access token without OpenID Connect in the picture. In fact, at one client that I’ve been working with recently, that is exactly what we have been doing. When using AAD in this way, my observation has been that the Claim Set in the OAuth2 access token JWT mirrors what would be found in the OpenID Connect (OIDC) id_token JWT.

That ends our exploration of OAuth2. Leave comments and questions below. In the next post, we will look at OpenID Connect.

Leave a Reply

Your email address will not be published. Required fields are marked *