Sascha Preibisch

Federation with OpenID Connect

Blog Post created by Sascha Preibisch Employee on Feb 11, 2017

Federation with OpenID Connect

I have been asked to write a blog post about federation in the context of OpenID Connect. Before I continue I would like to mention that a draft for OpenID Connect Federation exists. To see details please visit this website.

In this blog post I am not referencing that draft but I want to explain a few components that exist in OpenID Connect that can be used in any case.

 

Summary

At the end of this blog post you will know how to leverage OpenID Connect Discovery, Json Web Key (JWK) and Json Web Token (JWT). The bottom of this blog post shows a screenshot of my implementation. It starts where the authorization_code gets exchanged for an access_token.

 

The complete message flow this blog post is based on follows the OAuth authorization_code flow (RFC 6749). I am assuming that you understand that flow. This blog post should help you understand how to use an id_token.

 

Important APIs

For this blog post we are referencing two API's found in the context of OpenID Connect:

  1. /.well-known/openid-configuration (Specification for Discovery)
  2. /jwks_uri (referenced in Discovery)

Those API's (and with that the specifications) enable anyone to configure oauth clients and validate JWT. The JWT in this case contains an OpenID Connect id_token and is digitally signed using RS256 as algorithm.

 

On a side note:

JWT and id_token are often use synonymously. Even in this blog post you will find that. Please remember:

  • id_token: a JSON message with a well defined structure based on OpenID Connect
  • JWT: a base64url encoded string containing a jwt-header, jwt-payload, jwt-signature
  • in the context of OpenID Connect: jwt-payload --> base64url encoded id_token!

How these APIs relate to each other

Here a brief explanation of these API's:

  • /.well-known/openid-configuration
    • Idea: a list of details that are supported by an OAuth/ OpenID Connect provider
    • Content: "location of authorization endpoint", "location of token endpoint", "supported SCOPEs and claims", "algorithms for JWT signatures", information like that
    • In this blog post we will act as a client of Microsoft (MS) and will leverage their discovery API
    • Details: Discovery - details
  • /jwks.json:
    • Idea: a list of public keys (certificates) that can be used to validate JWT
    • In this blog post we will act as a client of Microsoft and will leverage their jwks API
    • Example: Microsoft's jwks_uri

 

Here is an image visualizing the connection between different APIs:

Overview of important OpenID Connect API's

Here is the cool part of the API relationship:

  • as a provider you only have to publish the Discovery endpoint (/.well-known/openid-configuration). All other API's are  referenced in its JSON response
  • as a client you only need to know the location of the discovery endpoint and you'll get all others
  • the image also shows the /register endpoint which allows clients to register themselves as an oauth client. We are not using that in this post
  • /authorize and /token are the usual oauth endpoints
  • /jwks.json contains all keys (certificates) required to validate JWT

 

Leveraging MS Office 365 accounts

As an example I am now explaining how I configured the OTK (OAuth Toolkit) authorization server to accept Office 365 id_token to log user in to OTK.

 

NOTE: I have got a new website at which you can see this example working when you select "Sign in with Microsoft": oauth.blog/register. You can try it and remove your registration afterwards if you wish. 

 

The complete story looks like this:

  1. register yourself as a developer and create an oauth application. Important: register the correct redirect_uri at which you want to receive an authorization_code!
  2. build an application that uses the oauth credentials received from Microsoft for your app
  3. implement the redirect_uri. Handle success cases but also error cases
  4. implement the validation of the JWT
  5. implement the validation of the id_token
  6. extract any value of the id_token that you want to use as the username, i.e.: "name" or "email"

 

It looks like a lot to do ... and it is .. but depending on your oauth/oidc provider it may be as simple as peeing a hole into the snow or as complicated as restoring a 1973 Corvette. In my case it was not complicated.

 

Register an oauth client with Microsoft

I registered myself as a developer with Microsoft and created an oauth application named "Saschas Authorization Server". With that I received oauth client credentials. You can register yourself here: register

On my authorization server's login page I have included a logo which, when clicked, initiated the social login flow with Microsoft. At the end of this flow I received an authorization_code at my redirect_uri.

 

Implement the redirect_uri

At this API the authorization_code will be received and exchanged for an access_token (actually, in the case of MS, you will receive an id_token packaged as JWT instead of an access_token). The response looks something like this (shortened for readability):

 

{

 "token_type":"Bearer",

 "id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjFMVE16YWtpaGlSbGFfOHoyQkVKVlhlV01xbyJ9.eyJ2ZXIiOiIyLjAiLC...DRiNjZkYWQifQ.y28AK...uv_vrKlLow"

}

In general, a JWT has this format:

jwt-header [.] jwt-payload [.] jwt-signature

 

The following steps are all implemented within my redirect_uri!

 

Prepare jwks

Microsoft publishes its discovery endpoint here: MS Discovery. If you follow that link you will see that all APIs that we need are referenced in the JSON response. In this case we are interested in "jwks_uri". The response message of that API looks like this:

{
 "keys": [{
 "kty": "RSA",
 "use": "sig",
 "kid": "Y4ueK2oaINQiQb5YEBSYVyDcpAU",
"x5t": "Y4ueK2oaINQiQb5YEBSYVyDcpAU",
 "n": "p3pKrlon...CroPTYQ",
 "e": "AQAB",
"x5c": ["MIIDBTCC....../8IHkxt"],
 "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0"
}, ...

It is an array of "keys". You may want to study all details, but what we need is this:

  • kid:
    • when we validate the JWT, we will find a matching kid in the JWT header. The kid tells us which certificate we have to use to validate it

 

Validate JWT

In OTK we can use the "Decode Json Web Token" assertion to validate the JWT. Other products surely have similar tools available. We just have to do 2 manual steps before we can use that assertion:

 

  1. base64 decode the JWT header (the string in front of the first [.] of the JWT). It looks like this:  {"typ":"JWT","alg":"RS256","kid":"1LTMzakihiRla_8z2BEJVXeWMqo"}
  2. extract the kid using a JSON Path assertion

 

As you remember, the kid references the certificate that can be used to validate the signature. We can now use that value with our "Decode Json Web Token" assertion like this:

 

Configuration of Decode Json Web Token

  • Source Payload: the JWT that we have received
  • Validation Method: tell the assertion to use a context variable when searching for "secrets"
  • Recipient Key Context Variable: contains the content of the JWKS file that I earlier imported after downloading it from Microsoft
  • Key Type: a JSON Web Key Set. As I said, its an array of keys
  • Key ID: here it comes! We need to tell the assertion which certificate to use, identified by the kid
  • Destination Variable Prefix: the payload of the JWT, which is the id_token. That part is the one we are actually interested in!

 

If the signature validation is successful we know that this token was issued by Microsoft!  Now the content of the id_token needs to be validated!

 

Validate the id_token

The content of the id_token looks something like this:

{
"ver": "2.0",
"iss": "https://login.microsoftonline.com/9188....b66dad/v2.0",
"aud": "2a817ae....9aa4b61",
"exp": 1485205244,
"iat": 1485118844,
"name": "Sascha Preibisch",
"preferred_username": "sascha.preibisch@ca.com",
"email": "sascha.preibisch@ca.com",
"sub": "AAAAA....Geg5k",
"tid": "9188....b66dad"
}

  • iss: the issuer. It contains a tenantid. You can choose to care about a specific tenantid (as I did) or simply accept anyone. In the case you care you MUST verify that it includes your acceptable tenant. The concept of a tenantid is specific to MS. In google's case you could simply check if the issuer is accounts.google.com (or https://accounts.google.com, one or the other may appear)
  • aud: my client_id. You MUST verify that it matches the one you used when requesting the token!
  • exp: the expiration. You MUST verify that it has not yet expired
  • name: the user's name information
  • preferred_username: I modified it for this example, but it will match the username that you are using in your corporation
  • email: the email address
  • sub: a value identifying the user within the system of MS (or maybe the tenant, not sure ...)
  • tid: the tenantid which matches the value in the iss value

 

Except for extracting details of the id_token this is all you have to do. You have accepted and validated an id_token, issued by a third party. Your user is logged in!

 

To show you how few lines of policy you need in CA API Gateway to implement this I have added a screenshot below. It starts at "authorization_code received, exchange it for a token" (expand the image by clicking it):

 

API to validate a JWT and id_token

  • line 52: call the /token endpoint to receive an access_token in exchange for an auhorization_code
  • line 55, 56: MS does not issue an access_token but an id_token (which comes in handy in our case). On line 55 we are extracting the id_token from the JSON response that was received. On line 56 we are setting a variable with the content naming it "jwt"
  • lines 57 - 60: match the JWT header, base64 decode it, extract the kid.
  • line 62: validate the JWT. That assertion is configured as shown further up
  • line 63 - 68: extracting values from the id_token
  • line 69 - 71: validating claims such as "exp", "aud" and "iss"
  • line 72 - ... : extracting details such as "sub", "name" and "email" which are then used as username and user-id

 

Do not forget

Even if you forget most of this blog post, keep the most important steps in mind:

  • receive and id_token
  • validate the signature
  • validate claims such as "exp"
  • be happy about this easy and convenient way of logging in users with third-party credentials

 

That's it!

 

I hope this blog post gives you an idea how id_token (JWT) can be used for federation.

Please leave a comment if you need further details or if you find an error or if you like what I described here.

 

Thanks a lot!

Outcomes