Understanding JWTs: The Key to Secure User Authentication

Arish AhmadArish Ahmad
9 min read

What is JWT?

JWT stands for JSON Web Token. It’s a compact, URL-safe token format used to securely transmit information between parties as a JSON object.

How Does JWT Work?

Think of a JWT as a sealed envelope. This envelope contains some information and a signature to make sure it hasn’t been tampered with. Here’s how it works:

  1. Header: This part describes how the JWT is encoded and the algorithm used for the signature. It’s like the label on the envelope saying “Here’s how this envelope is sealed.”

  2. Payload: This is the main content of the JWT, like the letter inside the envelope. It contains the claims or information you want to share. For example, it might include user information or permissions.

  3. Signature: This part is used to verify that the JWT wasn’t altered after it was issued. It’s like the wax seal on the envelope. The server uses a secret key to create the signature, and when you receive the JWT, you can use the same key to check if the envelope has been tampered with.

Example

Here’s a very simplified JWT:

  • Header: {"alg": "HS256", "typ": "JWT"}

  • Payload: {"id": "1234567890", "name": "John Doe", "message": "this is a secret message"}

  • Signature: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

So a JWT might look like this (it’s base64-encoded):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im5ld3VzZXIiLCJleHAiOjE3MjUxNzQ1MTF9.cue3_U6PCiGCY8RY-kX4Yi-9N5DNT0UbN0mmvTQt5mY

Server: Decode and Verify JWT

If you send the JWT token to the server, the server can extract and read the information in the payload, including id, name, and message, as long as it can verify the token’s signature. Here’s how it works:

  1. Receive the JWT: When you send the JWT to the server (usually in the Authorization header or cookies), the server gets the token.

  2. Decode the JWT: The server decodes the JWT to read its header and payload. This is possible because JWTs are encoded in Base64, which means the payload is just encoded text. Decoding it doesn’t require a key, but it doesn’t ensure the content hasn’t been tampered with.

  3. Verify the Signature: To make sure the JWT hasn’t been altered, the server checks the signature. It uses the same secret key that was used to create the signature. If the signature matches, the server can trust that the payload is valid and hasn’t been modified.

  4. Extract Information: Once verified, the server can access the information in the payload, such as id, name, and message, and use it to handle the request appropriately.

Important Points

  1. Confidentiality: While JWTs are signed to ensure their integrity, they are not encrypted by default. Anyone who has the JWT can read its payload, so sensitive information should not be included in the payload unless you encrypt the JWT.

  2. Expiration: JWTs often include an expiration time (exp claim) to limit their validity period. The server checks this to ensure the JWT is still valid.

Sending & Receiving JWT Token

  1. User Sends Payload to Server:

    The user sends a request to the server with their credentials (e.g., username and password) or some other form of data for authentication.

  2. Server Generates JWT:

    • The server verifies the user's credentials or payload.

    • If the credentials are valid, the server creates a JWT that includes claims (like id) and signs it using a secret key or a private key (for asymmetric algorithms).

  3. Server Sends JWT to User:

    • The server sends the signed JWT back to the user as part of the response.

    • The JWT typically contains a claim (id) that specifies when the token expires.

  4. User Saves JWT:

    • The user stores the JWT securely, usually in local storage or a cookie.

    • The JWT can be stored in an HTTP-only cookie to protect it from XSS attacks.

  5. User Uses JWT for Further Requests:

    For any subsequent requests to protected resources, the user includes the JWT in the request, typically in the Authorization header like this: Authorization: Bearer <JWT>.

  6. Server Verifies JWT:

    • Upon receiving a request with the JWT, the server verifies the token's signature to ensure it hasn’t been tampered with.

    • The server also checks the exp claim to ensure the token hasn’t expired.

    • If the JWT is valid, the server processes the request and sends back the response.

Understanding JWTs and HTTPS Encryption: Why Your JWT Isn’t Encrypted

When dealing with JSON Web Tokens (JWTs), it's crucial to understand how they handle data and the security implications of their use. One common point of confusion is the difference between Base64 encoding, HTTPS encryption, and JWT confidentiality.

Base64 encoding transforms the JWT into a text format that’s easy to transmit, but it’s important to note that Base64 encoding is not encryption. This means that anyone who has access to the JWT can decode it to view its contents.

At the User End

  • JWT Readability: On the user end, the JWT is indeed readable. If a user has the JWT (e.g., stored in local storage or cookies), they can decode it to see its contents. However, this assumes the JWT is not encrypted.

At the Server End

  • JWT Readability: On the server end, the JWT is also readable. The server decodes the JWT to access its payload (e.g., user ID) after verifying the signature.

The Role of HTTPS: In Transit (During Transmission)

HTTPS (HyperText Transfer Protocol Secure) is designed to protect data transmitted over the internet. Here’s what it does:

  • Protection by HTTPS: When the JWT is transmitted over the network (from the client to the server), HTTPS encrypts the entire communication channel. This means:

    • Encryption: HTTPS encrypts the data being sent, including the JWT. So, if someone intercepts the data during transmission, they would see encrypted information and not the actual JWT.

    • Protection: This protects the JWT from being easily read or tampered with by attackers while it's in transit.

Additional Security Measures

If you need to ensure that the contents of the JWT remain confidential, especially if it contains sensitive information, consider the following:

  • Avoid Sensitive Data: Refrain from including highly sensitive information directly in the JWT payload.

  • Encrypt the JWT: Use additional encryption to protect the JWT’s contents. This ensures that even if the JWT is intercepted or accessed, its payload remains secure.

  • Use HTTPS: Always use HTTPS to transmit JWTs over the network to protect the data in transit.

Understanding JWT Expiry and How Servers Handle Expired Tokens

A JWT typically includes an exp (expiry) claim, which specifies the timestamp at which the token will expire. This is usually set when the token is issued. For instance, if a JWT is issued with a 5-minute expiry, it will be valid for 5 minutes from the time of issuance.

Example JWT Payload:

{ "sub": "1234567890", "name": "John Doe", "exp": 1700000000 }

In this example, the exp claim contains a Unix timestamp that represents the expiration time of the token.

What Happens After 5 Minutes?

After the specified expiry time has passed:

  1. Token Becomes Invalid: The JWT is no longer valid. This means that any requests made with this token will be rejected by the server.

  2. Server Check: When the server receives a request with a JWT, it checks the exp claim to determine if the token has expired.

  3. Error Response: If the JWT has expired, the server typically responds with an error message, such as a 401 Unauthorized status, indicating that the token is no longer valid.

How Does the Server Check JWT Expiry?

Here’s how the server determines if a JWT has expired:

  1. Receive the JWT: The server receives the JWT from the client, usually in the Authorization header or cookies.

  2. Decode the JWT: The server decodes the JWT to access its payload. This decoding allows the server to read the claims, including the exp claim.

  3. Extract and Compare exp Claim:

    • The payload contains an exp claim with a Unix timestamp.

    • The server compares the current time with the exp timestamp.

    • If the current time is greater than the exp time, the token is considered expired.

  4. Verify Token: In addition to checking the expiry, the server verifies the token’s signature to ensure that the token has not been tampered with.

  5. Respond to Client: If the token is expired, the server sends a response indicating that the token is no longer valid.

Secure JWT Storage

JWTs can be stored either on the client side or server side. Each approach has its own security implications and best practices. Here’s a detailed look at both:

  1. Client-Side Storage

a. HTTP-Only Cookies

  • Description: Storing JWTs in HTTP-only cookies is a highly secure method because these cookies cannot be accessed via JavaScript. This protects against Cross-Site Scripting (XSS) attacks.

  • Implementation:

      httpCopy codeSet-Cookie: jwt=your_jwt_token; HttpOnly; Secure; SameSite=Strict
    
    • HttpOnly: Prevents JavaScript from accessing the cookie.

    • Secure: Ensures the cookie is only sent over HTTPS.

    • SameSite=Strict: Limits the cookie to same-site requests, reducing Cross-Site Request Forgery (CSRF) risks.

b. Local Storage

  • Description: Local Storage and Session Storage are more accessible via JavaScript, making them vulnerable to XSS attacks.

  • Implementation:

      javascriptCopy codelocalStorage.setItem('jwt', your_jwt_token);
    
  • Risks: Not recommended due to susceptibility to XSS. HTTP-only cookies are preferred for enhanced security.

  1. Server-Side Storage

a. Secure Server-Side Sessions

  • Description: Store JWTs on the server side using sessions. This involves storing only a session identifier in a client-side cookie while keeping the actual JWT on the server.

  • Implementation:

    • Store the JWT securely in a database or in-memory store like Redis.

    • Store a session ID in an HTTP-only cookie.

    • Map the session ID to the JWT on the server.

b. Token Storage in a Secure Database

  • Description: Use a secure database or cache system (e.g., Redis) for storing JWTs, with an identifier (e.g., session ID) in the client-side cookie.

  • Implementation:

    • Securely store the JWT in the database or cache.

    • Use a unique identifier to refer to the JWT in client-side cookies.

Preventing Unauthorized Requests

To secure your server and prevent unauthorized access from bots or testing tools, follow these strategies:

  1. Rate Limiting:

    • Purpose: Limit the number of requests a client can make within a given timeframe.

    • Implementation: Use middleware or libraries to enforce rate limits on your server.

  2. CAPTCHA:

    • Purpose: Verify that the request is made by a human, not a bot.

    • Implementation: Integrate CAPTCHA services like Google reCAPTCHA on critical forms and authentication endpoints.

  3. Authentication and Authorization:

    • JWT: Use JWTs to authenticate users and manage access. Ensure JWTs are validated and include necessary claims.
  4. Referer and Origin Checking:

    • Purpose: Validate that requests originate from your own website.

    • Implementation: Check the Referer or Origin headers to confirm that requests come from your domain.

  5. Secure Development Practices:

    • Regular Audits: Regularly audit your security practices and code.

    • Penetration Testing: Perform penetration testing to identify and address vulnerabilities.

Summary

JWT (JSON Web Token) is a compact token format used to transmit information securely between parties. It consists of three parts:

  1. Header: Describes how the JWT is encoded and the signature algorithm used.

  2. Payload: Contains the claims or information to be shared.

  3. Signature: Ensures the JWT hasn't been tampered with.

How JWT Works:

  1. User sends credentials to the server.

  2. Server generates a JWT and sends it back to the user.

  3. User stores the JWT (commonly in HTTP-only cookies).

  4. User includes JWT in requests for protected resources.

  5. Server verifies JWT and processes the request if valid.

Key Points:

  • JWTs are not encrypted by default; they are encoded. To protect sensitive information, use HTTPS for transmission or encrypt the JWT.

  • HTTP-only cookies are preferred for storing JWTs due to their protection against XSS attacks.

  • Local Storage is less secure and susceptible to XSS.

  • Server-side storage is an option for additional security.

0
Subscribe to my newsletter

Read articles from Arish Ahmad directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Arish Ahmad
Arish Ahmad

Final-year BTech student specializing in software development. Completed a Flutter internship and amassed three years of project experience, crafting Threads clone, Stream Arbiter, Ticketify, and Google Docs. Proficient in C++, having successfully solved 250+ LeetCode questions.