feat: revoke VK-mode OAuth2 grants on VK deletion and add user-liveness checks at refresh and request time - #4806
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds an Liveness enforcement for users and VKs
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" Comment |
|
|
38b8dd1 to
4065be9
Compare
Merge activity
|
4065be9 to
1729701
Compare
…ss checks at refresh and request time (#4806) ## Summary When a user identity is deleted or deactivated, their gateway-issued OAuth2 grants and active MCP requests should be cut off immediately rather than continuing to work until the access token naturally expires. This PR adds a user liveness check (`IsUserActive`) that mirrors the existing virtual-key liveness check, enforcing it at both request time and token refresh time. ## Changes - Added `IsUserActive` to the `OAuth2IdentityResolver` interface, returning `(false, nil)` for a gone/deactivated user and reserving errors for transient failures. - Added a user liveness check in `userScopedServer` on the MCP request path — a deleted user is rejected before any virtual key resolution, preventing fallthrough to the global server until the access token expires. - Added a user liveness check in `handleTokenRefresh` — a deleted or deactivated user receives `invalid_grant` on refresh rather than silently receiving a new access token. - When a virtual key is deleted, any gateway-issued OAuth2 refresh tokens in `vk` mode bound to that VK are now revoked (setting `revoked_at`) rather than deleted, so they stop minting access tokens on refresh and fall off the active-grants view while remaining available for reuse detection until the sweep. - Migrated `TableOAuth2RefreshToken` in the test setup and added tests covering VK deletion grant revocation, user-inactive refresh rejection, active-user refresh success, and user-inactive MCP request rejection. ## Type of change - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [ ] Chore/CI ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [ ] Providers/Integrations - [ ] Plugins - [ ] UI (React) - [ ] Docs ## How to test ```sh go test ./framework/configstore/... ./transports/bifrost-http/handlers/... ``` Key scenarios to verify: - Deleting a virtual key sets `revoked_at` on any associated `vk`-mode refresh tokens rather than leaving them active. - A refresh request for a `user`-mode token where `IsUserActive` returns `false` receives a `400 invalid_grant` response with `"user is no longer active"`. - A refresh request for a `user`-mode token where the user is active completes successfully with a `200` and a rotated token. - An MCP request bearing a `user`-mode JWT where `IsUserActive` returns `false` is rejected with an error rather than falling through to the global server. ## Breaking changes - [x] Yes - [ ] No Any implementation of the `OAuth2IdentityResolver` interface must now implement the `IsUserActive(ctx context.Context, userID string) (bool, error)` method. ## Security considerations This closes a window where a deleted or deactivated user could continue to access MCP resources and silently rotate refresh tokens until their access token expired. The fix ensures revocation is enforced at both the request and refresh layers, consistent with how virtual key deactivation is already handled. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable

Summary
When a user identity is deleted or deactivated, their gateway-issued OAuth2 grants and active MCP requests should be cut off immediately rather than continuing to work until the access token naturally expires. This PR adds a user liveness check (
IsUserActive) that mirrors the existing virtual-key liveness check, enforcing it at both request time and token refresh time.Changes
IsUserActiveto theOAuth2IdentityResolverinterface, returning(false, nil)for a gone/deactivated user and reserving errors for transient failures.userScopedServeron the MCP request path — a deleted user is rejected before any virtual key resolution, preventing fallthrough to the global server until the access token expires.handleTokenRefresh— a deleted or deactivated user receivesinvalid_granton refresh rather than silently receiving a new access token.vkmode bound to that VK are now revoked (settingrevoked_at) rather than deleted, so they stop minting access tokens on refresh and fall off the active-grants view while remaining available for reuse detection until the sweep.TableOAuth2RefreshTokenin the test setup and added tests covering VK deletion grant revocation, user-inactive refresh rejection, active-user refresh success, and user-inactive MCP request rejection.Type of change
Affected areas
How to test
go test ./framework/configstore/... ./transports/bifrost-http/handlers/...Key scenarios to verify:
revoked_aton any associatedvk-mode refresh tokens rather than leaving them active.user-mode token whereIsUserActivereturnsfalsereceives a400 invalid_grantresponse with"user is no longer active".user-mode token where the user is active completes successfully with a200and a rotated token.user-mode JWT whereIsUserActivereturnsfalseis rejected with an error rather than falling through to the global server.Breaking changes
Any implementation of the
OAuth2IdentityResolverinterface must now implement theIsUserActive(ctx context.Context, userID string) (bool, error)method.Security considerations
This closes a window where a deleted or deactivated user could continue to access MCP resources and silently rotate refresh tokens until their access token expired. The fix ensures revocation is enforced at both the request and refresh layers, consistent with how virtual key deactivation is already handled.
Checklist
docs/contributing/README.mdand followed the guidelines