JWT for Beginners: A Complete Guide to JSON Web Tokens
If you have ever logged into a web app and seen a long string of characters in your browser's developer tools, you have probably encountered a JWT. This guide explains what they are, how they work, and how to use them safely.
What Is a JWT?
A JSON Web Token (JWT) is a compact, URL-safe string that carries claims (statements about a user) between parties. JWTs are commonly used for authentication: after you log in, the server gives you a JWT, and you send it with every subsequent request.
A JWT looks like this:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEyM30.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cIt has three parts separated by dots: header, payload, and signature.
The Three Parts Explained
Header
{ "alg": "HS256", "typ": "JWT" }Tells the verifier which algorithm was used to sign the token.
Payload (Claims)
{ "userId": 123, "role": "user", "exp": 1712345678 }Contains data about the user. Standard claims include sub (subject), iat (issued at), and exp (expiration). Never put sensitive data in the payload — it is only base64-encoded, not encrypted.
Signature
The signature proves the token has not been tampered with. It is computed as:
HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)Only someone with the secret can produce a valid signature.
How JWT Login Works
1. User submits username and password
2. Server validates credentials
3. Server creates a JWT signed with its secret and returns it
4. Client stores the JWT (cookie or memory)
5. Client sends Authorization: Bearer with each request
6. Server verifies the signature and reads the claims — no database lookup needed
This stateless design scales well but requires careful secret management.
Generating Your First Secret
Before you can sign tokens, you need a secret key. Open the JWT Secret Generator, click Generate, and copy the 256-bit key.
Store it as an environment variable:
JWT_SECRET=your-64-char-hex-keySigning a Token (Node.js)
const jwt = require('jsonwebtoken');
const token = jwt.sign(
{ userId: 123, role: 'user' },
process.env.JWT_SECRET,
{ algorithm: 'HS256', expiresIn: '1h' }
);Or build one visually with the JWT Encoder.
Validating a Token
const payload = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
});Paste any token into the JWT Validator to check its signature and expiration during development.
Registered vs Custom Claims
JWT defines standard claims (registered claims) that libraries recognize:
| Claim | Name | Purpose |
|---|---|---|
| `sub` | Subject | User ID or identifier |
| `iss` | Issuer | Who created the token |
| `aud` | Audience | Intended recipient service |
| `exp` | Expiration | Unix timestamp when token expires |
| `iat` | Issued At | When the token was created |
| `nbf` | Not Before | Token not valid before this time |
You can add custom claims like role, permissions, or tenantId. Keep the payload small — JWTs are sent with every request, and large payloads increase bandwidth and latency.
Where to Store Tokens on the Client
| Storage | XSS Risk | CSRF Risk | Recommendation |
|---|---|---|---|
| localStorage | High | Low | Avoid for auth tokens |
| sessionStorage | High | Low | Avoid for auth tokens |
| Memory (variable) | Medium | Low | OK for SPA during session |
| httpOnly cookie | Low | Medium | Best default — add CSRF protection |
For single-page apps, the industry trend is httpOnly Secure SameSite cookies for refresh tokens and short-lived access tokens in memory.
Common Beginner Mistakes
1. Trusting decoded tokens without verification — jwt.decode() does not check the signature
2. Storing sensitive data in the payload — JWTs are readable by anyone who has the token
3. No expiration — tokens without exp remain valid forever
4. Using alg: none — never accept unsigned tokens in production
5. Sharing secrets between environments — use separate keys for dev, staging, and prod
Building a Complete Auth Flow
Here is a minimal end-to-end flow you can test today:
1. Generate a 256-bit secret with the JWT Secret Generator
2. Set JWT_SECRET in your .env file
3. Create a login endpoint that signs a token after credential validation
4. Protect API routes with middleware that calls jwt.verify()
5. Return 401 for missing or invalid tokens; 403 for valid tokens with insufficient permissions
// Login handler (simplified)
app.post('/login', async (req, res) => {
const user = await authenticate(req.body.email, req.body.password);
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
const token = jwt.sign(
{ sub: user.id, role: user.role },
process.env.JWT_SECRET,
{ algorithm: 'HS256', expiresIn: '1h' }
);
res.json({ token });
});Test the full loop with the JWT Encoder and JWT Validator before wiring up your database.
Framework Integration Notes
Most web frameworks have JWT plugins or middleware:
- Express:
express-jwtor manual middleware withjsonwebtoken - Fastify:
@fastify/jwt - Django:
djangorestframework-simplejwt - Spring Boot:
spring-boot-starter-oauth2-resource-server
Regardless of framework, the validation rules are the same: verify signature, check expiration, validate issuer and audience.
When JWTs Are Not the Right Choice
JWTs excel at stateless authentication between services, but they are not always ideal:
- Session-heavy apps with immediate revocation needs may prefer server-side sessions
- Opaque tokens stored in Redis give instant revocation without waiting for expiry
- First-party mobile apps sometimes use refresh-token-only flows with short access tokens
Understand the tradeoffs before committing to JWTs for your entire auth architecture.
HS256 vs RS256
- HS256 — one shared secret signs and verifies. Simple, fast, good for monoliths.
- RS256 — private key signs, public key verifies. Better for microservices.
See the HS256 vs RS256 comparison for a quick decision, or read the full in-depth guide when you are ready to choose.
Security Essentials
1. Use a 256-bit random secret — not a password
2. Set short expiration times (15–60 minutes)
3. Store secrets in environment variables, not source code
4. Always verify signatures — never just decode
5. Use httpOnly cookies instead of localStorage
See the JWT Best Practices Checklist for the complete list.
Free Tools to Practice
| Tool | What It Does |
|---|---|
| [JWT Secret Generator](/tools/jwt-secret-generator) | Create signing keys |
| [JWT Encoder](/tools/jwt-encoder) | Build and sign tokens |
| [JWT Validator](/tools/jwt-validator) | Verify signatures and inspect claims |
| [Hash Generator](/tools/hash-generator) | Understand SHA-256 used in HMAC |