Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OIDC userinfo endpoint support #4955

Merged
merged 10 commits into from
Apr 19, 2024
Merged

OIDC userinfo endpoint support #4955

merged 10 commits into from
Apr 19, 2024

Conversation

ssddanbrown
Copy link
Member

@ssddanbrown ssddanbrown commented Apr 16, 2024

Review and continuation of #4726.
Main functional change is to only call endpoint if missing data from ID token.
Reviewed original PR against spec, and app logic, in 9183e7f.
Other OIDC code clean-up/improvements done while in this area.

Todo

  • Confirm if we'd need to support application/jwt (signed/encrypted) based responses.
  • Check and update existing testing.
  • Test cases for:
    • Ensuring discovery endpoints are https.
    • Ensuring userinfo endpoint only called if missing info.
    • Userinfo data fetched and used if expected claims missing.
    • Ensuring userinfo endpoint response is "application/json".
    • Ensuring userinfo sub claim is verified against token.
    • Ensure response validation is performed (5.3.4).
    • Name parsing from userinfo.
    • Group parsing (including nested) from userinfo.
    • Userinfo JWT use
    • Userinfo JWT validation
  • Test against real identity provider.

Doc Updates

  • Update OIDC docs to include OIDC_USERINFO_ENDPOINT option.
  • Update OIDC docs to expand non-encryption & algorithm limitations to also cover userinfo JWKs response data.
  • Update advisory to mention endpoint may now be called where not called before (in limited cases where all expected details were not coming back, check if relevant).

Luke T. Shumaker and others added 4 commits December 15, 2023 14:11
BooksStack's OIDC Client requests the 'profile' and 'email' scope values
in order to have access to the 'name', 'email', and other claims.  It
looks for these claims in the ID Token that is returned along with the
Access Token.

However, the OIDC-core specification section 5.4 [1] only requires that
the Provider include those claims in the ID Token *if* an Access Token is
not also issued.  If an Access Token is issued, the Provider can leave out
those claims from the ID Token, and the Client is supposed to obtain them
by submitting the Access Token to the UserInfo Endpoint.

So I suppose it's just good luck that the OIDC Providers that BookStack
has been tested with just so happen to also stick those claims in the ID
Token even though they don't have to.  But others (in particular:
https://login.infomaniak.com) don't do so, and require fetching the
UserInfo Endpoint.)

A workaround is currently possible by having the user write a theme with a
ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE hook that fetches the UserInfo
Endpoint.  This workaround isn't great, for a few reasons:
 1. Asking the user to implement core parts of the OIDC protocol is silly.
 2. The user either needs to re-fetch the .well-known/openid-configuration
    file to discover the endpoint (adding yet another round-trip to each
    login) or hard-code the endpoint, which is fragile.
 3. The hook doesn't receive the HTTP client configuration.

So, have BookStack's OidcService fetch the UserInfo Endpoint and inject
those claims into the ID Token, if a UserInfo Endpoint is defined.
Two points about this:
 - Injecting them into the ID Token's claims is the most obvious approach
   given the current code structure; though I'm not sure it is the best
   approach, perhaps it should instead fetch the user info in
   processAuthorizationResponse() and pass that as an argument to
   processAccessTokenCallback() which would then need a bit of
   restructuring.  But this made sense because it's also how the
   ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE hook works.
 - OIDC *requires* that a UserInfo Endpoint exists, so why bother with
   that "if a UserInfo Endpoint is defined" bit?  Simply out of an
   abundance of caution that there's an existing BookStack user that is
   relying on it not fetching the UserInfo Endpoint in order to work with
   a non-compliant OIDC Provider.

[1]: https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
- Added endpoint validation to ensure HTTPS as per spec
- Added some missing types
- Removed redirectUri from OidcProviderSettings since it's not a
  provider-based setting, but a setting for the oauth client, so
  extracted that back to service.
Allows a proper defined object instead of an array an extracts related
logic out of OidcService.
Updated userinfo to only be called if we're missing details.
Wrapped userinfo response in its own class for additional handling and
validation.
Updated userdetails to take abstract claim data, to be populated by
either userinfo data or id token data.
Not yet tested, nor checked all response validations.
Also updated test to suit validation changes
@ssddanbrown ssddanbrown merged commit d949b97 into development Apr 19, 2024
16 of 17 checks passed
@ssddanbrown ssddanbrown deleted the oidc_userinfo branch April 19, 2024 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

None yet

1 participant