fix(vcs): send the GitHub App ID as an integer in the JWT issuer claim - #204
Open
levivannoort wants to merge 2 commits into
Open
fix(vcs): send the GitHub App ID as an integer in the JWT issuer claim#204levivannoort wants to merge 2 commits into
levivannoort wants to merge 2 commits into
Conversation
generateAccessToken() put the App ID into `iss` as the string it was read
from. GitHub reads that claim as a JSON integer and answers every token
request 401 "'Issuer' claim ('iss') must be an Integer", so no installation
token is ever issued and all 94 GitHub e2e tests error. The suite has been
red on every run since 2026-09-02; the runs that looked green in between
had no vcs job at all, because vcs only runs when the changed set reaches
it.
A client ID is a legitimate string issuer, so only an all-digit identifier
is cast.
The unit test froze the defect rather than catching it: it asserted
`iss` was the string '5678' -- the exact value GitHub rejects -- so it
passed throughout. It now asserts the type, and a second case covers the
client-ID path the cast must not touch. Reverting the one-line fix turns
the first red with "Failed asserting that '5678' is of type int."
levivannoort
requested review from
Meldiron,
abnegate,
eldadfux and
loks0n
as code owners
September 3, 2026 10:33
The cast alone did not clear CI: the 94 GitHub 401s came back unchanged, which can only happen if ctype_digit() saw a non-digit and the identifier passed through as a string. The likeliest reason is invisible whitespace -- the value arrives from a CI secret, where a trailing newline does not show up anywhere. Trimming is right regardless of whether that is the cause here. It also makes the empty case honest: a whitespace-only secret now collapses to '' and the e2e suite skips with "GitHub App credentials not configured" instead of erroring 94 times. If the identifier is genuinely not numeric, the failure now says so. The exception carries the issuer's type and length -- never its value -- because GitHub's response cannot distinguish a badly shaped claim from a wrong credential behind it, and this cost a full CI round trip to establish.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What breaks
generateAccessToken()puts the App ID into the JWTissclaim as the string it was read from:That encodes as
"iss":"1234567". GitHub reads the claim as a JSON integer and answers every token request:No installation token is ever issued, so all 94 GitHub e2e tests error. Six Bitbucket 403s are a separate issue, likely masked behind this.
It has been red for two days
test (vcs, linked)has failed on every run since 2026-09-02. The runs that looked green in between had zero vcs jobs — vcs only runs when the changed set reaches it, so a nats-only or queue-only PR never exercises it. It surfaced on #201 only because that touchesbin/, which puts every package in scope.This is not the staging cluster and not NATS: the vcs compose stack is gitea, forgejo, gogs, gitlab and a request-catcher, with no NATS service. The credentials are present too —
GitHubTest::setupAdapter()callsmarkTestSkipped()when the App ID is empty, and the tests error rather than skip.The fix
A client ID (
Iv23li...) is a legitimate string issuer, so only an all-digit identifier is cast.The test froze the defect
testInitializeVariablesSignsJwtWithPemStringverified the signature and the path correctly, but its last assertion was:So it passed the whole time the suite was down. It now asserts the type, and a second case covers the client-ID path. Seen red: reverting the one-line fix gives
Failed asserting that '5678' is of type int.Unit suite: 559 tests, 1127 assertions, all passing.