Almost every modern API you integrate with hands you a token and expects it back on the next request. Very often that token is a JSON Web Tokens value, a JWT, and if you have ever pasted one into a debugger you will recognise it: three chunks of dense, url-safe text separated by two dots. It looks like encryption. It is not. Understanding the difference between what a JWT hides and what it merely protects is the whole point of this guide, because most JWT security incidents come from engineers who assumed the token was doing something it was never designed to do. If you build or consume integrations, a clear mental model of JWTs pays for itself the first time you have to decide where to store one or how to verify it. For where token-based authentication fits in the larger integration landscape, start with the enterprise system integration explained pillar.
The message up front: a JWT is signed, not encrypted, by default. Anyone who holds the token can read every claim inside it. What the signature guarantees is integrity and authenticity, that the claims were issued by someone holding the signing key and have not been altered since. Treat the payload as public, protect the token in transit and at rest, and never put a secret inside it.
1. What a JWT is
A JSON Web Token is a compact, self-contained way to carry a set of claims between two parties. A claim is simply a statement about a subject, for example "this user is identified as 12345", "this token expires at 3pm", or "this caller is allowed to read invoices". The format is defined by RFC 7519, and it builds directly on the JSON Web Signature specification, RFC 7515, which handles the actual signing. The two specifications together are what make a JWT more than just a blob of JSON: they define how the JSON is encoded, signed and verified so that independent systems can trust it without calling back to the issuer.
The word to hold onto is self-contained. A traditional session identifier is a meaningless random string; the server has to look it up in a store to discover who it belongs to. A JWT is the opposite. The claims travel inside the token itself, so a service that holds the signing key can validate the token and read the identity and permissions from it directly, with no database round trip. That property is what makes JWTs so popular for distributed systems, microservices and APIs, where the service checking the token is frequently not the service that issued it.
It also explains the shape of the token. Because the claims travel with every request, the format is deliberately compact and url-safe, so it can sit comfortably in an HTTP header, a query string or a cookie without needing extra encoding. That compactness is the "compact serialization" that RFC 7515 describes, and it is the form almost everyone means when they say JWT.
2. The three parts: header, payload, signature
A JWT in its usual form is three base64url-encoded segments joined by dots. The first segment is the header, the second is the payload, and the third is the signature. The two dots are literal separators, and each segment is independently decodable. The diagram below shows how a single token string breaks apart.
The header is a small JSON object that describes the token. Its most important field is alg, the signing algorithm, and it usually also carries typ set to "JWT". The payload is another JSON object holding the claims, the actual statements the token is asserting. The signature is a cryptographic value computed over the encoded header and payload together, using a key and the algorithm named in the header. Because the signature covers both of the first two segments, changing even a single character of the header or payload invalidates it. That is the mechanism that lets a receiver trust the contents.
A crucial point that trips people up: the header and payload are base64url-encoded, not encrypted. base64url is a reversible encoding, not a cipher. Anyone with the token can decode those two segments and read them in plain JSON. The signature is the only part that requires a key, and it protects the token from being altered, not from being read. If you need the contents to be secret as well as tamper-proof, you need a separate mechanism such as JSON Web Encryption or, far more commonly, transport-level protection with HTTPS.
Here is what the decoded payload of a small token might look like, shown in plain JSON with straight quotes:
"iss": "https://auth.example.com",
"sub": "user-12345",
"aud": "invoices-api",
"exp": 1893456000,
"iat": 1893452400,
"scope": "read:invoices"
}
Notice there are no passwords, no secrets and nothing you would mind a determined user reading. That is deliberate. The claims describe who the token is for and what it permits, and the signature is what stops anyone forging or editing them.
3. Standard claims
RFC 7519 defines a set of registered claim names that carry consistent meaning across systems. You are free to add your own custom claims, and most real tokens do, but the registered claims are the ones every JWT library understands and validates the same way. Using them correctly is what makes a token interoperable and safe. The seven you will meet most often are below.
| Claim | Name | Meaning |
|---|---|---|
| iss | Issuer | Who issued the token. Verify it matches the authority you expect. |
| sub | Subject | Who or what the token is about, typically the user or client identifier. |
| aud | Audience | Who the token is intended for. A service should reject tokens not addressed to it. |
| exp | Expiration Time | Numeric timestamp after which the token must be rejected. |
| nbf | Not Before | Numeric timestamp before which the token is not yet valid. |
| iat | Issued At | Numeric timestamp of when the token was created. |
| jti | JWT ID | A unique identifier for the token, useful for revocation and replay protection. |
The three time claims, exp, nbf and iat, use numeric date values, seconds since the Unix epoch. A correct verifier checks that the current time is after nbf, before exp, and it should validate iss and aud against the values it expects. Skipping those checks is one of the more common ways a technically valid signature still lets a token be misused, because a token issued for one audience gets happily accepted by another service that never looked.
4. How signing works: HMAC versus asymmetric keys
The signature is where the trust comes from, and there are two broad families of signing algorithm, distinguished by whether the signer and verifier share the same key or not. The choice between them is one of the most consequential decisions in a JWT deployment, and it is driven entirely by who needs to verify the token.
Symmetric signing (HMAC) uses one shared secret key for both signing and verifying. The algorithm names look like HS256, HS384 and HS512, where the HS stands for HMAC with SHA-2. Whoever holds the secret can both create and validate tokens. This is simple and fast, and it is fine when the same trusted party does both jobs, for example a single application issuing tokens to itself. The catch is that verifying a token requires holding the same secret that signs it. If ten services need to verify tokens, all ten hold a key powerful enough to forge tokens, and that is a large, brittle attack surface.
Asymmetric signing uses a key pair: a private key that signs and a public key that verifies. The algorithm names look like RS256 (RSA with SHA-256) or ES256 (ECDSA), among others. Only the issuer holds the private key, so only the issuer can create valid tokens. Every consumer holds only the public key, which can verify a signature but cannot produce one. This is the right model for distributed systems, because you can hand the public key to any number of services without ever giving them the ability to mint tokens. It is why identity providers publish their public keys and why federated, multi-service architectures almost always use asymmetric signing.
The rule of thumb: if the same party issues and verifies tokens, HMAC is fine and simpler. The moment tokens are verified by services other than the issuer, use asymmetric signing so that verifiers hold only a public key and cannot forge tokens. Getting this wrong, by sharing an HMAC secret across many services, means any one of them being compromised lets an attacker forge tokens for all of them.
5. How JWTs are used for stateless authentication
The most common use of a JWT is as an access token in a stateless authentication flow. The pattern is straightforward once you have the pieces. A user or client authenticates once, by whatever means, and the authentication server responds with a signed JWT. From then on, the client attaches that token to every request, conventionally in the HTTP Authorization header as a bearer token: Authorization: Bearer <token>. Each service that receives the request verifies the signature, checks the standard claims, reads the identity and permissions from the payload, and serves the request, all without looking anything up in a session store.
The word stateless is the key. The server keeps no per-user session record. Everything it needs to authorise the request is inside the verified token. That has a real operational payoff: because there is no shared session state, you can run many identical instances of a service behind a load balancer and any instance can handle any request. There is nothing to replicate, nothing to make sticky. For horizontally scaled APIs and microservice fleets, that is a genuine simplification, and it is the single biggest reason JWTs became the default in that world.
In practice, most systems pair a short-lived access token with a longer-lived refresh token. The access JWT is deliberately given a short lifetime, minutes to maybe an hour, so that if it leaks the window of misuse is small. When it expires, the client presents the refresh token to the authentication server to obtain a fresh access token, without forcing the user to log in again. This is the pattern you meet inside the broader authorization frameworks. For how that fits into a full delegated-access flow, see the OAuth 2.0 explained pillar, and for the wider menu of options, the API authentication methods pillar.
6. JWT versus server-side session tokens
It is worth being honest that JWTs are not automatically better than the older model of server-side sessions. They are a different set of tradeoffs, and for a lot of ordinary web applications the classic session is still the simpler, safer choice.
With a server-side session, the server stores the session data and gives the client only an opaque identifier, usually in a cookie. On each request the server looks that identifier up. The identifier means nothing on its own, so there is nothing sensitive to read and, critically, revoking a session is trivial: delete the record and the next request fails. The cost is that the server must keep and consult that state, which needs a shared store when you run multiple instances.
With a JWT, the data lives in the token, so there is no lookup and no shared state, which is exactly the scaling benefit described above. But that same property creates the JWT's biggest weakness: revocation is hard. A validly signed token is accepted until it expires, because verification does not consult any central record. If a token is stolen or a user is deactivated, you cannot simply delete it; it stays valid until exp. Teams work around this with short lifetimes, denylists keyed on jti, or token-version checks, but every one of those reintroduces some server-side state and erodes the pure-stateless promise.
The honest tradeoff: reach for JWTs when you genuinely need stateless verification across services that do not share a session store, such as APIs and microservices. For a single traditional web application, an opaque server-side session is often simpler and easier to secure, because instant revocation comes for free. Choose JWTs for what they are good at, not because they are fashionable.
7. Security pitfalls
Most JWT vulnerabilities are not exotic cryptographic breaks. They are predictable misuses of a tool whose properties were misunderstood. Four of them account for the majority of real incidents.
- The alg none attack. The JWT specifications include a "none" algorithm, meaning the token is unsigned. A naive verifier that trusts the
algvalue in the header can be handed a token withalgset to "none" and no signature, and if it obliges, it accepts a completely forged token. Never let the token's own header dictate whether or how it is verified. Configure your verifier with an explicit allowlist of accepted algorithms and reject everything else, including "none". - No expiry. A token without an
expclaim, or with an absurdly long one, is valid effectively forever. If it leaks, the attacker has permanent access. Always set a short expiry on access tokens and always validate it on the receiving side. An unexpiring bearer token is a password that never changes and that you have scattered across logs and proxies. - Insecure storage. A JWT is a bearer token, which means whoever holds it can use it. Storing it carelessly, in browser local storage where any injected script can read it, or logging full tokens, or passing them in URLs where they land in server logs and browser history, hands attackers the keys. Keep tokens out of places that scripts and logs can reach, prefer secure, HttpOnly cookies for browser contexts where appropriate, and treat the token itself as a secret in transit even though its contents are not.
- Confusing signed with encrypted. This is the conceptual error underneath many of the others. A standard JWT is signed, not encrypted. The payload is readable by anyone who holds the token. Putting anything confidential inside it, a password, a full personal record, an internal secret, exposes that data to anyone who intercepts or is handed the token. Assume the payload is public, and rely on HTTPS for confidentiality in transit.
The one warning I repeat most: verify with an explicit algorithm allowlist, always enforce expiry, never store a token where hostile script or log aggregation can read it, and never put a secret in the payload. Those four disciplines prevent the overwhelming majority of JWT incidents, and none of them requires cryptographic expertise, only correct configuration.
A closely related point for anyone comparing credential styles: bearer JWTs and long-lived API keys have overlapping failure modes but different lifecycles, and choosing between them deserves its own thought. The API key versus OAuth pillar works through that comparison in detail.
8. When to use JWT
Pulling the threads together, a JWT is the right tool in a fairly specific set of circumstances, and forcing it outside them is where teams get into trouble. It shines when you need stateless verification across independent services, where the party checking a token is not the party that issued it, and asymmetric signing lets every consumer verify without being able to forge. It is a strong fit for APIs and microservices that scale horizontally and want to avoid a shared session store. It works well for short-lived access tokens in delegated-authorization flows, where a brief lifetime keeps the revocation weakness manageable. And it is genuinely useful for federated identity, where an identity provider issues tokens that many downstream applications accept.
It is a poor fit when you need immediate, reliable revocation, because a valid signature is honoured until expiry. It is unnecessary for a single traditional web application that already has a session store and gains nothing from statelessness. And it is the wrong tool for carrying anything confidential, because the payload is readable. In those cases a server-side session, or an encrypted mechanism, serves you better.
In enterprise integration work specifically, JWTs frequently appear as the access tokens that platform APIs issue and accept. If you are integrating with a system such as Microsoft's business platform, its APIs sit behind exactly this kind of token-based authorization, and understanding JWTs is a prerequisite for wiring it up cleanly. See the Business Central APIs and integrations pillar for a concrete example, and the enterprise system integration explained pillar for where token security sits in the bigger picture.
9. References
Two specifications define everything above, and they are the authoritative source when a detail matters. Both are freely published by the Internet Engineering Task Force and are worth reading directly rather than relying on second-hand summaries.
- RFC 7519, JSON Web Token (JWT). Defines the token structure, the registered claim names, and the rules for creating and validating a JWT.
- RFC 7515, JSON Web Signature (JWS). Defines how the header and payload are signed and how the signature is verified, including the compact serialization used by almost every JWT.
Final thoughts
A JWT is a small, elegant idea: put the claims in the token, sign them, and let any service holding the key trust them without a database lookup. That single property, self-contained and stateless, is what makes JWTs so well suited to APIs, microservices and federated identity, and it is a genuine improvement over shared session stores in exactly those settings. But the same property is the source of every one of its risks. The payload is public, revocation is hard, and the signature only helps if you verify it correctly.
Get the mental model right and the rest follows. Remember that a JWT is signed, not encrypted. Verify with an explicit algorithm allowlist so the "none" trick cannot bite you. Always enforce expiry, always validate issuer and audience, store the token somewhere hostile code cannot reach, and never put a secret in the payload. Do those things and JWTs are a clean, scalable foundation for authentication across systems. Skip them and a JWT becomes a forgeable, unexpiring, readable credential that you have helpfully scattered across your logs. The technology is sound; the discipline around it is what keeps it safe.
Designing token-based authentication across systems?
Independent, vendor-neutral advice on API authentication, JWT and OAuth flows, identity federation and secure enterprise integration. 22+ years across ERP, EAM, CAFM and enterprise integration. Practical patterns, not slideware.
Book a conversationRelated reading: Enterprise system integration explained, OAuth 2.0 explained, API authentication methods, API key vs OAuth, Business Central APIs and integrations.
Muhammad Abbas
CMMS / CAFM Manager & Enterprise Integration Specialist · 22+ years across ERP, EAM, CAFM and enterprise integration.
Work with me