Skip to content

Set the OIDC publishing vars for production - #90

Merged
fengmk2 merged 1 commit into
mainfrom
feat/oidc-vars
Aug 10, 2026
Merged

Set the OIDC publishing vars for production#90
fengmk2 merged 1 commit into
mainfrom
feat/oidc-vars

Conversation

@fengmk2

@fengmk2 fengmk2 commented Aug 10, 2026

Copy link
Copy Markdown
Member

Fixes an omission from #86: the four OIDC_* vars were declared in src/config.ts's Env interface but never in env.ts or .env.production, so they are absent at runtime and the OIDC path has been disabled since deploy.

They are not secrets. All four hold public identifiers and the verification key is GitHub's public JWKS, so per the README's configuration model they belong in env.ts plus the committed .env.production, not void secret put. I had been describing this as needing Void auth, which was wrong; it is a code change and should have shipped with #86.

Declared optional as a group. All four unset disables OIDC and leaves admin-token publishing untouched, which is production's current state. Setting only some is already rejected at request time with a 503 naming the missing var.

OIDC_AUDIENCE=https://registry-bridge.viteplus.dev
OIDC_TRUSTED_WORKFLOWS=voidzero-dev/vite-plus/.github/workflows/publish-preview-register.yml@refs/heads/main
OIDC_TRUSTED_REPOSITORY_ID=943901988
OIDC_TRUSTED_OWNER_ID=149750581

The numeric ids are what anchor trust, since OIDC_TRUSTED_WORKFLOWS embeds a repository name and names can be renamed, transferred, or reclaimed. Read back from gh api repos/voidzero-dev/vite-plus.

One wrinkle worth knowing

pnpm deploy:staging runs the same void deploy, so staging inherits .env.production — which is also why PUBLIC_BASE_URL there already points at prod. Staging will therefore share this audience and allowlist.

That is not an escalation while the two are identical: only the vite-plus register workflow can mint an acceptable token either way. But it does mean staging cannot be given a looser allowlist to smoke-test OIDC without that workflow also being able to publish to production, so the "test OIDC against staging first" plan I suggested does not work as described. Recorded in a comment next to the values rather than left to be rediscovered.

Effect of merging

Deploying this turns OIDC on in production. Nothing changes for existing publishes: vite-plus main still uses the admin-token path, and the admin path is unaffected either way. It simply makes voidzero-dev/vite-plus#2387 able to work once merged.

205 tests pass, typecheck clean.

These were declared in src/config.ts's Env interface but never in env.ts or
.env.production, so they were simply absent at runtime and the OIDC path has
been disabled since deploy. The post-deploy check I ran (a JWT-shaped token
returning 401) was consistent with that and I read it as "correctly
unconfigured" rather than "not configured at all".

They are NOT secrets, so they do not need `void secret put` and they do not
need anyone's Void auth: all four hold public identifiers, and the
verification key is GitHub's public JWKS. Per README, non-secret values are
declared in env.ts and set in the committed .env files. This should have been
part of #86.

Declared optional as a group: all four unset disables OIDC and leaves
admin-token publishing untouched, which is the state production is in today.
Setting only some is already rejected at request time.

One wrinkle recorded next to the values: `pnpm deploy:staging` runs the same
`void deploy`, so staging inherits .env.production (which is also why
PUBLIC_BASE_URL there points at prod). Staging therefore shares this audience
and allowlist. Not an escalation while both are identical, but it does mean
staging cannot be given a looser allowlist for an OIDC smoke test without
that workflow also being able to publish to production.
@fengmk2
fengmk2 merged commit 572f7cf into main Aug 10, 2026
4 checks passed
@fengmk2
fengmk2 deleted the feat/oidc-vars branch August 10, 2026 02:46
fengmk2 added a commit that referenced this pull request Aug 10, 2026
Found by probing production after #90 turned OIDC on: `aaa.bbb.ccc` answered
500 Internal error instead of 401.

"aaa" is a legal base64url segment, but it decodes to 0x69 0xa6, which is
invalid UTF-8, and TextDecoder({fatal: true}) throws a plain TypeError. That
escaped decodeJsonSegment (only JSON.parse was guarded), missed the HttpError
branch in the router, and became a 500. The base64url alphabet says nothing
about whether the decoded bytes are valid UTF-8, so this is reachable from
any well-formed-looking token.

No security impact, the token is rejected either way, but an unauthenticated
caller could produce 500s at will, which is noise in error monitoring and the
wrong signal about which layer refused.

The tests missed it because every fixture segment held valid UTF-8. Added one
that does not. Also guards atob, though that path is not reachable today;
marked defensive rather than covered by a test asserting a case that cannot
occur.
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