JWT: what is a JSON Web Token, structure and usage

Author: IT Sectr Published: 2026-04-05 Reading time: 9 min

JWT (JSON Web Token) is a compact format for transferring data between parties as a JSON object protected by a digital signature. The token can be signed using HMAC (symmetric key) or RSA/ECDSA (asymmetric pair), which ensures data integrity and authenticity. According to IETF RFC 7519, 2015, JWT is used in millions of applications for authentication, secure claims exchange, and as the ID Token format in OpenID Connect.

Key Takeaways

  • JWT is a self-contained token containing all verification data within itself
  • Structure — three parts: header, payload and signature, separated by dots
  • Signature — ensures that the data has not been altered after token creation
  • Stateless — the server does not need to store a session, simplifying scaling
  • Security — JWT does not encrypt data, only signs it; sensitive information must not be placed in the payload

What is JWT?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way to transmit information between parties as a JSON object. The information in JWT is called claims — statements about the subject (user) and additional attributes. Each claim is a key-value pair: user identifier, role, expiration time, issuer.

JWT is called self-contained because all the information needed for verification is inside the token itself. The server does not need to access a database or external storage to verify the token's validity — it only needs to check the signature. This property makes JWT ideal for distributed systems and microservice architectures, where multiple services must authenticate requests without a shared session store.

According to Auth0, 2025, more than 65% of mobile and web applications use JWT as the primary token format for API authentication, outpacing opaque tokens and session identifiers.

JWT Structure: header, payload and signature

JWT consists of three parts separated by dots: header.payload.signature. Each part is a Base64url-encoded JSON. Let us examine each part in detail.

Header — algorithm and token type

Header contains two mandatory fields: alg (algorithm — signing algorithm) and typ (type — token type, always “JWT”). The algorithm can be symmetric (HS256 — HMAC with SHA-256) or asymmetric (RS256 — RSA with SHA-256, ES256 — ECDSA with P-256). Asymmetric algorithms are preferred because they allow the client to verify the signature without possessing the secret key.

Example of a decoded header:

json
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-id-1"
}

Payload — claims and data

Payload contains claims — statements about the subject. Claims are divided into three types: registered (iss, sub, aud, exp, nbf, iat, jti), public (defined by the developer in the IANA Registry), and private (agreed upon between parties). sub (subject) is the unique user identifier. exp (expiration) is the token expiration timestamp. iss (issuer) is the token issuer.

json
{
  "sub": "user-abc-123",
  "iss": "https://auth.example.com",
  "aud": "my-mobile-app",
  "exp": 1812345678,
  "iat": 1812342078,
  "role": "premium_user"
}

Signature — integrity verification

Signature is created by applying the signing algorithm to the concatenation of the header and payload using a secret or private key. The formula: HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) for HMAC, or RSASHA256(...) for asymmetric algorithm. The recipient computes the signature the same way and compares it with the received one — if they match, the data has not been altered.

How JWT Works: creation and verification

The workflow with JWT consists of two phases: token creation (issuance) by the authentication server and token verification by the client or resource server. The authentication server receives the user’s credentials, creates a payload with claims, and signs it. The resulting JWT is sent to the client in response to a login request or in the OAuth 2.0 / OpenID Connect response body.

JWT in mobile authentication

In mobile applications, JWT is used as follows: after successful login, the user receives an access token in JWT format. The application stores it in a secure storage (Keychain on iOS, EncryptedSharedPreferences on Android). With each API request, the application adds the Authorization: Bearer <token> header. The API server verifies the JWT signature, extracts claims, and makes access decisions based on them — without querying the database.

According to Google Codelabs, 2025, using JWT in Firebase Authentication reduces the number of requests to the authentication server by 40–60% compared to session tokens, since data is verified locally on each microservice. This is especially important for high-load architectures where every millisecond of latency impacts user experience. At 50,000 requests per minute, switching to JWT can save up to 10 server instances handling introspection requests.

JWT vs Session Token

JWT and Session Token solve the same problem — request authentication — but differ fundamentally in architecture. Session Token is a random identifier string that references session data stored on the server (stateful). JWT is a self-contained token containing all data within itself (stateless).

ParameterJWTSession Token
Data storageInside the token (self-contained)On the server (session storage)
ScalingNo shared storage requiredRequires Redis/DB for multi-server
Token revocationComplex (needs blacklist)Simple (delete session from DB)
SizeLarge (500–2000 bytes)Small (16–64 bytes)
Signature verificationCryptographicNone (string comparison)

Advantages and disadvantages of JWT

JWT wins in distributed systems: microservices can verify the token locally without a shared store. For example, in an architecture with five microservices, each service verifies JWT in 1–2 ms without a network call, while a session token requires a centralized Redis lookup on every request, adding 10–30 ms of latency. However, JWT is difficult to revoke — once a token is issued, it is valid until it expires. Session Token is easy to revoke by deleting the record from the DB or Redis.

For mobile applications, a combined approach — JWT with a short lifetime (15–30 minutes) and Refresh Token — provides a balance between performance and security. JWT is used for API access, while the refresh token (usually opaque) is used to obtain new JWTs. If a JWT is compromised, the attacker has access for 15–30 minutes; if a refresh token is compromised, the session is blocked through rotation and reuse detection.

JWT Security

Security of JWT depends on proper implementation. The most common vulnerability is the “alg none” attack: an attacker changes the token header to “alg”: “none”, and the server, without verifying the algorithm, accepts the forged token. Protection: always verify that the algorithm in the header matches the expected one (RS256, ES256), and reject tokens with alg: none.

Common vulnerabilities

JWT vulnerabilities also include: weak secret key for HMAC (brute-force in minutes), private key leakage (signing any data on behalf of the server), storing sensitive data in the payload (JWT does not encrypt, only signs), JWK header injection attack (injecting a custom public key). Using trusted libraries — Nimbus JOSE + JWT, jjwt (io.jsonwebtoken), PyJWT — reduces the risk of exploiting these vulnerabilities.

An additional security measure is JWK Thumbprint (RFC 7638): binding a public key to the token via a thumbprint in the header. If the server stores the expected thumbprint for each client, JWK header injection becomes impossible — the server rejects any key that does not match the registered one. The OAuth Security Workshop 2025 recommends JWK Thumbprint as mandatory protection for all JWTs used in financial and medical applications.

Code Example: working with JWT in Kotlin

The jjwt library (auth0/java-jwt) allows creating and verifying JWTs in an Android application in just a few lines. In the example below, the server generates a token with sub and role, and the client verifies the signature. For secure storage of the secret key on the server, use environment variables or an HSM (Hardware Security Module) — storing the key in code or a configuration file is a serious security mistake.

JWT Generation

kotlin
val secret = "my-256-bit-secret-key-here"
val token = JWT.create()
    .withSubject("user-abc-123")
    .withIssuer("auth.example.com")
    .withClaim("role", "premium_user")
    .withExpiresAt(Date(System.currentTimeMillis() + 3600000))
    .sign(Algorithm.HMAC256(secret))

// Sending token to client
println("JWT: $token")

JWT Verification

kotlin
fun verifyToken(token: String): Boolean {
    return try {
        val decoded = JWT.require(Algorithm.HMAC256(secret))
            .withIssuer("auth.example.com")
            .build()
            .verify(token)
        // Signature valid, claims extracted
        println("Subject: ${decoded.subject}")
        true
    } catch (e: Exception) {
        println("Token invalid: ${e.message}")
        false
    }
}

Frequently Asked Questions

Can passwords be stored in JWT?

No. JWT is signed, not encrypted — anyone can decode the Base64 payload and read the data. Sensitive information (passwords, credit card numbers, personal data) must be transmitted only in encrypted form using JWE (JSON Web Encryption).

Which JWT signing algorithm is the most secure?

ES256 (ECDSA with P-256) is recommended — it provides an equivalent level of security to RSA 2048-bit with a significantly smaller signature size. RS256 is suitable for compatibility with legacy systems. HS256 (HMAC) requires secure exchange of a secret key, which is more challenging in a distributed architecture.

How to revoke a JWT before it expires?

JWT cannot be revoked directly — it is valid until exp. Solutions: use a short lifetime (15–30 minutes), maintain a blacklist of revoked jti (JWT ID) values on the server, or bind tokens to a secret key version. The refresh token is revoked in the standard way — by removing it from storage.

How is JWT different from Bearer token?

Bearer token is a concept: any token that the bearer can use for access. JWT is a specific token format. A Bearer token can be a JWT or an opaque string. JWT adds self-containment and cryptographic verification to the Bearer concept.

What size of JWT is considered normal?

A typical JWT with RS256 signature is 500–2000 bytes. If the payload contains many custom claims or an asymmetric signature with a large key is used, the size can reach 4–5 KB. This is significantly larger than a session token (16–64 bytes), which affects HTTP header size.

Summary

  • JWT is a compact self-contained JSON token with a digital signature
  • Structure — three parts: header (algorithm), payload (claims), signature (signature)
  • Stateless — the server verifies the token without querying the database
  • JWT vs Session — JWT wins in scaling, Session wins in revocation
  • Security — protection against alg none, weak keys, and JWK injection is mandatory
  • Payload is not encrypted — sensitive data requires JWE
  • JWT is the standard format for ID Token in OpenID Connect and Firebase Authentication tokens

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also