Add vice-users OAuth callback relay to vice-operator - #144
Conversation
vice-proxy can no longer send per-app VICE subdomains as the Keycloak redirect_uri — Keycloak only wildcard-matches a trailing *, so https://*.cyverse.run:4343/* never matches a real app. The operator now exposes a fixed /vice-users/callback endpoint that is registered in Keycloak as the single static redirect_uri for the vice-users client. The handler is a stateless relay: it verifies the HMAC-signed state (go-mod/viceauth), recovers the original app URL from it, checks that URL is a single-label subdomain of the VICE base domain (open-redirect guard), and bounces the browser back with the authorization code intact. vice-proxy holds the client secret and does the token exchange itself. New --public-url and --state-hmac-secret flags feed two new cluster-config keys (OPERATOR_CALLBACK_URL, STATE_HMAC_SECRET) consumed by vice-proxy via EnvFrom. The state HMAC secret must be stable across operator restarts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
- Build operatorCallbackURL with url.URL.JoinPath() instead of string concatenation, per the project URL-construction guideline; fail fast on an unparseable --public-url. Drops the now-unused strings import. - Fix the startup warning: the relay is not "disabled" when only --public-url is empty (it is gated on the state HMAC secret alone) — the accurate consequence is that vice-proxy pods fail to start without both values. - Harden the relay redirect target: reject origin URLs carrying userinfo (a redirect-spoofing vector) and strip any fragment before relaying. Adds a userinfo test case. Addresses code review feedback on PR #144. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
go-mod#11 merged and viceauth/v1.0.0 is tagged; move off the auth-relay pseudo-version to the released tag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| func NewApp(op *operator.Operator, verifier *oidc.IDTokenVerifier, expectedClientID string, swaggerCfg *SwaggerAuthConfig, adminRole string, adminEntitlements []string) *App { | ||
| // without authentication. When viceUsersCfg is non-nil, the unauthenticated | ||
| // vice-users OAuth callback relay is registered. | ||
| func NewApp(op *operator.Operator, verifier *oidc.IDTokenVerifier, expectedClientID string, swaggerCfg *SwaggerAuthConfig, adminRole string, adminEntitlements []string, viceUsersCfg *ViceUsersAuthConfig) *App { |
There was a problem hiding this comment.
The number of parameters here was already large and is getting larger. Might be time to add an init struct.
| // viceUsersCallbackPath is the fixed path of the OAuth callback relay. It is | ||
| // combined with --public-url to form OPERATOR_CALLBACK_URL, the single static | ||
| // redirect_uri registered in Keycloak for the vice-users client. | ||
| const viceUsersCallbackPath = "/vice-users/callback" |
There was a problem hiding this comment.
Let's rename the callback path to /auth/callback.
| // handleViceUsersCallback returns the handler for GET /vice-users/callback. | ||
| // | ||
| // vice-proxy registers this operator's URL as the single static redirect_uri | ||
| // for the Keycloak "vice-users" client, because Keycloak cannot wildcard-match |
There was a problem hiding this comment.
Let's not reference configurable values in a comment like this. They're fine to include in examples, but the way it's worded here makes it sound like it's set in stone that we're using a vice-user client. We could rename or recreate it later, making the comment stale.
|
The changes you proposed, all sound reasonable to me. 👍 |
- NewApp now takes an AppConfig struct instead of a 7-parameter positional list. - Rename the OAuth callback path from /vice-users/callback to /auth/callback. - Reword the relay comments so they no longer state the Keycloak client name as fixed — the client is configurable and could be renamed or recreated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks for the review, @slr71. |
slr71
left a comment
There was a problem hiding this comment.
These changes seem reasonable to me.
| // redirect_uri for the OAuth client vice-proxy authenticates against — | ||
| // Keycloak cannot wildcard-match per-app VICE subdomains, so a single fixed | ||
| // callback is needed. Keycloak delivers the authorization code here; the |
There was a problem hiding this comment.
I view this endpoint as essentially a relay that allows us to use a static callback URL in the Keycloak OIDC client for each VICE operator. This endpoint uses the state parameter to determine which vice-proxy to redirect the browser to and does some sanity checks on the request to ensure that the state parameter is signed with the appropriate key and that the browser is only redirected to authorized locations.
Rewording the comment may not be necessary, but I didn't develop a thorough understanding of how this works until I read the code. I'm not sure that the original comment helped me understand it (although I must admit that I'm not entirely sure that it didn't help me understand the endpoint either). 😆
Summary
GET /auth/callbackendpoint to the vice-operator. This becomes the single staticredirect_uriregistered in Keycloak for thevice-usersclient, replacing the non-functionalhttps://*.cyverse.run:4343/*wildcard (Keycloak only honors*as a trailing wildcard, so per-app VICE subdomains never matched — the "invalid parameter: redirect_uri" error in QA).state(github.com/cyverse-de/go-mod/viceauth), recovers the original app URL, validates that URL is a single-label subdomain of the VICE base domain (open-redirect guard), and bounces the browser back with the authorization code intact. It does no token exchange —vice-proxyholds the client secret and redeems the code itself.--public-urland--state-hmac-secretflags feed two new cluster-config Secret keys (OPERATOR_CALLBACK_URL,STATE_HMAC_SECRET) thatvice-proxyconsumes viaEnvFrom. No changes needed inincluster/— the Secret is forwarded wholesale.Notes
--state-hmac-secretmust be stable across operator restarts (treat like--keycloak-client-secret). A regenerated secret would diverge from the value baked into already-running vice-proxy pods until they are recreated.Dependencies
viceauthmodule) — pinned to a pseudo-version of that branch; bump toviceauth/v1.0.0once it merges.Test plan
go build ./...,go vet ./...,gofmtgo test ./cmd/vice-operator/...— table-driven tests forisAllowedHostandhandleViceUsersCallback(valid bounce,?error=, missing code/state, bad signature, off-domain / nested-subdomain / non-https origin)/auth/callbackURL in Keycloak🤖 Generated with Claude Code