
In large organizations, authentication rarely happens in a single place. A user may sign in through a centralized identity provider, access multiple internal tools, call partner APIs, and interact with several backend services within a single session. Passing identity and authorization context reliably across these boundaries is a core enterprise requirement, and JSON Web Tokens (JWTs) are commonly used for this.
JWTs are often misunderstood as an authentication mechanism, but they are not an authentication system. A JWT is a standardized, signed token format for encoding claims that other systems can verify. In enterprise setups, JWTs are typically issued by an authorization or identity system, most commonly through OAuth 2.0 or OpenID Connect (OIDC), after a user or service has already been authenticated.
Because JWTs are compact, self-contained, and cryptographically verifiable, they work well in microservice architectures, internal APIs, and zero-trust environments. Each service can validate tokens independently without shared session state, which simplifies scaling but introduces new responsibilities around token lifetimes, validation, and key rotation. This guide focuses on how JWTs fit into that broader system and how to use them safely in production.
A JSON Web Token (JWT) is a compact, URL-safe token format used to transmit claims between parties in a verifiable way. At its core, a JWT is a JSON object that has been cryptographically signed, allowing the recipient to validate that the data has not been altered and to trust the issuer of the token. JWTs are signed using either a shared secret (HMAC) or an asymmetric key pair (RSA or ECDSA).
It’s important to be precise about what JWTs do. A JWT does not authenticate a user or grant access on its own. Instead, it carries claims, such as who the subject is, who issued the token, and who it is intended for, that other systems can evaluate. Authentication and authorization decisions are made by the systems that issue and validate JWTs, not by the token format itself.
JWTs are commonly used in authentication and authorization systems because they are easy to transmit and verify across service boundaries. They are typically sent in HTTP headers as bearer tokens, though they can also be transmitted via cookies depending on the application architecture. Their compact size and standardized structure make them a practical choice for APIs, service-to-service communication, and distributed systems.
In identity systems, JWTs are widely used as the token format for OAuth 2.0 access tokens and OpenID Connect (OIDC) ID tokens. In these flows, the JWT represents the outcome of an authentication event or an authorization grant; it does not perform either function on its own.
JWTs are often associated with stateless architectures, but statelessness is a system design decision, not an inherent property of the token. In a stateless setup, all the information needed to validate a request is contained within the token itself, allowing services to verify requests without consulting shared session storage.
This approach can reduce coupling between services and simplify horizontal scaling, which is why it is popular in microservice and API-driven environments. However, stateless JWTs also introduce tradeoffs. Because tokens are self-contained, revoking them immediately is difficult unless additional state, such as revocation lists or token introspection, is introduced.
For this reason, many production systems combine JWTs with short-lived tokens and key rotation to balance scalability with security and control.
A JWT consists of three parts, each Base64URL-encoded and separated by dots. Base64URL is a URL-safe variant of Base64 that replaces the + and / characters with - and _, and omits padding characters (=). This ensures JWTs can be safely transmitted in URLs and HTTP headers without additional encoding.


Together, these three components enable JWTs to be efficiently transmitted and independently verified, but only when used as part of a correctly designed authentication and authorization system.
A JWT’s security does not come from its structure alone; it depends on how rigorously the token is issued, validated, and managed throughout its lifecycle. Even though a JWT is cryptographically signed, it is only trustworthy if the receiving system enforces strict validation rules. Choices such as which signing algorithms are allowed, how signing keys are managed, and which claims are required all directly affect whether a JWT implementation is secure in practice.
At minimum, services validating JWTs should verify the token’s signature using trusted keys and explicitly check critical claims such as issuer (iss), audience (aud), and expiration (exp). Relying on signature verification alone is a common mistake. Tokens must also be rejected if they use unexpected algorithms, reference unknown key IDs (kid), or violate time-based constraints. These checks ensure the token was issued by the correct authority, intended for the receiving service, and is still valid.
Key management is equally important. Signing keys should be rotated regularly, algorithms should be allow-listed rather than accepted dynamically, and tokens should be issued with short lifetimes to limit blast radius if compromised. JWTs are powerful, but only when used as part of a controlled system rather than as a self-contained security solution.
A JWT is transmitted as a single string made up of three Base64URL-encoded parts separated by dots. This encoded form is what clients send to APIs and services on each request.
Although this representation looks opaque, it is only encoded, not encrypted. Anyone with access to the token can decode its contents.
The header describes how the token was created and how it should be verified.
Systems validating JWTs should never blindly trust these values. Allowed algorithms must be explicitly configured on the server.
Critical security note for developers:
Never accept the none algorithm ("alg": "none"). This is a known class of vulnerabilities where attackers can craft unsigned tokens that bypass verification if the server does not enforce algorithm checks. Always configure an explicit allowlist of accepted algorithms (for example, RS256 or ES256) and reject any tokens that use none or unexpected algorithms.
This ensures that token verification behavior is deterministic and resistant to algorithm-confusion and downgrade attacks.
The payload contains claims about the subject and the context in which the token was issued.
Claims are fully readable once decoded. For this reason, sensitive information should never be placed in the payload. Fields such as sub (subject) and iat (issued at) are commonly used, but their presence alone does not grant access; authorization decisions must be enforced separately by the receiving service.
Developer note:
Time-based claims such as iat (issued at) and exp (expiration time) are NumericDate values as defined in RFC 7519. They represent Unix timestamps in seconds, not milliseconds. In the example above, 1516239022 corresponds to seconds since the Unix epoch.
This distinction matters in practice, as misinterpreting timestamps can lead to tokens being treated as valid for far longer, or shorter, than intended.
The final part of the JWT is the signature. It is generated using the encoded header, encoded payload, and a signing key.
When a service receives a JWT, it verifies the signature using a trusted key, such as a shared secret or a public key obtained from a JWKS. If the signature does not validate, or if required claims are missing or invalid, the token must be rejected.
Signature verification is what makes a JWT trustworthy. Without strict verification and claim validation, a decoded token is just untrusted data.
JWTs are not responsible for authenticating users. Instead, they are issued after authentication has already happened, typically by an authorization server or identity provider. In enterprise systems, this flow is most commonly implemented using OAuth 2.0 or OpenID Connect.
When a user signs in, the client application sends an authentication request to the authorization server. The authorization server validates the user’s credentials using its own mechanisms, such as passwords, SSO, or MFA. If authentication succeeds, the authorization server issues a token, often a JWT, back to the client. This token represents the result of that authentication and authorization decision.
The client then includes this JWT in subsequent requests to APIs or backend services, usually in the Authorization: Bearer header or via a secure cookie. The API does not re-authenticate the user. Instead, it validates the token, checks its claims, and decides whether to allow the request.

When an API (also called a resource server) receives a request with a JWT, it must treat the token as untrusted input until verification succeeds. Validation involves more than just checking whether the token exists.
At a minimum, the API should:
If any of these checks fail, the request must be rejected. A valid signature alone is not sufficient. JWT security depends on strict claim validation and explicit trust boundaries.
JWT signatures enable systems to trust tokens without shared session state. That trust depends entirely on how signing keys are managed.
With symmetric signing (HMAC), the same secret is used to sign and verify tokens. This approach is simple but risky at scale, because every system that verifies tokens must also be trusted to protect the signing secret. If the secret leaks, attackers can mint valid tokens.
With asymmetric signing (RSA or ECDSA), the authorization server signs tokens using a private key, while APIs verify them using a public key. This model is better suited for enterprise and multi-service environments because only the issuer can create tokens, while many services can safely verify them.
Regardless of the algorithm, signing keys must be rotated regularly and distributed securely, often via JWKS endpoints. Weak key management undermines the entire security model, even if the JWT format itself is used correctly.
JWTs are commonly sent in authorization headers, especially for APIs and service-to-service calls. In browser-based applications, they may also be stored in HTTP-only, secure cookies to reduce exposure to XSS attacks. Storage strategy depends on the client type and threat model, but no storage option is “safe by default” without proper controls.
In production systems, signing keys should be rotated regularly, and multiple keys may be active simultaneously. JSON Web Key Sets (JWKS) provide a standardized way for token issuers to publish the public keys clients and APIs need to verify JWT signatures. A JWKS is a JSON document that contains one or more cryptographic keys, each identified by a key ID (kid).
Rather than hard-coding verification keys, resource servers fetch the JWKS from a trusted endpoint exposed by the authorization server. When a JWT is received, the verifier uses the kid value in the token header to select the correct key from the set. This design allows new keys to be introduced and old keys to be retired without downtime or coordinated redeployments across services.
A simplified JWKS might look like this:
Each entry represents a public key that can verify token signatures. The presence of multiple keys enables seamless key rotation: new tokens are signed with a new key, while previously issued tokens remain verifiable until they expire.
In OAuth 2.0-based systems, JWTs are commonly used as access tokens. An access token represents the authorization granted to a client, not the user’s authentication state. It tells a resource server what the client is allowed to do, not how the user logged in.
JWT access tokens typically include claims such as the intended audience (aud), scopes or permissions, and an expiration (exp). When an API receives a request, it validates the token and evaluates these claims to decide whether the requested operation is allowed. The API does not rely solely on the token format; authorization is enforced by mapping token claims to application-specific access rules.
Using JWTs as access tokens enables APIs to make authorization decisions locally and consistently, provided signature verification, claim validation, and key rotation are handled correctly.
This walkthrough demonstrates how JWTs are issued and validated inside a backend API. The example focuses on token handling and request validation rather than implementing a full OAuth or identity provider. In real-world deployments, token issuance is typically handled by a dedicated authorization service, while APIs validate tokens and enforce access rules.
The goal here is to show how JWTs are used correctly in practice and to highlight the checks that matter in production systems.
Start by initializing a basic Node.js project and installing the required dependencies:
These dependencies serve distinct roles in the example:


In this simplified setup, the API verifies user credentials and issues a JWT upon successful authentication. While this responsibility often belongs to an authorization server in larger systems, keeping it in one place here makes the token lifecycle easier to follow.
What’s happening during login:
The key takeaway is that the JWT represents the result of authentication rather than performing authentication itself.
When a client accesses a protected endpoint, the API does not re-authenticate the user. Instead, it validates the JWT attached to the request and decides whether to proceed.
During token validation, the API enforces several critical checks:
Only after these checks pass does the API process the request. Skipping any of these validations can allow forged or replayed tokens to be accepted.
Access tokens are intentionally short-lived to limit the damage if they are leaked. To maintain user sessions without forcing repeated logins, applications rely on refresh tokens to obtain new access tokens when their existing ones expire. Refresh tokens should be treated as long-lived credentials and handled more carefully than access tokens.
Below is a simplified refresh flow that demonstrates the mechanics without introducing unsafe shortcuts.
In production systems, refresh tokens should be rotated on every use and stored server-side so that replayed tokens can be detected and rejected. This example keeps refresh handling minimal to avoid obscuring the core mechanics.
Token verification is the most security-critical step in any JWT-based system. APIs must treat every incoming token as untrusted input until all verification and claim checks pass. Relying on default library behavior or skipping explicit validations is a common source of vulnerabilities, especially as systems evolve, teams change, or multiple identity providers are introduced.

This helper enforces a consistent validation policy across the application. Restricting allowed algorithms prevents downgrade and confusion attacks, while issuer and audience checks ensure that only tokens issued by a trusted authority and intended for this API are accepted.
By verifying tokens inside middleware, invalid requests are rejected before they reach application logic, and downstream handlers only ever see a validated identity context.
During development and debugging, tools such as JWT.io are useful for inspecting tokens outside the application. By pasting a JWT into the tool, you can decode the header and payload, confirm which claims are present, and verify that the signature matches the expected key. This is helpful for understanding token structure and diagnosing validation failures, but it should only be used for inspection, not as part of any production verification flow.
Using JWTs safely in production requires more than correct syntax or library defaults. Many JWT-related incidents stem from operational shortcuts rather than cryptographic failures, so token handling needs to be treated as an ongoing lifecycle concern.
In practice, this typically includes the following:
JWTs are powerful building blocks, but their safety depends entirely on how deliberately they are issued, verified, rotated, and monitored over time.
Although the examples in this guide use Node.js, the underlying JWT concepts apply consistently across languages and platforms. Most ecosystems provide mature libraries for creating and validating tokens, but the same security principles still apply regardless of implementation.
Common options include:
Each language has its own conventions and tooling, but the fundamentals remain unchanged: JWTs must be issued intentionally, validated rigorously, and used as part of a broader authentication and authorization architecture, not as a standalone security solution.
JWTs bring a host of benefits that make them a widely used choice for managing authorization state in distributed systems:
Even though JWTs themselves define a secure format for carrying claims, implementation mistakes or library vulnerabilities can still introduce serious risks. Examining real incidents shows why rigorous validation and secure defaults are essential:
These cases illustrate that the JWT format is secure, but the security of a system depends on how libraries implement verification, enforce claim checks, and reject unsafe tokens. All of these flaws have since been addressed in patch releases, but they serve as strong reminders that developers must keep libraries up to date and defend against protocol misuse.
JWTs are simple by design, but using them safely in production requires discipline around storage, validation, and lifecycle management. Most JWT-related issues arise not from the format itself, but from how tokens are issued, stored, and reused over time.
The following practices help reduce risk while preserving the benefits of JWT-based systems:
In browser-based applications, JWTs are often stored in HTTP-only, secure cookies to prevent access from client-side JavaScript and reduce exposure to XSS attacks. These cookies should always be sent over HTTPS and configured with appropriate SameSite attributes. For non-browser clients such as mobile apps or service-to-service calls, tokens are typically transmitted in authorization headers, with transport security handled at the protocol level.
Every JWT should include an exp claim, and access tokens should be short-lived. Limiting token lifetime reduces the window of misuse if a token is leaked. When longer sessions are required, short-lived access tokens should be combined with refresh tokens rather than extending the validity of the access token.
JWTs are not well-suited to real-time revocation without introducing additional state. Instead of relying on token blacklists, production systems typically allow tokens to expire naturally and issue new ones through controlled refresh flows. Once expired, tokens should be rejected unconditionally and removed from client storage to prevent reuse.
JWT payloads are Base64URL-encoded, not encrypted. Anyone in possession of the token can decode its contents. Personally identifiable information, secrets, or internal system data should never be included unless the token is explicitly encrypted using JWE and the threat model justifies it.
JWTs should carry only the claims required for validation and authorization decisions. Overloading tokens with excessive data increases payload size, impacts network performance, and makes future changes harder. Lightweight tokens are easier to rotate, validate, and reason about across services.
Following these practices keeps JWT usage predictable and auditable, and ensures that tokens remain a reliable mechanism for carrying claims rather than a hidden source of security risk.
JWTs are best understood as a mechanism for carrying verified claims across system boundaries. They are most effective when used as part of a broader authentication and authorization architecture rather than as a standalone solution.
Common scenarios where JWTs are a strong fit include:
JWTs serve as the standard format for ID tokens in OpenID Connect (OIDC). In this context, an ID token is issued after successful authentication and is used by client applications to verify the user’s identity and retrieve basic profile information.
Because ID tokens are self-contained and cryptographically signed, client applications can validate them locally without having to repeatedly call the identity provider. This makes JWTs particularly effective in enterprise environments where identity needs to be propagated reliably across multiple applications and platforms.
JWTs are often associated with stateless architectures, but they can also be used in hybrid session models where limited session state still exists. In these setups, JWTs carry session context, such as user identity or authorization scope, while the system retains control over session lifecycle through expiration and refresh mechanisms.
In microservice-based systems, JWTs allow each service to independently validate incoming requests without relying on a centralized session store. This enables consistent request verification as a request moves across services, improving scalability while maintaining clear trust boundaries between components.
JWTs are commonly used as access tokens to secure API endpoints. Each request includes a token that represents an authorization decision already made by an upstream system, such as an OAuth authorization server.
APIs validate the token’s signature and claims, such as audience, issuer, and scope, to determine whether the requested operation is allowed. This approach avoids repeated authentication checks and enables APIs to make fast, local authorization decisions while remaining decoupled from identity systems.
JWTs are a natural fit for Single Sign-On (SSO) implementations, where users authenticate once and gain access to multiple services within the same ecosystem. In these environments, tokens issued by a trusted identity provider can be reused across applications, reducing friction for users while maintaining consistent enforcement of access policies.
JWT-based SSO is particularly common in enterprise platforms, internal tooling, and cloud-based service suites, where users routinely interact with many applications that share a common identity layer.
Across all of these scenarios, the key principle remains the same: JWTs carry claims, not trust. Trust is established by how tokens are issued, validated, and managed, not by the token's format.
JWTs are a powerful mechanism for carrying claims, but they introduce specific risks that teams need to understand before adopting them. Most of these risks are not inherent flaws in JWT itself, but consequences of how tokens behave once issued.
Key risks to account for include:
If an attacker gains access to a valid JWT, they can impersonate the token holder until the token expires. Mitigation relies on short-lived access tokens, secure transport (HTTPS), careful client-side storage, and strong validation on every request.
Stateless JWTs cannot be revoked instantly without introducing additional state, such as revocation lists or token introspection. Applications that require hard, immediate session termination, such as financial systems or high-risk admin tooling, may find traditional server-side sessions easier to control.
Accepting unexpected signing algorithms, skipping issuer or audience checks, or trusting unvalidated claims can all lead to authorization bypass. These failures often go unnoticed until exploited.
JWTs work best when they carry minimal, well-defined claims. Using them to encode complex or frequently changing authorization states can make systems harder to reason about and harder to evolve safely.
JWTs are not always the right choice. Applications that require fine-grained session control, frequent permission changes, or guaranteed real-time revocation may be better served by traditional server-side sessions or token introspection–based approaches.
JWTs are not an authentication system, but a standardized token format for carrying verified claims across services. When used within frameworks like OAuth 2.0 and OpenID Connect, they enable scalable, decoupled request validation, provided tokens are issued carefully, validated rigorously, and managed throughout their lifecycle.
If you’re designing or reviewing an authentication system, start by clarifying where JWTs fit in your architecture, enforcing strict validation and expiration policies, and evaluating whether stateless tokens match your security and revocation requirements. For a broader comparison of approaches, explore our guide to API authentication in B2B SaaS to decide when JWTs are the right choice, and when they aren’t.
Scalekit helps teams issue, sign, rotate, and validate JWTs as part of a broader identity and authorization setup. Instead of building token infrastructure from scratch, teams can rely on Scalekit to handle key rotation, standards-compliant token issuance, and secure verification flows that integrate cleanly with OAuth 2.0 and OpenID Connect.
Yes. Scalekit is designed to integrate with existing identity providers and applications rather than replace them. It can validate JWTs issued by trusted authorities, publish JWKS endpoints, and enforce consistent token policies across services, which makes it easier to scale and standardize authentication flows.
JWTs are not inherently better; they solve a different problem. JWTs work well in distributed systems where services need to validate requests independently, while server-side sessions are often better when immediate revocation and centralized control are required. The right choice depends on your architecture, security requirements, and operational constraints.
It depends on the client and threat model. Browser-based applications often use HTTP-only, secure cookies to reduce exposure to XSS, while mobile apps and service-to-service communication typically send JWTs in authorization headers. Neither option is universally safer without proper validation and transport security.
JWTs may not be a good fit if your application requires real-time session revocation, frequent permission changes, or heavy server-side session logic. In these cases, stateful sessions or token introspection–based approaches can offer clearer control and simpler security guarantees.