🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

Expose external auth token expiry so git credential helpers can set password_expiry_utc #26036

Description

@jasonwbarnett

Summary

Surface the expiry of an external auth token (alongside the token itself) so a git credential helper built on coder external-auth access-token can emit git's password_expiry_utc field. This lets git-credential-cache hold a Coder-minted token for exactly its valid lifetime and evict it the moment it rotates.

Background

In a workspace, git HTTPS auth to GitHub is handled by GIT_ASKPASS pointing at the coder binary (coder gitaskpass), which fetches a token from external auth via the control plane. This is always-fresh and correct, but it costs a control-plane round-trip on every credential request (~0.6-1s in our environment), and git requests credentials twice per HTTPS auth (username + password), so each git fetch / push pays roughly 1-2s of auth overhead.

The natural mitigation is to put git-credential-cache in front of the askpass:

[credential "https://github.com"]
    helper = cache --timeout=900

This is fast, but git-credential-cache only accepts a fixed --timeout and has no knowledge of the token's real lifetime. Because external auth tokens rotate (GitHub App user-to-server tokens last ~8h), a fixed timeout can cache a token that rotates/expires before the timeout elapses. The cache then serves a stale token until the timeout expires, producing intermittent 401s. The worst-case stale window equals the timeout.

Request

Expose the token's expiry so a credential helper can output password_expiry_utc (git >= 2.34). With it, git and git-credential-cache cache the credential for exactly its valid life and re-invoke the helper the instant it expires: fast and correct, with no fixed-timeout guesswork and no stale window.

A helper would emit:

protocol=https
host=github.com
username=x-access-token
password=<token>
password_expiry_utc=<unix-seconds>

The server already tracks the OAuth expiry (oauth_expiry; see coderd/database/queries/externalauth.sql and coderd/externalauth/externalauth.go); it just isn't surfaced to clients. Today:

  • coder external-auth access-token <provider> (cli/externalauth.go) prints only the token. --extra reads the provider's OAuth extra map only; for GitHub it returns no extra properties found for token (cli/externalauth.go:91), and the tracked Expiry isn't reachable.
  • agentsdk.ExternalAuthResponse (codersdk/agentsdk/agentsdk.go:696) carries TokenExtra, URL, Username, Password, but no token expiry.

Any one of these would solve it:

  1. A --expiry flag (or a --format json / password_expiry_utc output mode) on coder external-auth access-token.
  2. Add the expiry to agentsdk.ExternalAuthResponse so coder gitaskpass and credential helpers can read it.

Option 1 alone is enough to build the helper purely client-side.

Why password_expiry_utc

It is git's intended mechanism for short-lived, rotating credentials: a helper returns the credential plus its expiry, git treats it as invalid after that time and re-invokes the helper. The field was added in git 2.34 (see the git-credential documentation). Without an expiry source, users must choose between a slow per-operation askpass and a fixed-timeout cache that has a stale-token window.

Environment

  • Coder v2.33.x (CLI + workspace agent)
  • git 2.43.0
  • GitHub App-based external auth (token rotates ~8h)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions