What is JWT (JSON Web Token)? Complete Guide with Examples, Verification, and Security Best Practices
If you’ve ever logged into a website and stayed signed in without re-entering your password every few minutes, you’ve already benefited from tokens. One of the most common and powerful types of these tokens is the JWT, or JSON Web Token.
JWTs have become a backbone of modern web authentication. They’re small, fast, secure, and designed to work seamlessly across APIs, mobile apps, and cloud services. But despite their popularity, many developers still misunderstand how JWTs work—or worse, use them in unsafe ways.
This guide will help you understand JWTs completely: how they’re built, how to decode and verify them, and what security practices to follow to keep your applications safe.
By the end, you’ll not only know how JWTs function but also how to inspect them using tools like the JWT Decoder and Verifier.
Why JWT Matters in Modern Web Development
Every application that deals with user login or API access needs a way to verify identity. Traditionally, this was done using sessions—a server would store your login state, and your browser would hold a small cookie that points to that session.
While sessions still work, they don’t scale well across multiple servers or microservices. JWTs were designed to solve that problem. Instead of storing the user’s state on the server, JWTs carry all necessary identity data inside the token itself.
This design makes JWTs stateless, which means:
- You don’t need to maintain a session database.
- Authentication works smoothly across APIs and cloud apps.
- They’re ideal for mobile, web, and IoT devices that connect through REST APIs.
How JWT Works in Simple Terms
Imagine you run a website that lets users log in. When a user successfully enters their username and password:
- The server creates a JWT containing information about the user (like their user ID or role).
- The JWT is signed using a secret key or a private key.
- The token is sent to the user’s browser or app, usually stored in a cookie or local storage.
- On each subsequent request, the token is sent back to the server.
- The server verifies the token’s signature to ensure it wasn’t modified.
- If valid, the request is authorized; otherwise, it’s rejected.
That’s it—no need to store sessions or query a database for every request.
JWT in the Real World
JWTs are used almost everywhere today:
- When logging in with Google or GitHub
- When accessing APIs from your frontend app
- When mobile apps store your login session
- In OAuth 2.0 and OpenID Connect protocols
- In Single Sign-On (SSO) systems for enterprise logins
Their compact format makes them perfect for passing through URLs, HTTP headers, or even QR codes.
A typical JWT looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaSIsImlhdCI6MTUxNjIzOTAyMn0
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Even though it looks like a random mess of letters, this token actually contains readable information. The first two parts are just Base64URL-encoded JSON, which means you can decode them easily.
You can try decoding one right now using the JWT Decoder and Verifier tool.
Just paste the token into the input box and hit Decode—you’ll instantly see the header, payload, and signature.
The Three Parts of a JWT
A JWT has three main components, separated by dots (.):
header.payload.signature
- Header
The header describes how the token is signed. For example:{ "alg": "HS256", "typ": "JWT" }alg= Algorithm (e.g., HS256 means HMAC with SHA-256)typ= Token type (always “JWT”)
- Payload
The payload contains the actual data or claims. These can be:- Registered claims: predefined ones like
iss(issuer),exp(expiration),sub(subject) - Public claims: information shared with other systems, like user ID or roles
- Private claims: custom fields your app defines, such as
plan: premium
{ "sub": "1234567890", "name": "Ali", "role": "admin", "iat": 1696580400, "exp": 1696584000 } - Registered claims: predefined ones like
- Signature
This is the most important part—it ensures that the token wasn’t tampered with.
The signature is generated like this:HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
If someone changes even one character in the header or payload, the signature will no longer match.
Example of Encoding and Decoding a JWT
Let’s look at a simple Node.js example of how JWTs are created and verified.
Creating a Token
// npm install jsonwebtoken
const jwt = require("jsonwebtoken");
const secret = "my_secret_key";
const token = jwt.sign(
{ userId: 42, name: "Ali" },
secret,
{ expiresIn: "1h" }
);
console.log("JWT:", token);
Verifying a Token
try {
const decoded = jwt.verify(token, secret);
console.log("Verified payload:", decoded);
} catch (err) {
console.error("Invalid or expired token:", err.message);
}
This example shows how the same secret key used to sign the token can verify it.
If anyone modifies the token, the verification fails immediately.
Now that you know the structure of a JWT, let’s take a closer look at how decoding and verification actually work. Even though JWTs look complicated, they’re built on simple principles of Base64URL encoding and digital signatures.
How JWT Decoding Works
A JWT is made up of three Base64URL-encoded strings separated by dots. These strings represent JSON data, and decoding them is straightforward.
For example, the following JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaSIsImlhdCI6MTUxNjIzOTAyMn0
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
When decoded, becomes:
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"sub": "1234567890",
"name": "Ali",
"iat": 1516239022
}
Signature:
A long string of characters that verifies authenticity.
You can decode these manually or use your own tool.
For example, you can try your JWT Decoder and Verifier — it automatically splits and decodes each section, showing the header and payload in readable JSON format.
Understanding Base64URL Encoding
JWTs use Base64URL encoding, which is a slightly modified version of standard Base64.
The difference is that certain characters are replaced to make it URL-safe:
+becomes-/becomes_- No padding
=characters at the end
This is important because JWTs are often passed through HTTP headers and URLs, where special characters can cause problems.
Example:
Standard Base64:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9==
Base64URL version:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
The Process of Verification
Decoding a JWT only reveals its contents—it doesn’t prove that it’s genuine.
To confirm that the token is valid and untampered, the signature must be verified.
Here’s what happens during verification:
- The server takes the received JWT and splits it into the three parts (header, payload, signature).
- It then recreates the signature using:
algorithm(header + "." + payload, secret) - The recreated signature is compared with the one in the token.
- If they match, the token is valid. If not, it’s either been modified or was signed with the wrong key.
If the JWT also has an exp (expiration) claim, the verification step will fail if the current time is past the expiration timestamp.
Symmetric vs Asymmetric JWT Signing
JWTs can be signed in two main ways:
1. Symmetric Signing (HMAC)
Algorithms like HS256, HS384, and HS512 use the same secret key for both signing and verification.
Example:
const token = jwt.sign({ user: "Ali" }, "secret123", { algorithm: "HS256" });
This method is simple but requires the same key to be kept secret on both ends.
2. Asymmetric Signing (RSA / ECDSA)
Algorithms like RS256 or ES256 use a private key to sign and a public key to verify.
This is more secure for distributed systems because you never share the private key.
Example:
const token = jwt.sign({ user: "Ali" }, privateKey, { algorithm: "RS256" });
jwt.verify(token, publicKey);
Large-scale platforms (like Google, Auth0, or AWS Cognito) prefer asymmetric signing for better key control and rotation.
Example: Verifying a JWT in Python
Here’s a simple demonstration using Python:
import jwt
secret = "my_secret"
# A token generated elsewhere
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiQWxpIn0.8abM4hJfqT1iQpBaGgJvYqPwNJDnlb1bJK7zV6UV9VQ"
try:
payload = jwt.decode(token, secret, algorithms=["HS256"])
print("Token is valid. Payload:", payload)
except jwt.ExpiredSignatureError:
print("Token expired.")
except jwt.InvalidTokenError:
print("Invalid token.")
The jwt.decode method automatically handles both decoding and verification.
Common JWT Claims and Their Purpose
| Claim | Description |
|---|---|
| iss | Issuer — identifies the server that issued the token |
| sub | Subject — represents the user or entity |
| aud | Audience — who the token is intended for |
| exp | Expiration time (UNIX timestamp) |
| nbf | Not before — time before which the token is invalid |
| iat | Issued at — when the token was created |
| jti | JWT ID — a unique identifier for the token |
Example payload with standard claims:
{
"iss": "https://api.0xgz.com",
"sub": "user_123",
"aud": "0xgz-client",
"exp": 1696620000,
"iat": 1696616400
}
Using these claims correctly helps prevent common issues like token reuse or audience confusion.
How Expiration Works
The exp (expiration) claim defines how long the token is valid. When a JWT is used after this time, verification fails automatically.
Example:
"exp": 1696620000
This timestamp corresponds to a specific date and time.
Best practice: Keep access tokens short-lived (e.g., 15–60 minutes) and use refresh tokens for renewing sessions.
Why JWTs Are Trusted
JWTs aren’t secure because of encryption (most aren’t encrypted).
They’re secure because of digital signatures. The signature ensures that the token’s contents haven’t been modified since it was issued.
If someone tries to edit the payload—say, changing "role": "user" to "role": "admin"—the signature will no longer match, and the token will be rejected.
You can experiment with this by using your own JWT Decoder and Verifier tool:
- Decode a valid token.
- Change something in the payload.
- Try verifying again—you’ll see it instantly fails.
That’s the magic of digital signatures in JWTs.
As powerful as JWTs are, they can also become dangerous when implemented incorrectly. A single mistake—like using the wrong algorithm or storing tokens insecurely—can expose an entire application to attackers.
Understanding the most common JWT-related security risks is the key to using them safely. Let’s explore these risks and how to protect your system from them.
1. The “None” Algorithm Vulnerability
One of the earliest and most famous JWT vulnerabilities involved the use of the “none” algorithm.
In the JWT standard, the header specifies which algorithm is used for signing. For example:
{
"alg": "HS256",
"typ": "JWT"
}
If a developer’s JWT library doesn’t properly check this value, an attacker can change it to "none":
{
"alg": "none",
"typ": "JWT"
}
Then the attacker removes the signature and sends:
header.payload.
Some older JWT libraries would accept this as a valid token—essentially meaning no verification at all.
This allowed attackers to impersonate any user.
How to Prevent It
- Always use modern, maintained JWT libraries.
- Explicitly define allowed algorithms during verification. For example:
jwt.verify(token, secret, { algorithms: ["HS256"] }); - Reject tokens with
alg: noneimmediately.
2. Algorithm Confusion Attacks
JWTs can be signed using symmetric (e.g., HS256) or asymmetric (e.g., RS256) algorithms.
If your server doesn’t enforce which algorithm to expect, attackers can trick it by switching algorithms.
For example:
- A token originally signed with RS256 (private/public key pair).
- Attacker modifies the header to HS256 and re-signs it using the public key (which the server might mistakenly use as the shared secret).
How to Prevent It
- Always specify the algorithm during verification.
- Never let the client choose the algorithm dynamically.
- Keep private and public keys in secure, separate locations.
- If using asymmetric signing, ensure your public key is read-only and never reused as a shared secret.
3. Token Replay Attacks
A replay attack happens when a valid token is captured and reused by an attacker.
For example, if someone intercepts a JWT from an insecure HTTP request, they can reuse it to impersonate the original user.
Since JWTs are stateless, the server can’t simply “revoke” them like sessions.
How to Prevent It
- Always use HTTPS to prevent token sniffing.
- Include a short expiration (
exp) claim—e.g., 15 minutes. - Use refresh tokens to issue new access tokens safely.
- Optionally, store JWT identifiers (
jti) in a blacklist when users log out.
4. Storing Tokens Insecurely on the Client
Many frontend developers make the mistake of storing JWTs in localStorage or sessionStorage.
The problem? These locations are accessible via JavaScript—so if your site ever suffers from an XSS attack, an attacker can easily read and steal the token.
How to Prevent It
- Store JWTs in HTTP-only cookies instead of localStorage.
These cookies aren’t accessible from JavaScript, making them much safer. - Use the
SecureandSameSiteflags for cookies:Set-Cookie: token=abc123; HttpOnly; Secure; SameSite=Strict - Implement Content Security Policy (CSP) headers to reduce XSS risks.
5. Missing Expiration or Long-Lived Tokens
If a JWT never expires, it’s like giving someone permanent access to your system. Even if you fix a vulnerability later, old tokens will still work.
How to Prevent It
- Always include an
expclaim:"exp": 1696620000 - Use short-lived access tokens (e.g., 15–60 minutes).
- Use refresh tokens to issue new ones without requiring users to log in again.
- Rotate refresh tokens periodically to minimize misuse.
6. Weak Secrets and Poor Key Management
For tokens signed with HS256, the entire system’s security depends on the secret key.
If your secret is weak, predictable, or hard-coded in your source code, attackers can brute-force it.
How to Prevent It
- Use a long, random secret (at least 256 bits).
- Store secrets in environment variables or a secure vault.
- Rotate secrets periodically.
- Avoid embedding keys in frontend code or Git repositories.
Example of generating a strong key in Node.js:
const crypto = require("crypto");
console.log(crypto.randomBytes(64).toString("hex"));
7. JWTs Are Not Encrypted by Default
Many developers assume that JWTs are automatically encrypted because they look like random text.
But most JWTs (the ones that start with eyJ...) are only encoded, not encrypted. Anyone who gets the token can decode and read its payload.
How to Prevent It
- Never put sensitive data (like passwords or personal info) inside the JWT payload.
- If you must store sensitive data, use JWE (JSON Web Encryption) instead of plain JWT.
- Always send tokens over HTTPS to prevent exposure in transit.
8. Overusing JWTs When Not Needed
JWTs are powerful, but they’re not always necessary.
For simple web apps or monolithic systems, traditional session-based authentication may be safer and easier to manage.
JWTs shine in distributed or API-based architectures, but overuse can add unnecessary complexity and risk.
When to Use JWTs
- When you need stateless authentication between services.
- When you’re working with APIs, microservices, or mobile apps.
- When you need single sign-on (SSO) across domains.
When Not to Use JWTs
- For simple, server-rendered websites where sessions work fine.
- When you don’t need token sharing across services.
9. The Importance of Audience and Issuer Validation
Attackers can sometimes use a valid JWT from one system to access another if you don’t validate the iss (issuer) and aud (audience) claims.
How to Prevent It
When verifying tokens, check these fields explicitly:
jwt.verify(token, secret, {
algorithms: ["HS256"],
issuer: "https://api.0xgz.com",
audience: "0xgz-client"
});
This ensures the token was created for your specific application and not reused elsewhere.
10. Revocation Challenges
Unlike sessions, JWTs can’t be easily revoked because the server doesn’t store them.
If a user’s token is stolen, it remains valid until it expires—unless you implement your own revocation mechanism.
How to Prevent It
- Keep tokens short-lived.
- Maintain a token blacklist using the
jti(JWT ID) claim. - For critical actions, always re-verify user credentials or request re-authentication.
Summary of Best Defenses
| Threat | Prevention |
|---|---|
| Algorithm confusion | Enforce a specific algorithm |
| Replay attacks | Use HTTPS and short expiration |
| Token theft | Store tokens in HTTP-only cookies |
| Weak keys | Use long, random secrets |
| Data exposure | Avoid storing sensitive info in payload |
| Token misuse | Validate iss, aud, and exp claims |
By now, you know how JWTs work, how they’re structured, and what security pitfalls to avoid.
This final part focuses on how to use JWTs effectively and safely in production, along with some real-world examples and modern implementation advice.
Best Practices for Using JWT in Production
1. Always Use HTTPS
Never send tokens over unencrypted HTTP connections.
JWTs often contain identifiers or claims that can be misused if intercepted.
Using HTTPS prevents man-in-the-middle attacks and ensures your tokens remain private.
2. Keep Tokens Short-Lived
Access tokens should expire quickly, ideally within 15 minutes to 1 hour.
Short-lived tokens reduce damage if one is stolen.
To maintain seamless user sessions, issue refresh tokens that can request new access tokens when needed.
Example structure:
- Access token: Valid for 15 minutes
- Refresh token: Valid for 7 days, stored securely (e.g., in an HTTP-only cookie)
3. Store Tokens Securely
If you’re building a web app:
- Store tokens in HTTP-only, Secure cookies, not localStorage.
- Use the
SameSite=Strictflag to prevent CSRF attacks. - Avoid embedding tokens in URLs or HTML.
For mobile or desktop apps:
- Store tokens in secure system storage (like Keychain or Keystore), not in plain text.
4. Use Strong Secrets and Key Rotation
For symmetric algorithms (like HS256), always use a long, random secret key.
For asymmetric algorithms (like RS256), rotate your private keys periodically and protect them with access control systems or vaults (such as AWS KMS, Google Cloud KMS, or HashiCorp Vault).
Rotation ensures that if a key ever leaks, older tokens can be invalidated without disrupting all users.
5. Validate All Claims
When verifying JWTs, validate key fields like:
- iss (Issuer) → Must match your domain
- aud (Audience) → Must match your app or API client
- exp (Expiration) → Must not be in the past
- nbf (Not before) → Ensures token isn’t used prematurely
Ignoring these checks can let attackers reuse tokens in unintended contexts.
6. Avoid Storing Sensitive Data in Payloads
Remember: JWT payloads are only encoded, not encrypted.
Anyone who decodes a JWT can see its content. Avoid storing:
- Passwords or personal details
- Credit card numbers
- Private keys or API secrets
If you must transmit sensitive data, consider using JWE (JSON Web Encryption) instead of plain JWT.
7. Log and Monitor Token Activity
In production environments, log:
- Token creation and expiration events
- Failed verifications
- IP and device information for suspicious activity
These logs help detect abuse and trace potential compromises quickly.
8. Use JWT Libraries Carefully
Avoid writing your own JWT handling logic. Always use well-tested libraries from trusted sources such as:
jsonwebtoken(Node.js)PyJWT(Python)auth0/java-jwt(Java)jwt-go(Golang)
Each library provides secure defaults, expiration handling, and claim validation.
JWT in Real-World Applications
JWTs have become a core part of modern authentication and authorization systems. Let’s look at where and how they’re used today.
1. Single Sign-On (SSO)
When you log in once and gain access to multiple services, JWTs often power that system.
For example, an organization may use a single identity provider that issues a JWT.
Each connected app verifies the token, reads the claims, and grants access—without needing to ask for credentials again.
2. RESTful APIs and Microservices
In distributed systems, every microservice needs a way to authenticate incoming requests.
JWTs are ideal because they’re stateless and compact.
Each API only needs the public key to verify a token—no shared session store or database lookup is required.
This makes JWTs perfect for:
- Serverless environments (AWS Lambda, Cloudflare Workers)
- API gateways and load balancers
- Microservice-to-microservice communication
3. Mobile and Desktop Applications
Mobile apps often store JWTs locally after login to maintain a user session.
Each request to the backend includes the token in the authorization header:
Authorization: Bearer <token>
Because mobile devices frequently go offline or change networks, JWTs allow for lightweight and resilient session management without constant re-authentication.
4. OAuth 2.0 and OpenID Connect
Most OAuth 2.0 and OpenID Connect providers (Google, GitHub, Facebook, Microsoft) rely on JWTs for access and ID tokens.
When you “Sign in with Google,” the system issues a JWT that your app can verify to confirm identity and permissions.
This is one of the most standardized and secure uses of JWTs today.
5. Temporary Access and One-Time Links
JWTs are great for temporary authorization, such as:
- Password reset links
- File download links
- Invite systems
Because they can expire automatically, they make it easy to grant short-term, controlled access to resources.
Example: Building a Secure JWT Flow
A secure authentication flow typically looks like this:
- Login Request: User provides credentials.
- Token Issuance: Server verifies credentials and returns a short-lived JWT and a refresh token.
- Accessing Resources: The client includes the JWT in the
Authorizationheader. - Token Verification: Each protected route verifies the JWT before serving the request.
- Token Renewal: When the JWT expires, the client uses the refresh token to request a new one.
- Logout or Revocation: The refresh token is blacklisted, ending the session securely.
This flow balances performance, scalability, and safety.
When Not to Use JWT
Although JWTs are popular, they’re not ideal for every project.
If your app:
- Only runs on one server
- Doesn’t expose APIs
- Uses simple form-based login
Then traditional session-based authentication might be simpler and more secure.
JWTs shine in distributed environments, but they introduce complexity that isn’t always necessary for small projects.
Testing and Debugging JWTs
When building or debugging authentication systems, it’s helpful to inspect tokens visually.
That’s where your tool comes in handy.
Developers can use the JWT Decoder and Verifier to:
- Decode tokens without exposing them to third-party sites
- View headers, payloads, and signatures instantly
- Verify validity using their own secret or public key
This is especially useful when troubleshooting issues like invalid signatures, expired tokens, or incorrect algorithms.
Future of JWTs
JWTs have already become a foundational technology in authentication, but their evolution continues.
We’re now seeing more secure extensions like:
- JWE (JSON Web Encryption) for encrypted payloads
- JWS (JSON Web Signature) for improved verification flexibility
- JWT Profile for OAuth 2.0 for standardized security claims
As systems become more distributed and privacy-focused, JWTs will likely remain a central part of how identity and trust are managed online.
Final Thoughts
JWTs are more than just tokens—they’re a way to bring secure, scalable, and stateless authentication to modern web and mobile systems.
When implemented carefully, they offer simplicity, speed, and strong protection for APIs and users alike.
But like all powerful tools, they demand respect. Use strong keys, short expirations, and validated claims, and never assume a JWT is safe just because it looks complex.
If you want to explore JWTs further or experiment with decoding and verification, try the JWT Decoder and Verifier on your own site.
It’s a simple, safe way to learn how tokens work—right in your browser.
Frequently Asked Questions (FAQ) about JWT (JSON Web Token)
1. What is a JWT and why is it used?
A JWT (JSON Web Token) is a compact, URL-safe token used to securely transmit information between parties. It’s mainly used for authentication and authorization, allowing servers and APIs to verify user identity without maintaining session data.
2. How is a JWT structured?
A JWT consists of three parts separated by dots (.):
Header – contains the algorithm and token type.
Payload – includes claims (user data or permissions).
Signature – verifies the token hasn’t been tampered with.
Example:
xxxxx.yyyyy.zzzzz
3. Is a JWT encrypted?
No. JWTs are encoded, not encrypted. Anyone can decode and read the payload using a Base64 decoder.
If you need confidentiality, use JWE (JSON Web Encryption) instead of plain JWT.
4. What are the common algorithms used in JWT?
The most common signing algorithms include:
HS256 (HMAC-SHA256): Uses a shared secret key.
RS256 (RSA-SHA256): Uses a private/public key pair.
RS256 is generally preferred in production for stronger security and scalability.
5. Where should I store JWTs in my application?
For web apps, store tokens in HTTP-only, Secure cookies to prevent access by JavaScript and reduce XSS risks.
Avoid using localStorage or embedding tokens in URLs.
6. How long should a JWT be valid?
JWTs should have short lifespans, typically 15–60 minutes.
Pair them with refresh tokens that can issue new access tokens when needed.
Short expiry limits damage if a token is ever leaked.
7. How can I invalidate or revoke a JWT?
Because JWTs are stateless, you can’t directly “delete” them.
To revoke a token:
Maintain a blacklist or revocation list on your server.
Rotate signing keys so old tokens become invalid.
Expire refresh tokens on logout or suspicious activity.
8. Can JWTs be used for Single Sign-On (SSO)?
Yes. JWTs are commonly used in SSO (Single Sign-On) systems.
They allow users to authenticate once and access multiple applications securely without re-entering credentials.
9. What are common mistakes when using JWTs?
Some common mistakes include:
Storing sensitive data (like passwords) inside payloads
Using long-lived tokens without refresh mechanisms
Not validating claims like iss, aud, or exp
Transmitting tokens over insecure HTTP
Avoiding these mistakes ensures stronger JWT security.
10. How can I verify and debug a JWT safely?
You can verify and decode JWTs using your own local tools or trusted platforms.
A great option is the JWT Decoder and Verifier — it lets you:
Decode JWTs offline
Verify signatures
Inspect headers and payloads
Without sending tokens to third-party servers.
