Improve OIDC session handling and logout
Describe the feature you'd like
Once a user successfully logs into BookStack, their session becomes largely independent from the session at the OpenID Provider. While BookStack does support RP-Initiated Logout, it doesn't utilize the OPs session lifetime nor does it offer an endpoint for Front/Back-Channel Logout.
In the event that the OP sets a session lifetime, BookStack should deem a user as unauthenticated once the session expires. However, if the OP supplies a refresh_token, BookStack should use this token to refresh the session in the background, thus allowing the users session to persist. This refresh should be transparent to the user.
Ideally, BookStack should also provide an endpoint for Back-Channel Logout. This would enable the OpenID Provider to request the termination of a specific session or all sessions associated with a user.
Describe the benefits this would bring to existing BookStack users
- Reduces the risk of unauthorized access by limiting the session lifetime
- Users do not need to log out of multiple applications
- Users may log out from other devices than the one they used to log in
- Administrators may initiate a logout
Can the goal of this request already be achieved via other means?
No. (While it might technically be possible to accomplish this through the logical theme system, that's likely to get quite ugly.)
Have you searched for an existing open/closed issue?
- [X] I have searched for existing issues and none cover my fundamental request
How long have you been using BookStack?
Not using yet, just scoping
Additional context
Prior discussion in #3715 (RP-Initiated Logout).
The necessary specs have been finalized a while ago:
- https://openid.net/specs/openid-connect-frontchannel-1_0.html
- https://openid.net/specs/openid-connect-backchannel-1_0.html
In terms of implementation, both Keycloak and Auth0 support Back-Channel Logout, although I've not looked into the extent of support from other OpenID Providers. Keycloak passes the OpenId Conformance tests for Front/Back-Channel Logout.
I'm open to implementing this feature myself, but I lack prior experience with Laravel and PHP, so I have some questions about the applications architecture and Laravel.
Here's my preliminary plan for managing the session lifetime:
- In OidcService::processAccessTokenCallback: Store the token response in the session.
- Is there a size limit on the session? Keycloaks tokens can be quite long.
- In a new middleware: Check if it's an OIDC session. If so, validate the lifetime and decide whether the session is valid, a refresh is possible and was successful or whether a logout is necessary.
- Would a middleware be the best place for this?
- Potentially add an option to enable / disable session management.
For Front/Back-Channel Logout:
- In routes / OidcController: Add an endpoint.
- In OidcService: Validate and handle the request.
- There doesn't seem to be a method to query all sessions (independent of storage method), so what's the best way to execute a logout for an entirely different session? Add the session ID to a blacklist and perform the actual logout in the middleware?
Notes / Considerations:
- Front/Back-Channel Logout must be explicitly configured at the OP, so there's no need to make this configurable in BookStack.
- In order to receive logout requests, we need to keep the clients session at the OP alive, meaning we have to implement the session management described above.
- If a user remains idle long enough for the OP's session to expire, they'll be logged out (as intended), but this might occur while editing a page. Currently, if you edit something, log out in another tab, and then try to save the edit, BookStack shows a generic error message and the edit is lost. We should mitigate this, perhaps by checking a new endpoint like
/ajax/logged-inbefore saving and prompting the user to reauthenticate if necessary. - There's little benefit in implementing the
iframepolling described in OpenID Connect Session Management 1.0 as it doesn't work for Back-Channel Logouts.