Verify JWT signature (HMAC)
Paste a JWT and the secret it was signed with. The Web Crypto API runs the HMAC verification locally — instant, private, no upload.
Why signature verification matters
A JWT's first two segments (header, payload) are just base64-encoded JSON. Anyone with a JWT can decode them — that's not a security hole, it's the design. The signature is what makes the token trustworthy: only someone with the correct key can produce a valid signature for a given header+payload.
A receiver who skips signature verification is treating attacker- controlled JSON as authoritative. That's a critical bug. Always verify.
Algorithms this tool supports
| Algorithm | Hash | Use case |
|---|---|---|
HS256 | SHA-256 | Most common; symmetric, secret can be any UTF-8 string ≥ 256 bits |
HS384 | SHA-384 | Higher security margin; rare |
HS512 | SHA-512 | Highest HMAC security; rare |
What "valid" means
A "VALID" result means: the JWT was signed by someone who knew the secret you provided, and neither the header nor the payload has been modified since signing.
It does not mean:
- The token isn't expired (check
expseparately). - The token was issued by who you think (check
iss). - The token was meant for your service (check
aud). - The user the token represents is still allowed access (check your DB).
A complete authentication check verifies signature and claims
and business rules. Most JWT libraries do this for you when
you call jwt.verify(token, secret) with the right options.
Common verification failures
- Wrong secret. Most common. Double-check the secret matches what your auth service uses to sign. Watch for trailing whitespace, encoding differences (UTF-8 vs UTF-16), or pre-shared base64 secrets that need to be decoded first.
- Tampered token. Someone changed a single character in the header or payload. The signature no longer matches.
- Wrong algorithm. Your service signs HS256 but the JWT says
"alg": "HS512"(or vice-versa). The hash computation differs; signatures won't match. Always validate the alg field on the receiver side. - Re-encoded segments. If your token went through a system that decoded and re-encoded the JSON segments, the byte-level signature input may differ. Verifiers compare the original base64 strings, not the JSON; round-tripping breaks them.
Programmatic equivalents
// Node.js with jose
import { jwtVerify } from "jose";
import { TextEncoder } from "util";
const secretKey = new TextEncoder().encode(secret);
const { payload } = await jwtVerify(token, secretKey, {
algorithms: ["HS256"],
});
// Python with PyJWT
import jwt
payload = jwt.decode(token, secret, algorithms=["HS256"])
// Go with golang-jwt
import "github.com/golang-jwt/jwt/v5"
token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (any, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected method")
}
return []byte(secret), nil
}) Always specify expected algorithms
Every snippet above passes algorithms: ["HS256"] (or
similar) explicitly. Never let the JWT's own header decide which
algorithm to verify with. A famous class of bugs (CVE-2015-2951
and friends) was: server signs with RS256, attacker forges a JWT with
alg: HS256 and the public key as the HMAC secret. The
server, trusting the alg in the header, used the public key to verify
the HMAC — and accepted the attacker's forged token.
This tool's UI doesn't have that bug because the verifier is HMAC-only and reads the alg from the JWT only to pick the right hash size — but in your own code, always pin the algorithm.
FAQ
Why HMAC only?
HMAC (HS256/384/512) uses a single shared secret. Verification needs the secret and the JWT — that's it. RSA / ECDSA / EdDSA need a public key in PEM or JWK format, plus algorithm-specific Web Crypto setup. The MVP supports HMAC because it's the most common in symmetric internal-API setups.
How does HMAC verification work?
The verifier computes HMAC-SHA256(header + '.' + payload, secret) and compares it byte-for-byte with the JWT's signature segment. Match = valid. Mismatch = either wrong secret, or someone tampered with the header or payload.
What if my JWT uses RS256?
This tool will tell you the algorithm isn't supported. For RS256/384/512 or ES256/384/512 verification, use a library on the server: jose (JS), PyJWT (Python), github.com/golang-jwt/jwt (Go), etc. We're considering adding RSA verification — vote via the email signup.
Is the secret stored anywhere?
No. The secret is held in JavaScript memory just long enough to import into the Web Crypto HMAC key, run verify, and discard. Nothing is sent over the network or persisted. Verify by checking DevTools → Network and Storage tabs after pasting.