mcp: delete upstream oauth2 token (#5707)

## Summary

Adds `POST /.pomerium/mcp/routes/disconnect` that allows an MCP client
application to request upstream OAuth2 tokens to be purged, so that a
user may get a new ones with possibly different scopes.

## Related issues

Fix
https://linear.app/pomerium/issue/ENG-2545/mcp-user-should-be-able-to-purge-their-upstream-oauth2-token

## User Explanation

<!-- How would you explain this change to the user? If this
change doesn't create any user-facing changes, you can leave
this blank. If filled out, add the `docs` label -->

## Checklist

- [x] reference any related issues
- [x] updated unit tests
- [x] add appropriate label (`enhancement`, `bug`, `breaking`,
`dependencies`, `ci`)
- [x] ready for review
This commit is contained in:
Denis Mishin 2025-07-08 09:46:45 -07:00 committed by GitHub
parent f5c5326c72
commit 8a89c975d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 141 additions and 12 deletions

View file

@ -40,6 +40,10 @@ func (srv *Handler) listMCPServers(w http.ResponseWriter, r *http.Request) error
return fmt.Errorf("user id is not present in claims")
}
return srv.listMCPServersForUser(r.Context(), w, userID)
}
func (srv *Handler) listMCPServersForUser(ctx context.Context, w http.ResponseWriter, userID string) error {
var servers []serverInfo
for v := range srv.hosts.All() {
servers = append(servers, serverInfo{
@ -47,12 +51,12 @@ func (srv *Handler) listMCPServers(w http.ResponseWriter, r *http.Request) error
Description: v.Description,
LogoURL: v.LogoURL,
URL: v.URL,
needsOauth: v.Config != nil,
NeedsOauth: v.Config != nil,
host: v.Host,
})
}
servers, err = srv.checkHostsConnectedForUser(r.Context(), userID, servers)
servers, err := srv.checkHostsConnectedForUser(ctx, userID, servers)
if err != nil {
return fmt.Errorf("failed to check hosts connected for user %s: %w", userID, err)
}
@ -79,7 +83,7 @@ func (srv *Handler) checkHostsConnectedForUser(
) ([]serverInfo, error) {
eg, ctx := errgroup.WithContext(ctx)
for i := range servers {
if !servers[i].needsOauth {
if !servers[i].NeedsOauth {
servers[i].Connected = true
continue
}
@ -106,6 +110,6 @@ type serverInfo struct {
LogoURL string `json:"logo_url,omitempty"`
URL string `json:"url"`
Connected bool `json:"connected"`
needsOauth bool `json:"-"`
NeedsOauth bool `json:"needs_oauth"`
host string `json:"-"`
}