Skip to content

feat(auth): send stacks an environment-specific access token - #261

Open
MrHaila wants to merge 2 commits into
mainfrom
feature/environment-access-token
Open

MrHaila wants to merge 2 commits into
mainfrom
feature/environment-access-token

Conversation

@MrHaila

@MrHaila MrHaila commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Requests to a stack carried the session's own access token, which every environment the user can reach accepts. For an environment on the default auth provider, the CLI now exchanges that token with the Portal for one minted for that environment alone (client_id is the environment), and sends only that to the stack.

 resolveEnvironment                        # session tokens, as before
-NewTargetEnvironment(tokenSet, domain, env)
+NewTargetEnvironment(tokenSet, domain, env, authProvider)
+  EnvironmentAccessToken
+    default provider: GET {portal}/api/v1/tokens/get-environment-access-token?environment_human_id=env
+    custom provider:  the provider's own access token
   StackApiClient   bearer = environment token

The same token reaches every stack-facing path:

  • StackAPI, through TargetEnvironment, in every command that builds one.
  • The game server admin API, in debug admin-request.
  • The Kubernetes API proxy, in the exec credential from get kubernetes-execcredential --proxy, which reports that token's own expiry to kubectl.

The session's token still goes to the Portal and the auth provider, and to nothing else. There is no fallback: if the Portal does not mint a token, the command fails. It never sends the session's token instead.

Supersedes #117, rebuilt on current main. Since #117 was opened, main gained new commands that reach a stack, a constructor taking a StackAPI base URL, and the Kubernetes API proxy exec credential, all covered here.

Evidence

  • Before: a stack received the session's access token, with the CLI's OAuth2 client ID (or a machine user's own) as client_id, valid for the session's lifetime.
    After: a stack receives a Portal-issued token with client_id set to the target environment and a few minutes' lifetime. Verified end to end against a stack whose StackAPI trusts the Portal as an issuer, both as a human user and as a machine user:

    • get environment-info
    • debug admin-request GET api/hello
    • debug server-status
    • debug logs
    • secrets list
    • image push / image list
    • deploy server
    • get server-info
    • get kubeconfig --type=dynamic, then kubectl get pods through the proxy

    The stack verified every user token as Portal-issued for that environment.

  • Exchanging for an environment the user cannot reach gets the Portal's 404 and no credential.

  • Unit tests:

ExchangeTokenForEnvironment: success, server error, empty token
kubernetes-execcredential --proxy
  hands kubectl the environment's token, never the session's, with that token's expiry
  fails, printing nothing, when the Portal refuses the exchange
  existing refresh / skew / machine-user cases, now asserting the environment token
GetKubeConfigWithExecCredential cases, against a fake that is also the Portal

go vet ./..., go test ./... and go mod tidy are clean.

Merge Danger

Door: two-way

Reverting restores the old token handling. Nothing is stored or migrated.

Blast Radius: every stack-facing command

A stack whose StackAPI does not yet accept Portal-issued user tokens refuses every request this CLI makes there. Hold the release until every stack the CLI must reach runs a StackAPI that does. Every command reaching a stack also makes one extra request to the Portal, so the Portal being unavailable now blocks those commands too.

Requests to a stack carried the session's own access token, which every
environment the user can reach would accept. For environments signing in
with the default auth provider, the CLI now exchanges it with the Portal
for a short-lived token minted for that one environment, and sends only
that to the stack: StackAPI, the game server's admin API, and the
Kubernetes API proxy's exec credential alike. An environment with a
custom auth provider is sent that provider's token as before.

There is no fallback: if the Portal does not mint a token, the command
fails rather than send the session's.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant