feat(internal): jwtsign + public-request auth exemptions - #1375
Merged
Merged
Conversation
NitinKumar004
marked this pull request as ready for review
September 27, 2026 16:15
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.
Summary
Work item F1 from build-out plan A (Cognito, API Gateway, AppSync). It lays two foundations the Cognito and API Gateway PRs build on, and it tightens how
--enforce-authtreats STS session credentials.internal/jwtsignSignandVerify. Verify checks alg (only RS256, sononeand HS256 are rejected), kid, signature,exp(required),nbf, andiatwith a 60 second skew allowance, against aconfig.Clock.ErrExpiredis kept apart fromErrInvalidTokenso Cognito can return its separate "token has expired" message.NewRSAKeymakes 2048-bit keys. The kid is the RFC 7638 JWK thumbprint, so it survives persistence and two keys never share one.JWKS(keys...)publishes the public halves in Cognito'sjwks.jsonfield set. Rotation means adding a key and keeping the old one in the verify set until its tokens expire.MarshalPKCS8andParsePKCS8cover snapshot persistence.Nothing calls the package yet. Cognito token issuance (C2) is the first consumer.
Public (noAuth) requests under
--enforce-authThe exemption is decided by the handler that dispatch will pick, for that exact request:
server.PublicRequesteris a new optional handler interface:PublicRequest(r) bool. A handler implements it only for the public routes it serves itself.Matchon a probe copy of the request. The probe has fresh form state and the same body bytes dispatch will read. The gate then asks the matched handler. When no handler matches, the request is not exempt.Implementations:
server/aws/cognito/public.go). Only the noAuth cognito-idp operations, and only asPOST /with the operation'sX-Amz-Target. The list comes from the botocore model ("auth": ["smithy.api#noAuth"]).TestPublicOpsMatchSDKModelre-derives it from the aws-sdk-go-v2 auth resolver and fails on drift._user_request_path).ServeHTTProutes on the same two predicates.A request that borrows a public marker is still gated: a hosted-UI Host, a
/.well-knownpath, an execute-api Host with a DynamoDB target, or an AppSync host on/v1/apis. So is anything that is not one of these two surfaces.STS session credentials are authorized
Before this change, temporary (ASIA) credentials were authenticated but never authorized. Each STS session now records its owner, and the gate authorizes requests as that owner:
Authorization still covers JSON-RPC only. Query and REST requests stay authenticate-only, for long-term keys and sessions alike. That limit already existed and is documented in
authzgate.go.Deferred (each has a PR slot)
/.well-known/openid-configurationand the hosted UI. These become public when the Cognito handler serves them as target-less GETs (C-series).Tests
internal/jwtsign: round trip, tamper, wrong key under the same kid, alg none/HS256, malformed tokens, the expiry boundary, nbf, iat skew, missing exp, unknown kid, rotation, JWKS contents, and the PKCS#8 round trip.server/aws/authbypass_test.go: a table of 30 bypass attempts that must all get the gate's 403 MissingAuthenticationToken. It covers every reviewed repro plus these variants: duplicate and case-changed Actions, a bad query string, case-changed Cognito targets, and execute-api markers on DynamoDB, Lambda and IAM requests. A separate test sends the real STS SDK's anonymous AssumeRoleWithWebIdentity and expects rejection.server/aws/publicauth_test.go: unsigned public ops are let through, authz skips public ops, and STS sessions are authorized as their owner (missing role, role with no policies, role outside its policy, a policy-limited user's session, an unrestricted user's session).server/aws/cognito/public_test.go: the model cross-check and the route shape.contrib/server: an--enforce-authcase. InitiateAuth gets past the gate and CreateUserPool is rejected.The bypass and session tests fail on the previous commit of this branch.
E2E (
cloudemu serve --enforce-auth, AWS on 57966)All the curl repros are rejected with 403 MissingAuthenticationToken:
/_cognitopath with CreateUserPoolThese reach their handler:
With the aws CLI,
--no-sign-request cognito-idp initiate-authgets past the gate.--no-sign-request sts assume-role-with-web-identityis rejected.