Skip to content

Bind the challenge nonce in JWTAuthValidator so signed JWTs cannot be replayed #58

@nficano

Description

@nficano

When challengeRequired is set on ARCPRuntime, the handshake at Sources/ARCP/Runtime/ARCPRuntime.swift:149 sends a fresh session.challenge nonce and then calls auth.validate(auth: authPayload.auth, challenge: nonce). JWTAuthValidator.validate(auth:challenge:) at Sources/ARCP/Auth/JWTAuth.swift:24 receives that nonce as the challenge nonce: String? parameter but never reads it: it only verifies the JWT signature, exp, and aud. The minimal ARCPClaims struct at Sources/ARCP/Auth/JWTAuth.swift:48 has no nonce field, and verify(using:) only calls exp.verifyNotExpired(). The result is that the challenge mechanism is ceremonial when the configured auth scheme is signed_jwt — any valid, unexpired JWT for this runtime is accepted regardless of which nonce was issued, so a captured token can be replayed against any future challenge until it expires. That defeats the protection that challengeRequired is meant to provide and silently downgrades the security posture documented for the challenge flow.

Fix prompt: Have JWTAuthValidator.validate(auth:challenge:) enforce challenge binding when a non-nil challenge is supplied. Extend ARCPClaims with a nonce (and ideally jti) claim, require the JWT to carry the same nonce that the runtime issued, and reject the token with ARCPError.unauthenticated when it is missing or does not match. Document the new claim requirement in the JWT auth guide and in the inline doc comment on JWTAuthValidator, and add unit tests covering a happy path with a matching nonce, a rejected token with a missing nonce, a rejected token with the wrong nonce, and a token replayed against a second challenge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingseverity:highHigh severity issue

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions