test(jwt): pin the claim checks and every stored format; fix the rotation and blind-index guides - #481
Merged
Conversation
…ex read path, and the credential binding Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
…ro-value guard, and the refresh-hash format; give both fuzz targets oracles Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
… and the seeds that reach ParseID's rules Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
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.
Closes #480 in three commits: two are tests only, one is documentation and a package doc comment. No production behaviour changes.
Guides
docs/secure-login.mdsection 5 replaces the refresh hash with the conditionalUPDATE ... WHERE id = $2 AND refresh_hash = $3and a row-count check, the recipedocs/jwt.mdstep 5 got in docs: fix the recipes that lose credentials #427, and says that reuse detection depends on it.auth/fieldpackage doc's read path recomputes the index of the decrypted value and compares it with the one looked up; the sentence claiming an index hit proves the plaintext is gone.docs/configuration.mddescribes the credential binding as the length-prefixed construction the code uses;docs/errors.mdgains thejwt.ErrTokenOversizedandjwt.ErrNotInitialisedrows;docs/key-management.mdsays which modules share one HMAC key and why that waits for a release that can change stored hashes.jwt tests
expand one issued a day in the future are refused on the access path (the signer fixture now carries a UUIDv7jti, since fix(jwt): make issuance and verification agree #443's shape check refused the old"jti"first and left these tests green with the option deleted) and on the refresh path; each is paired with the same token accepted.mapJWTErrorcollapses the reason intoErrTokenInvalid, so the acceptance twin is what proves which check fired.RotateTokens(20 s pastexpaccepted with a 30 s leeway, 40 s refused); the refresh-token issuance cap with a 5806-byte issuer refused and a 5600-byte one issuing and rotating;validateConfigrefusing an empty audience directly, whichNewnever reaches; the zero-valueVerifyRefreshTokenHashguard against the hash an empty key produces, which the old fixture ("any-hash") never reached.FuzzIsUUIDv7compares against RFC 9562 written as a regular expression apart from the code, with seeds for a moved dash, hex digits where the dashes go, a non-hex byte and a short input;FuzzVerifyAccessTokenaccepts only the issued access token and requires every refusal to be a documented sentinel.apikey, credential, field tests
FuzzParseIDseeds that reach the lowercase-hex rule and the prefix check; a zero-valueapikey.Hashrefused.Sabotage, each edit confirmed to change the file and to compile:
WithExpirationRequired/WithIssuedAtfromparserOptionsVerifyRefreshTokenHash/ inapikey.HashisUUIDv7FuzzIsUUIDv7fails on a seedisHexto A-F / drop the prefix check inParseIDFuzzParseIDfails on a seedVerifyAccessTokenFuzzVerifyAccessTokenfails on the refresh seedTestValidateConfig_refusesAnEmptyAudiencefailsThe refresh-token issuance cap has a test; its sabotage was not run separately because the shared verifier refuses the oversized token on rotation anyway, so the test fails under it through that path.
gofmt,go vet ./...,go test -race -count=1 ./..., gosec and the nine example builds are clean. Coverage: jwt 95.9%, apikey 96.2%, credential 98.4%, field 92.9%.Signed-off-by: Jaro-c 75870284+Jaro-c@users.noreply.github.com