Using Apigee Edge with OpenID Connect

Hey everybody, here's a quick article on using Apigee Edge with OpenID Connect - either as a consumer of tokens or as a provider. What I mean by "consumer" - a system that validates tokens issued via OpenID Connect. What I mean by "provider" is, a system that issues tokens and codes according to the OpenID Connect specification.

OpenID Connect is a standard that layers authentication on top of OAuth 2.0. The whole constellation of standards and specs can get overwhelming, I know. So here's how I think about it, in ELI-5 terms:

  • OAuth 2.0 describes how to acquire tokens, and how to use tokens with API servers. There are a number of different "grant_types" that would apply to different app scenarios. A headless server app that sends API requests to another server app, would get a token via a different kind of request, than would a web app that issues API calls on behalf of an authenticated user. There are other scenarios too.
  • OpenID Connect specifies how to extend that basic model to add in user authentication and issuing secure (signed) tokens (JWT) that contain user claims. What's a claim? A claim is just a fact about a person, like "height" or "role" or "email address". This is handy because the JWT can be verified by parties other than the issuing party, without contacting the verifier issuer. This is done via public/private signature verification or shared-key signature verification.

Maybe that's not ELI-5, maybe it's ELI-15. But that's about as simple as I think I can make it.

Existing OpenID Connect Providers

Which systems are OpenID Connect providers? Basically, any authentication service that will be used within apps that send secure (authenticated) API requests, ought to be OpenID Connect compliant. And, no surprise, systems that manage user registries, like Azure Active Directory, or Google Sign-in, or Salesforce (!!), or Okta, or Paypal... all act as OpenID Connect providers. I'm sure there are more that I'm not listing here.

If you are using apps that authenticate their users via any of these providers, or in fact any provider that is compliant with OpenID Connect, using the issued token within Apigee Edge is EASY. E A S Y. In each case, it works like this:

  • the mobile app registers at the OpenID Connect provider, gets an application (client) ID.
  • the mobile or web app initiates a sign-in to the designated user directory
  • the token issuer (Azure AD, Google, etc) authenticates the user and client, and generates a JWT, and sends it back to the requesting app
  • the requesting app presents that token in an API call that gets proxied by Apigee Edge
  • Apigee Edge parses the token, verifies the signature, and makes an authorization decision based on the signed claims in the token.
  • Apigee Edge can optionally issue a smaller opaque oauth token in an exchange with the app.
  • Apigee Edge can also optionally verify the client ID, if you've imported the external client ID into Edge.

All of this works really easily, and there's no magic or exotic stuff here. You're just building a basic API Proxy. You can use this Java callout to verify the in-bound, externally-generated JWT.

By the way, this ALSO works if your OpenID Connect provider is internal to your enterprise. I think there is a product from Ping Identity that can act as an OpenID Connect provider. Using tokens generated by Ping would work exactly as I described above.

Creating an OpenID Connect Provider on Apigee Edge

In some cases, companies want to create an OpenID Connect provider themselves. This would be the case when the company itself "owns" the user registry and does not wish to delegate that responsibility to Microsoft Azure, or Google, or Paypal, etc. I've seen this quite a lot in Healthcare companies, in which security and privacy is paramount, and also Telcos, which have huge user bases. But really, any organization that owns the user identity, and wants to allow third party apps to run against its APIs, would probably be interested in this capability. For example, a University that has a student registry, and might want to allow third-party apps to access its APIs, on behalf of students, would definitely want to offer an OpenID Connect provider for all of those apps.

The good news is: there's an easy way to do this in Apigee Edge as well. In this case, Apigee Edge itself is issuing the tokens, and delegates only the user authentication and consent management to an external app. If you want to produce an OpenID Connect provider on Edge for your organization, then you need to write and provide that user-authentication and consent-gathering experience , which layers on an IdP, like an LDAP database, maybe a local, on-premises Active Directory. This is a small web app, and we have samples and sequence diagrams showing what is necessary.

Everything else, all the back-and-forth prescribed by the OAuth2.0 specs and the OpenID Connect specs, is handled by Apigee Edge.

The flow works as I described above, except that Apigee Edge is the token provider. Therefore:

  • the mobile or web app registers at Apigee Edge, via the developer portal, and gets an application (client) ID.
  • the mobile or web app initiates a sign-in to Apigee Edge at the designated authorization endpoint
  • Edge authenticates the client (via the client ID), and then 302-redirects the user to the registered login-and-consent app, to allow user authentication.
  • The login-and-consent app authenticates the user, and then calls to Edge to ask for a JWT or code or token, and then sends it back to the requesting app
  • the requesting app can either: presents that ID token in an API call that gets proxied by Apigee Edge, or just USE the ID token for its own purposes, and present the (opaque) access token back to Apigee Edge with API calls.
  • In either case, Apigee Edge parses the token, verifies it, and makes an authorization decision based on the metadata associated to the token.

This relies on the same Java callout as referenced earlier, only now Edge uses it to both generate and validate JWT.

Doing this is a little more complicated that the "Verify JWT" scenario above, because there are more things you need to do, as a company or organization that wants to act as an OpenID Connect provider. But this might eliminate the need for you to purchase a dedicated OpenID Connect provider for your workplace or organization. You're already going to use Apigee Edge for API management; you can stand up an OpenID Connect provider using the thing you already own.

Here's the screencast that elaborates on this second scenario. It's about 10 minutes long. Or you can Right click HERE to open the screencast in a new browser tab.

6116-screenshot-20171211-174124.png

If anyone has questions or comments, bring em on!

Also, here's the git repo with all the code + configuration used in that screencast.

Comments
anilsr
Staff

Great Article & Detailed video, Thank you @Dino

banto_78
New Member

Just because we know - by verifiying - the signature of an IdP like Google is valid we should trust an user? I mean anyone can be registered in Google. What am i missing?

thanks lot.

DChiesa
Staff

I don't think you are missing anything. With a JWT issued by Google, you have Google's assertion that:

  • the user is authentic (they've logged in, with password and maybe with a 2nd factor)
  • the user's information, like email, firstname, lastname... is accurate
  • The "audience" - the intended consumer of the JWT - is bonafide

If you use Google IdP, there may be a need for you collect and manage additional data about the user, on your own systems. For example, some sort of mapping between username and the set of authorized actions by that user.

But, this example isn't specific to Google Sign-in. It applies to any IdP that can participate in the OpenID Connect protocol. and specifically what I showed is that you can build an OpenID connect signin, using Apigee Edge, backed by any user database at all. Including (probably) the existing user database you already have. So you yourself can become an OpenID Connect provider, and all of your apps can rely on that authentication.

Does this make sense?

kurtkanaskie
Staff

Hey @Dino this is great!

I checked out the repo and I think I found some issues when using the code flow and the POST /token endpoint with response type of "code". Or maybe I'm missing something.

When I get the code and run the curl command, I noticed the last 3 policies are not executing, because there is a condition.

<Condition>(token_scope ~~ ".*\bid_token\b.*")</Condition> 

Token scope can be set to "openid" (required) and optionally "profile" and "email".

If I change the condition to:

<Condition>(token_scope ~~ ".*\bprofile\b.*")</Condition>

the policies execute (when profile is in scope), set vars, create JWT and assigns it to the response.

Then the response looks like:

{
  "access_token": "XdyDQ655V1Go5aGSGwhQO7dq9KoG",
  "token_type": "Bearer",
  "refresh_token": "Dq8dHpVzGg4Zsz5zUOG5Uo7KAMKMQq8y",
  "expires_in": 1799,
  "id_token": "eyJhbGciOiJIUzI1NiJ9.eyJpc3..."
} 

I also tweaked the AM-Token-Response to use:

"expires_in": $jwt_expires_in%,
 "id_token": "$jwt_jwt%" 

This is a quick work around to get the id_token in the response in your demo.

In a real solution, the token endpoint would always return an access_token and an id_token in the code flow.

Thanks again!

kurtkanaskie
Staff

@Dino I think there is another option to use Edge to effectively proxy requests and responses to an existing OpenId Connect Provider (OP)

  1. The App registers with Edge and has its own callback.
  2. Edge registers with the OP and has its own callback.

The endpoints would be:

App --> Edge: GET /authorize - validates API key, caches session info and redirects to OP login page using a callback to the proxy in Edge

OP --> Edge: GET /callback - receives the callback from the OP with a JWT code, looks up session info and then redirects to the App callback with the OP provided JWT code.

App --> Edge: POST /token - uses the JWT code from the App callback and requests a JWT token from the OP and then returns that to the App.

Using Edge in this way effectively hides the details the OP and allows the API developers to switch backends.

In this flow, there is no OAuth done by Apigee, each resource proxy needs to validate the JWT provided in the header using a Java callout. However, OAuth policies could be used to create an Edge token with an attribute to hold the JWT.

shawkyfoda
Silver 1
Silver 1

Thank You for this very valuable demo,This demo is for the apigee cloud is not it ?, can we apply it for the private cloud? what is the main changes I have to apply? especially the scripts used to generate the proxies and the cache

or should I build it in cloud then bundle it down to my private cloud ?

shawkyfoda
Silver 1
Silver 1

Why This js code in the ?

application_uri += ((response_type_id_token=="true") || (response_type_token=="true")) ? "#" : '?'

and how I could parse the request with the # sign in the beginning of the querystring instead of ?

actually if a request with # is sent to a proxy, the proxy will ignore what is after the # sign .. correct me if im wrong.

I'm trying to verify an jwt token returned from google directly by pointing the callback uri to an apigee proxy. it works fine if I replaced the # with ?

Please advise

dchiesa1
Staff

Yes it works in Private cloud.

dchiesa1
Staff

The ? in a URL precedes query params. The # indicates a URL Fragment . In a callback URL, they are used for different response types. OpenID Connect uses them for different client types.

how I could parse the request with the # sign in the beginning of the querystring instead of ?

I don't know what you mean. I can try to help, if you can describe the problem you are trying to solve, more specifically. "how could I parse" = where? What do you mean by parse? The code that you want to use to parse - where do you expect it will be running?

actually if a request with # is sent to a proxy, the proxy will ignore what is after the # sign .. correct me if I'm wrong.

The # is a url fragment. It is intended to be interpreted only by the user agent. a URL containing # should never be sent to a server. If a server (like an API proxy running in Apigee Edge) receives a URL with a #, the server is supposed to ignore the # and everything following it. You can read about this in the URL RFC.

I'm trying to verify an jwt token returned from google directly by pointing the callback uri to an apigee proxy.

I don't understand. If you have new questions you should ASK A NEW QUESTION, and I'll try to help.

But you will need to be more clear about what you are trying to do. "pointing the callback URL" ... doesn't make sense to me. So please add more details when you ASK A NEW QUESTION.

ericmery
New Member

Hi,

Would it be possible that Apigee Edge request another system to gather the claims when the RP requests the UserInfo Endpoint ? So the user identity will not be saved in the Apigee System but in an external System (for privacy purposes). So Apigee will have to save only the id of the user (email or phone number).

Thanks.
Eric.

dchiesa1
Staff

I think so, but I don't know what you mean by "save". Apigee Edge doesn't save the claims, I don't think. Apigee Edge inserts claims into the JWT.

But anyway this seems like a bigger question - maybe you can post a new question for that, eh?

shawkyfoda
Silver 1
Silver 1

Thank You Mr. Chiesa 

What if our idp is a SAML assertion provider ? 

Version history
Last update:
‎07-28-2016 11:05 AM
Updated by: