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:
Maybe that's not ELI-5, maybe it's ELI-15. But that's about as simple as I think I can make it.
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:
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.
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:
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.
If anyone has questions or comments, bring em on!
Also, here's the git repo with all the code + configuration used in that screencast.
Great Article & Detailed video, Thank you @Dino
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.
I don't think you are missing anything. With a JWT issued by Google, you have Google's assertion that:
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?
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!
@Dino I think there is another option to use Edge to effectively proxy requests and responses to an existing OpenId Connect Provider (OP)
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.
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 ?
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
Yes it works in Private cloud.
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.
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.
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?
Thank You Mr. Chiesa
What if our idp is a SAML assertion provider ?