broker: use /userinfo as fallback for claims not available in id token#1476
broker: use /userinfo as fallback for claims not available in id token#1476shiv-tyagi wants to merge 3 commits intocanonical:mainfrom
Conversation
7fcdce8 to
94176c9
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1476 +/- ##
==========================================
- Coverage 86.93% 80.22% -6.72%
==========================================
Files 93 20 -73
Lines 6401 1082 -5319
Branches 111 0 -111
==========================================
- Hits 5565 868 -4697
+ Misses 780 214 -566
+ Partials 56 0 -56 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| oidcUserInfo, err := session.oidcServer.UserInfo(ctx, oauth2.StaticTokenSource(token)) | ||
| if err != nil { | ||
| log.Warningf(ctx, "could not fetch information from /userinfo endpoint: %v", err) | ||
| } | ||
|
|
||
| userInfo, err := b.provider.GetUserInfo(idToken, oidcUserInfo) | ||
| if err != nil { | ||
| return info.User{}, err | ||
| } |
There was a problem hiding this comment.
The UserInfo request is one more network request which can go wrong, and it's not needed in most cases, so I think we should not do it unconditionally but only if the ID token is actually missing a required claim. I would implement it like this:
- First, call
b.provider.GetUserInfo(idToken) - If the ID token misses a required claim,
GetUserInfoshould return aMissingClaimError - If it does, we fetch the claims from the UserInfo endpoint and merge them with the ones from the ID token into a single
claimsstruct and then callb.provider.GetUserInfo(claims)
There was a problem hiding this comment.
I don't think it is as simple as that. How do we differentiate if the email_verified claim is missing because it is a thin id token (first call) or if it is really missing (second call)? Currently, the email_verified claim check sits inside GetUserInfo.
If we really want to save a network call, the right way should be, define func IsRequiredClaimMissing(idToken info.Claimer) at provider end, if it returns true, we fetch claims from /userinfo and merge them and then do the GetUserInfo call (only one single time).
We need to decouple the ensuring required claims are present and is user's email verified steps.
There was a problem hiding this comment.
How do we differentiate if the email_verified claim is missing because it is a thin id token (first call) or if it is really missing (second call)?
In my proposal, GetUserInfo returns MissingClaimError in both cases, but the caller knows how to handle it. If GetUserInfo returns a MissingClaimError again after 3. from above then a required claim is still missing in the combined claims from the ID token and UserInfo, so we return an error.
There was a problem hiding this comment.
That would require us to move the actual "email is not verified" check out of generic provider and return an error while handling the MissingClaimError (form the second call) in broker.go. Does that sound okay?
I was assuming that we wanted to confine the email verified check within the generic broker.
There was a problem hiding this comment.
That would require us to move the actual "email is not verified" check out of generic provider
I don't understand why we would need to do that. I'm on my phone right now so I can't show you the diff of my proposal, but I don't see why we would have to do anything else in broker.go than return an error in the case that GetUserInfo returns a MissingClaimError again after 3. Maybe I'm missing something.
There was a problem hiding this comment.
Thanks for responding on a weekend @adombeck. Please feel free to check this and reply when you return to work.
if !userClaims.EmailVerified {
return info.User{}, &providerErrors.ForDisplayError{Message: "Authentication failure: email not verified"}
}
Currently this line sits in in GetUserInfo in genericprovider.go, if I am not getting it wrong, now you are suggesting to just return a MissingClaimError from there.
The GetUserInfo is called from broker.go, so now we would need to handle MissingClaimError in broker.go for email_verified claim after 3 (why specifically for email_verified claim? - to display user friendly error like we do today). And this would apply for all providers (just that entra provider would never return MissingClaimError so that path would never get hit there).
There was a problem hiding this comment.
@adombeck I pushed some work. It saves a network call by only trying /userinfo if a mandatory claim required for a provider is missing in the id token.
Please check if this is what you were looking for.
I have included tests in my work and have tested this for both msentraid and oidc broker.
Thanks!
There was a problem hiding this comment.
I'm out of office this week, will take a look next week
94176c9 to
e5f4a56
Compare
Signed-off-by: Shiv Tyagi <shivtyagi3015@gmail.com>
e5f4a56 to
483e7f2
Compare
Signed-off-by: Shiv Tyagi <shivtyagi3015@gmail.com>
Signed-off-by: Shiv Tyagi <shivtyagi3015@gmail.com>
483e7f2 to
06e3b78
Compare
|
Tested this on our side as well with okta and can confirm it's working; would be great to see this merged :) |
Fixes #1440.
This uses the information retrieved from /userinfo endpoint as a fallback for claims not available in id token.
This only affects the generic oidc broker, for msentraid, we simple drop the information retrieved from /userinfo.
I have verified both oidc and msentraid brokers with these changes, they both work fine.