Skip to content

Concept map· 6 connected terms

JSON Web Token(JWT)

Also called: JWT token · Signed JWT · JWS token

In one line
A compact signed token of three Base64url parts (header, claims, signature) that lets a server verify a request's identity without a database lookup.

Reviewed

What is a JSON Web Token (JWT)?

A JSON Web Token (JWT), defined in RFC 7519, is a compact way to carry signed claims between two parties. It has three parts separated by dots: a header saying which algorithm signed it, a payload of claims such as who the user is and when the token expires, and a signature over the first two parts. Each part is Base64url-encoded, so a JWT fits neatly in an HTTP header: Authorization: Bearer eyJhbGciOi….

Because the token is signed, a server can verify it with only the key, with no database lookup, and then trust the claims inside. Registered claims include iss (issuer), sub (subject), aud (audience), exp (expiry), nbf (not before), iat (issued at) and jti (token ID). Signing uses either a shared secret (HS256, which is HMAC-SHA256) or a key pair (RS256, ES256, EdDSA), where only the issuer holds the private key and anyone can verify with the public key, often published as a JWKS.

JWTs appear as OpenID Connect ID tokens, single-page-app sessions, service-to-service credentials and the API tokens of many SaaS platforms. They differ from Meta's Graph API access tokens, which are opaque strings that only Meta can interpret: you inspect those with Meta's debug_token endpoint, not by decoding them. A JWT is a format, not a protocol; OAuth 2.0 and OpenID Connect decide when tokens are issued, and a JWT is one way of writing them down.

Key takeaways

  • A JWT is header.payload.signature: three Base64url-encoded parts joined by dots.
  • The payload is only encoded, not encrypted: anyone holding the token can read its claims.
  • Verify the signature with an algorithm you pin, then check exp, nbf, iss and aud before trusting any claim.
  • HS256 signs and verifies with one shared secret; RS256 and ES256 sign with a private key and verify with a public one.
  • Meta's Graph API access tokens are opaque strings, not JWTs, so never try to decode them.

Edit a JWT and watch its signature break

3 / 3

Incoming request

As signed

POST /webhook HTTP/1.1

JWT signature (HS256): sha256=8b0e326ae2abbb9a1cdd7f4a85112b2897d3f84cff8520cec276880604af4c6e

127 bytes, exactly as the sender signed them.

Your server verifies

  1. Read the raw body and the headerUse the exact bytes received — 127 bytes, before any JSON parsing.
  2. Recompute the HMACHMAC-SHA256(secret, raw body)
  3. Compare the two digestsUse a constant-time comparison.
Server's HS256 signing keydemo-app-secret

JWT signature (HS256) (received) vs HMAC-SHA256(secret, raw body) (computed)

Receivedsha256=8b0e326ae2abbb9a1cdd7f4a85112b2897d3f84cff8520cec276880604af4c6e
Computedsha256=8b0e326ae2abbb9a1cdd7f4a85112b2897d3f84cff8520cec276880604af4c6e
Match — signature validThe body is byte-for-byte what the sender signed. Process the event.
The body is a real JWT signing input: Base64url header, a dot, Base64url claims. HS256 is HMAC-SHA256 over it. Change any character, as an attacker editing "role" would, and the signature fails (shown as hex here).

Match — signature valid. The body is byte-for-byte what the sender signed. Process the event.

How does a JSON Web Token (JWT) work?

  1. The issuer builds header and claims

    For example {"alg":"HS256","typ":"JWT"} and {"sub":"agent_42","role":"agent","iat":1792396800,"exp":1792397700}: a token valid for 15 minutes.

  2. It encodes and signs

    Header and payload are Base64url-encoded and joined with a dot. The signature is computed over that string (HMAC-SHA256 for HS256) and appended as the third part.

  3. The client sends it as a bearer token

    Usually in Authorization: Bearer <jwt>, or for browser sessions in an HttpOnly, Secure, SameSite cookie.

  4. The server verifies the signature

    It checks the signature with an algorithm fixed in its own configuration, never the one the token's header asks for, using the shared secret or the issuer's public key.

  5. Then it validates the claims

    Reject the token if exp has passed, nbf is in the future, or iss and aud are not the expected values. Only then use sub and any roles.

A JSON Web Token (JWT) in code

import jwt from "jsonwebtoken";
const SECRET = process.env.JWT_SIGNING_KEY; // 32+ random bytes, from your secret store
// Issue a 15-minute agent session token
export const issue = (agentId) =>
jwt.sign({ sub: agentId, role: "agent" }, SECRET, {
algorithm: "HS256",
expiresIn: "15m",
issuer: "https://dashboard.example.in",
audience: "inbox-api",
});
// Verify: pin the algorithm, check issuer and audience, allow a little clock skew
export function readSession(token) {
try {
return jwt.verify(token, SECRET, {
algorithms: ["HS256"], // never "none", never whatever the header says
issuer: "https://dashboard.example.in",
audience: "inbox-api",
clockTolerance: 30, // seconds
});
} catch {
return null; // expired, bad signature, wrong audience…
}
}

jwt.verify checks the signature and exp; issuer and audience are only checked if you pass them.

Common wrong turns

Common mistakes with a JSON Web Token (JWT)

  • Trusting the algorithm in the header

    Libraries that honour the token's own alg can be tricked into accepting none, or into verifying an RS256 token as HS256 with the public key as the secret. Pin the allowed algorithms in every verify call.

  • Not checking expiry and audience

    A token minted for another service, or one that expired last month, still carries a valid signature. Always validate exp, iss and aud.

  • Putting secrets in the payload

    Anyone holding the token can decode it. Keep passwords, API keys, OTPs and unnecessary personal data out of claims.

  • Long-lived tokens with no way to revoke

    A stolen 30-day JWT works for 30 days. Keep access tokens to minutes, refresh them server-side, and keep a deny-list of jti values for emergencies.

  • Storing JWTs in localStorage

    Any cross-site scripting bug can read localStorage. For browser sessions, prefer HttpOnly, Secure, SameSite cookies.

Key facts at a glance

A JSON Web Token (JWT): key facts and limits

Specification
RFC 7519 (2015); best practices in RFC 8725
Structure
header.payload.signature, Base64url, no padding
Registered claims
iss, sub, aud, exp, nbf, iat, jti
Common algorithms
HS256 (shared secret); RS256, ES256, EdDSA (key pairs)
Payload confidentiality
None: encoded, not encrypted (JWE encrypts)
Meta Graph API tokens
Opaque strings, not JWTs

Example: Two attacks on an agent session

A Gurugram SaaS company's shared-inbox dashboard issues 15-minute HS256 JWTs to support agents, with aud set to its inbox API and the agent's role as a claim. A penetration tester edits a captured token's role from "agent" to "admin" and re-encodes it: the signature no longer matches and the API answers 401. He then sends a token whose header says alg: none; verification fails because the server accepts only HS256. Meanwhile, the service that calls the WhatsApp Cloud API holds an opaque Meta system user token in a vault, never in the browser and never inside a JWT.

Why does a JSON Web Token (JWT) matter?

JWTs are the default session and service token in many stacks, including the dashboards and internal APIs built around WhatsApp messaging, and their failure modes are well known: unchecked expiry, accepted alg: none, confusion between HS256 and RS256, secrets left in a readable payload. Each has caused real authentication bypasses. Getting verification right once, in a shared library every service uses, protects all of them.

Good practice

Best practices for a JSON Web Token (JWT)

  • Generate HS256 keys from at least 32 random bytes: a short secret can be cracked offline from any captured token.
  • Publish a JWKS with key IDs (kid) so RS256 or ES256 signing keys can be rotated without downtime.
  • Keep claims minimal: a subject ID and roles are usually enough, and the rest can be looked up server-side.
  • Inspect Meta access tokens with the Graph API debug_token endpoint rather than trying to decode them.

JWT FAQs

Is a JWT encrypted?+
No. A standard signed JWT is only Base64url-encoded, so anyone with the token can read the claims. Encryption needs JWE, a separate format; otherwise keep sensitive data out of the payload.
What is the difference between HS256 and RS256?+
HS256 signs and verifies with one shared secret; RS256 signs with a private key and verifies with a public key. Use RS256 or ES256 when many services verify tokens but only one should issue them.
Are WhatsApp Cloud API access tokens JWTs?+
No. Meta's Graph API access tokens are opaque strings. You cannot decode them; check their permissions and expiry with the debug_token endpoint instead.
How do I revoke a JWT before it expires?+
You cannot change a token already issued, so keep lifetimes short. For emergencies, keep a deny-list of token IDs (`jti`) or rotate the signing key, which invalidates every token signed with it.

See InfiQ pricing

Try it on the official WhatsApp Business API

InfiQ is an official Meta Business Partner. Start a 7-day free trial — no card needed — and see this running on your own number.