JWTs Explained
JWTs come up everywhere in modern web auth. You will see them on every conference talk, in every tutorial, in every job description. This page explains, in plain English, what they actually are, what makes them useful, and why Dashify uses them in some places but not others.
What a JWT is
JWT is short for JSON Web Token. A JWT is a small string that looks like three blocks of garbled text separated by dots:
xxxxxx.yyyyyyyy.zzzzzz
Each block is base64-encoded (which makes it look like garbage but is reversible). When you decode them you get:
- Header — what algorithm signed this token.
- Payload — the actual claims (who the token is about, when it was issued, when it expires, anything else you want).
- Signature — a cryptographic stamp that proves the token has not been altered.
The payload might say something like: "This token is for user 65f..., in tenant 63a..., issued at this timestamp, expires in fifteen minutes."
The signature is the magic. It is computed from the header + payload + a secret only the server knows. If anyone tries to alter the payload (changing the user id, extending the expiry), the signature stops matching and the token is rejected.
What makes JWTs useful
The big draw of JWTs is that they are self-contained. The server can verify a JWT without looking anything up in a database, because the token carries everything the server needs and the signature is verifiable in pure CPU.
That makes JWTs cheap. No database hit. No Redis hit. Just sign on issue, verify on every request. For high-throughput APIs that is appealing.
What makes JWTs awkward
The same property that makes them cheap is also their weakness: because the server does not look anything up, it cannot revoke a JWT before it expires.
If a JWT is leaked, every server that accepts it will keep accepting it until it expires. The only ways to revoke a JWT early are:
- Wait for it to expire (set short expiries, like 15 minutes).
- Maintain a blacklist (defeats the "no database lookup" point).
- Rotate the signing secret (invalidates every token, not just the leaked one).
For a session that should last hours, that is a tough trade. Dashify wants instant revocation when a user logs out — which is exactly what a server-side session in Redis gives you.
Where Dashify uses JWTs
Given the trade-offs, Dashify uses JWTs in two specific places where the trade is fine.
1. Socket.IO handshake
When the browser opens a WebSocket connection to the API for real-time features (chat, notifications, board updates), the connection needs to be authenticated. We could authenticate by inspecting the session cookie, but cookies behave awkwardly with WebSockets across some proxy configurations.
Instead, the browser asks the API for a short-lived JWT (15 minutes, single-use), opens the WebSocket with the JWT in a query parameter, and the Socket.IO middleware verifies the JWT once at handshake time. Once the connection is established, the JWT is no longer needed — the connection itself is the authentication.
The JWT in this flow is a signed envelope around the user id and tenant id. Even if it leaks, the worst an attacker can do is open a WebSocket as that user for the next 15 minutes — and then it expires.
2. Internal service-to-service tokens
Some background tasks (the worker calling back into the API, for example) authenticate with a JWT signed by a shared secret. These tokens are short-lived, scoped to a single operation, and live entirely inside Docker — they never travel to a browser.
What JWT does not do in Dashify
JWTs are not the primary authentication token. The session cookie is. JWTs are a tactical tool used in specific places where their properties fit; sessions are the strategic tool used everywhere else.
This is a deliberate choice. Many tutorials present JWTs as a replacement for sessions. They are not. They are a different tool with a different shape. Knowing when to reach for each is the point.
What a JWT looks like in transit
The signing algorithm
Dashify signs JWTs with HS256 — a symmetric algorithm where the same secret is used to sign and verify. That works because both sides of the verification (the API issuing tokens and the API verifying them) live in the same deployment with access to the same secret.
If we ever needed to issue tokens that a different party verifies, we would switch to RS256 (asymmetric — sign with private key, verify with public key) so we could distribute the public key without giving away the signing power. We do not need that today.
Common JWT mistakes (and how Dashify avoids them)
- Storing JWTs in
localStorage. Anything in localStorage is readable by JavaScript, so an XSS attack can steal the token. Dashify keeps JWTs in memory only — they never touch persistent browser storage. - Not setting an expiry. A JWT without an expiry is valid forever, which is terrifying. Every Dashify JWT has a tight expiry.
- Trusting the
algclaim from the header. Some libraries default toalg: noneif the header says so, allowing forged tokens. Dashify uses a library that requires the algorithm to be specified out-of-band — the header is informational, not authoritative. - Putting secrets in the payload. The payload is base64-encoded but not encrypted. Anyone with the token can read it. Dashify only puts non-sensitive ids and timestamps in the payload.
Key takeaways
- A JWT is a self-contained signed token that carries claims like user id and expiry.
- JWTs are cheap to verify (pure CPU, no database) but cannot be revoked early.
- Dashify uses JWTs only for short-lived, narrowly-scoped purposes (Socket.IO handshake, internal service tokens).
- The session cookie is the primary authentication mechanism because it can be revoked instantly.
- JWTs are a tool. Knowing when not to use them is the point.