Hello @dchiesa1
I am migrating a proxy from Apigee Edge to Apigee X. This proxy includes a managed hosted resource and a Node.js server running in Apigee Edge. When other proxies forward requests to this endpoint, the Node.js module (a JavaScript file) modifies the message and uses the JWKS client to send a request to Google API OAuth v3 for retrieving the public key, which is then returned to the requesting proxies.
jwks_client.getSigningKeyAsync(kid, (err, key) => {
const signingKey = key.getPublicKey();
res.end( signingKey );
});
My proxy default.xml look like this
<HTTPProxyConnection>
<BasePath>/jwks-rsa</BasePath>
<VirtualHost>secure</VirtualHost>
<VirtualHost>secure-apigee</VirtualHost>
<VirtualHost>secure-apigee-temp</VirtualHost>
</HTTPProxyConnection>
Are there any ways to send the jwks-client request within the proxy itself?
Thanks
Kevin
Solved! Go to Solution.
Maybe?
You're asking me if it's possible to retrieve a specific key from a JWKS, within Apigee, without resorting to the nodejs logic that wraps the Google OAuth library (I think).
The JWKS endpoint for Google is here: https://www.googleapis.com/oauth2/v3/certs . If you are using Firebase Auth, there's a different JWKS endpoint.
You can use a serviceCallout to retrieve that payload. It's a JSON.
<ServiceCallout name='SC-Get-Google-JWKS'>
<Request variable='simpleGetRequest'>
<Set>
<Verb>GET</Verb>
</Set>
</Request>
<Response>jwksResponse</Response>
<HTTPTargetConnection>
<SSLInfo>
<Enabled>true</Enabled>
<IgnoreValidationErrors>false</IgnoreValidationErrors>
</SSLInfo>
<Properties>
<Property name='success.codes'>2xx</Property>
</Properties>
<URL>https://www.googleapis.com/oauth2/v3/certs</URL>
</HTTPTargetConnection>
</ServiceCallout>
You can then use Jsonpath to retrieve a specific key , by key id (kid).
<AssignMessage name='AM-Extract-JWK-via-Jsonpath'>
<AssignVariable>
<Name>json_path_1</Name>
<Template>$.keys[?(@.kid == '{request.queryparam.desiredkid}')]</Template>
</AssignVariable>
<AssignVariable>
<Name>jwk</Name>
<Value>BADDBEEF</Value>
<Template>{jsonPath(json_path_1,jwksResponse.content)}</Template>
</AssignVariable>
</AssignMessage>
Then you have a JWK. What do you want to do with that JWK? What's the real goal?
Usually you use the JWK to verify the signature on a signed JWT, or to encrypt data in an encrypted JWT. And, you have builtin Apigee policies that can do those things, and if you use them, you don't have to worry about retrieving the specific key by key-id. The policies do that for you.
What I mean is, suppose you have a signed JWT with kid=abc123. If it was signed by google, then you can just use the VerifyJWT policy, specify the PublicKey source as that URL ^^ I cited above, and the policy will examine the JWT, find the kid, retrieve the JWKS, extract the key with that kid, then verify the signature on the JWT. It does all that for you, and you don't need to worry about the mechanics of retrieving a specific key.
Having said that, I suppose there might be cases where you'd want the specific JWK structure. Right now I am not thinking of any mainstream cases, but I suppose it's possible. And if you have such cases, then I suggest ServiceCallout + AssignMessage with a jsonpath, as I showed above.
Is there a way to convert the JSON format to PEM format inside the proxy?
Maybe this? https://github.com/DinoChiesa/Apigee-Java-Transform-Jwks
The ServiceCallout + AssignMessage method supports only one target backend service. Do you have any suggestions for enabling this method to work with more than one target backend service?
I don't really understand that. You can set dynamic URLs for servicecallout. Search on this community for hints.
Maybe?
You're asking me if it's possible to retrieve a specific key from a JWKS, within Apigee, without resorting to the nodejs logic that wraps the Google OAuth library (I think).
The JWKS endpoint for Google is here: https://www.googleapis.com/oauth2/v3/certs . If you are using Firebase Auth, there's a different JWKS endpoint.
You can use a serviceCallout to retrieve that payload. It's a JSON.
<ServiceCallout name='SC-Get-Google-JWKS'>
<Request variable='simpleGetRequest'>
<Set>
<Verb>GET</Verb>
</Set>
</Request>
<Response>jwksResponse</Response>
<HTTPTargetConnection>
<SSLInfo>
<Enabled>true</Enabled>
<IgnoreValidationErrors>false</IgnoreValidationErrors>
</SSLInfo>
<Properties>
<Property name='success.codes'>2xx</Property>
</Properties>
<URL>https://www.googleapis.com/oauth2/v3/certs</URL>
</HTTPTargetConnection>
</ServiceCallout>
You can then use Jsonpath to retrieve a specific key , by key id (kid).
<AssignMessage name='AM-Extract-JWK-via-Jsonpath'>
<AssignVariable>
<Name>json_path_1</Name>
<Template>$.keys[?(@.kid == '{request.queryparam.desiredkid}')]</Template>
</AssignVariable>
<AssignVariable>
<Name>jwk</Name>
<Value>BADDBEEF</Value>
<Template>{jsonPath(json_path_1,jwksResponse.content)}</Template>
</AssignVariable>
</AssignMessage>
Then you have a JWK. What do you want to do with that JWK? What's the real goal?
Usually you use the JWK to verify the signature on a signed JWT, or to encrypt data in an encrypted JWT. And, you have builtin Apigee policies that can do those things, and if you use them, you don't have to worry about retrieving the specific key by key-id. The policies do that for you.
What I mean is, suppose you have a signed JWT with kid=abc123. If it was signed by google, then you can just use the VerifyJWT policy, specify the PublicKey source as that URL ^^ I cited above, and the policy will examine the JWT, find the kid, retrieve the JWKS, extract the key with that kid, then verify the signature on the JWT. It does all that for you, and you don't need to worry about the mechanics of retrieving a specific key.
Having said that, I suppose there might be cases where you'd want the specific JWK structure. Right now I am not thinking of any mainstream cases, but I suppose it's possible. And if you have such cases, then I suggest ServiceCallout + AssignMessage with a jsonpath, as I showed above.
Thanks @dchiesa1 for the insightful information.
We fetch the JWK and store it in the KVM for caching purposes. This serves as a backup under two conditions:
Additionally, we aggregate another JWKS provider using a Node.js module for fetching, alongside the Google JWKS. The Node.js module acts as an aggregator based on input variables.
However, this solution addresses only conditions 1 and 2, but not the aggregation.
We have two additional requirements:
The request proxy requires the public key in PEM format instead of JSON. Is there a way to convert the JSON format to PEM format inside the proxy?
The ServiceCallout + AssignMessage method supports only one target backend service. Do you have any suggestions for enabling this method to work with more than one target backend service?
Thanks so much for the information.
Kevin-
Is there a way to convert the JSON format to PEM format inside the proxy?
Maybe this? https://github.com/DinoChiesa/Apigee-Java-Transform-Jwks
The ServiceCallout + AssignMessage method supports only one target backend service. Do you have any suggestions for enabling this method to work with more than one target backend service?
I don't really understand that. You can set dynamic URLs for servicecallout. Search on this community for hints.
Thanks @dchiesa1
We will give it a try on this.