Hello Apigee Community,
I have been following the videos of @dchiesa1 regarding the verification of JWT tokens (https://www.youtube.com/watch?v=Ijm7iyDOVFY) using the id_token coming from the google auth endpoint https://accounts.google.com/o/oauth2/v2/auth.
I pass in the 'id_token' in the authorization header, I use a Service Callout to get the public keys from Google's public keys /oauth2/v3/certs , then Verify the JWT using the keys from google.
I have created and OAuth 2.0 Client in the API & Services and had configured the user type as "Internal" in the OAuth Consent Screen so I have figured only gmail accounts with hd of "company.com" will only get accepted as valid JWT. My problem is that it is accepting any 'id_token' from Any Google accounts including my personal Gmail (which I do not want). I have placed my app to 'External' and added some users to test in the meantime.
My question is that how can I restrict my authenticated users on those who are only registered users? Am I missing anything in my configuration?
Q2. How does my OAuth 2.0 Client that I created fit in Apigee when the only time I used it is when trying to retrieve a JWT token. I have created the client myself and set the my scope to openid, email and profile myself so I understand that my email shows in the JWT token but what if there is another developer? Do we have to share the same Client Id? How do I let Apigee know what I am using this OAuth client from APIs & Services?
Thank you for reading my woes and I appreciate all of your insight regarding this issue. Also I apologize if I get some terms wrong.
Solved! Go to Solution.
I'm glad you are benefiting from the videos. One note: Since I published the original video, there has been an update to VerifyJWT policy.You don't need to still use ServiceCallout to retrieve the JWKS. You can specify the JWKS directly in the VerifyJWT policy. Like this:
<VerifyJWT name='JWT-VerifyGoogleIdToken'>
<Algorithm>RS256</Algorithm>
<Source>gauth_id_token</Source>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<PublicKey>
<!-- specify the URI directly -->
<JWKS uri='https://www.googleapis.com/oauth2/v3/certs'/>
</PublicKey>
</VerifyJWT>
Using ServiceCallout still works of course. But it might be easier to let the policy retrieve the keys directly.
Also, some comments.
It might be worth re-stating: The ID token returned from Google is an identity token - it asserts the identity of the authenticated user. That ID token is generally intended for consumption by the app that the user interacts with. It is not intended to be an access token, something that grants access to a particular service. Often an OIDC authentication event will issue an ID token as well as an access token. You can decide what to do with those tokens; I'm just pointing out the design intent.
how can I restrict my authenticated users on those who are only registered users? Am I missing anything in my configuration?
The VerifyJWT policy, when minimally configured, will check that the signature on the token is valid, using the JWKS (public keys) you provide. If the policy succeeds, you know that the party that published the public keys (in this case Google) has signed the token. That's all you know. Your observation that the policy will pass any Google ID token is consistent with that.
You can configure the VerifyJWT policy to check that specific claims in the token have specific values. There are some configuration elements that let you check the issuer, subject, and audience. For other, non-registered claims, there is the "AdditionalClaims" element. Use it like this to check the hd claim:
<VerifyJWT name='JWT-VerifyGoogleIdToken'>
<Algorithm>RS256</Algorithm>
<Source>gauth_id_token</Source>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<PublicKey>
<!-- specify the URI directly -->
<JWKS uri='https://www.googleapis.com/oauth2/v3/certs'/>
</PublicKey>
<AdditionalClaims>
<Claim name="hd">example.com</Claim>
</AdditionalClaims>
</VerifyJWT>
If you don't have an hd claim, then you could check the email claim. There is no way to configure VerifyJWT to allow tokens only if a claim matches a particular pattern, for example only if the email ends in@Former Community Member.com . Therefore without relying on the "hd" claim, if you want to restrict to users that are from a particular company, then, you can follow your policy with a Condition and RaiseFault, that checks the verified email address in the ID token. It might look something like this:
<Step>
<Name>JWT-VerifyGoogleIdToken</Name>
</Step>
<Step>
<Condition>NOT (jwt.JWT-VerifyGoogleIdToken.decoded.claim.email ~~ ".+@example.com")</Condition>
<Name>RF-Unauthorized</Name>
</Step>
I'm glad you are benefiting from the videos. One note: Since I published the original video, there has been an update to VerifyJWT policy.You don't need to still use ServiceCallout to retrieve the JWKS. You can specify the JWKS directly in the VerifyJWT policy. Like this:
<VerifyJWT name='JWT-VerifyGoogleIdToken'>
<Algorithm>RS256</Algorithm>
<Source>gauth_id_token</Source>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<PublicKey>
<!-- specify the URI directly -->
<JWKS uri='https://www.googleapis.com/oauth2/v3/certs'/>
</PublicKey>
</VerifyJWT>
Using ServiceCallout still works of course. But it might be easier to let the policy retrieve the keys directly.
Also, some comments.
It might be worth re-stating: The ID token returned from Google is an identity token - it asserts the identity of the authenticated user. That ID token is generally intended for consumption by the app that the user interacts with. It is not intended to be an access token, something that grants access to a particular service. Often an OIDC authentication event will issue an ID token as well as an access token. You can decide what to do with those tokens; I'm just pointing out the design intent.
how can I restrict my authenticated users on those who are only registered users? Am I missing anything in my configuration?
The VerifyJWT policy, when minimally configured, will check that the signature on the token is valid, using the JWKS (public keys) you provide. If the policy succeeds, you know that the party that published the public keys (in this case Google) has signed the token. That's all you know. Your observation that the policy will pass any Google ID token is consistent with that.
You can configure the VerifyJWT policy to check that specific claims in the token have specific values. There are some configuration elements that let you check the issuer, subject, and audience. For other, non-registered claims, there is the "AdditionalClaims" element. Use it like this to check the hd claim:
<VerifyJWT name='JWT-VerifyGoogleIdToken'>
<Algorithm>RS256</Algorithm>
<Source>gauth_id_token</Source>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<PublicKey>
<!-- specify the URI directly -->
<JWKS uri='https://www.googleapis.com/oauth2/v3/certs'/>
</PublicKey>
<AdditionalClaims>
<Claim name="hd">example.com</Claim>
</AdditionalClaims>
</VerifyJWT>
If you don't have an hd claim, then you could check the email claim. There is no way to configure VerifyJWT to allow tokens only if a claim matches a particular pattern, for example only if the email ends in@Former Community Member.com . Therefore without relying on the "hd" claim, if you want to restrict to users that are from a particular company, then, you can follow your policy with a Condition and RaiseFault, that checks the verified email address in the ID token. It might look something like this:
<Step>
<Name>JWT-VerifyGoogleIdToken</Name>
</Step>
<Step>
<Condition>NOT (jwt.JWT-VerifyGoogleIdToken.decoded.claim.email ~~ ".+@example.com")</Condition>
<Name>RF-Unauthorized</Name>
</Step>