JWT - CSU677 - Shoolini U

JWT

1. Introduction to JSON Web Tokens (JWT)

JSON Web Tokens (JWT) are a compact, URL-safe means of representing claims to be transferred between two parties. JWTs are often used for authentication and authorization in web applications, enabling secure data exchange. The tokens are digitally signed, ensuring their integrity and authenticity.

2. Structure of a JWT

A JWT consists of three parts: a header, a payload, and a signature. These parts are encoded in Base64URL and concatenated with periods to form the complete token.

2.1 Header

The header typically consists of two parts: the type of token (JWT) and the signing algorithm being used, such as HMAC SHA256 or RSA.

2.1.1 Example: JWT Header

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

2.2 Payload

The payload contains the claims, which are statements about an entity (typically the user) and additional data. There are three types of claims: registered, public, and private. Registered claims include standard fields like iss (issuer), exp (expiration time), and sub (subject). Public claims can be defined by those using the JWT, while private claims are custom claims agreed upon by the parties.

2.2.1 Example: JWT Payload

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}

2.3 Signature

The signature is created by taking the encoded header, the encoded payload, a secret, and the algorithm specified in the header. The signature is used to verify the token's integrity, ensuring that the token has not been altered after it was issued.

2.3.1 Example: JWT Signature

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

2.4 Complete JWT Example

A complete JWT looks like this:


eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

3. How JWTs Work

JWTs are used in authentication and authorization processes as follows:

3.1 Authentication

When a user logs in, the server generates a JWT containing the user’s identity and other claims, signs it, and sends it to the client. The client stores the JWT, typically in local storage or a cookie, and includes it in subsequent requests to access protected resources.

3.1.1 Example: Authentication Flow with JWT

1. User submits login credentials.
2. Server verifies credentials and creates a JWT.
3. Server sends JWT to the client.
4. Client stores the JWT and includes it in requests to the server.

3.2 Authorization

For each request to a protected resource, the client sends the JWT in the HTTP Authorization header. The server verifies the JWT’s signature and checks the claims (e.g., roles, permissions) to determine whether the user is authorized to access the requested resource.

3.2.1 Example: Authorization Header with JWT

GET /protected-resource HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

4. JWT Claims

Claims are the statements about an entity (typically the user) and additional metadata. JWTs can include several types of claims:

4.1 Registered Claims

Registered claims are predefined and standardized by the JWT specification to provide a consistent way to handle common information:

4.2 Public Claims

Public claims are custom claims that can be defined by the users of the JWT. However, to avoid collisions with other users, these should be namespaced (e.g., https://example.com/claims/user_roles).

4.3 Private Claims

Private claims are used to share information between parties that agree on using them and are not registered or public claims. They are specific to the application's domain and can represent anything the application needs, such as user roles or permissions.

5. Securing JWTs

While JWTs are a secure way to transmit information, proper implementation is crucial to maintaining security:

5.1 Signing Algorithms

JWTs should always be signed to ensure their integrity and authenticity. The most commonly used algorithms are HMAC SHA256 (HS256) and RSA SHA256 (RS256). It is important to choose an algorithm appropriate for the application’s security requirements.

5.2 Storing JWTs Securely

JWTs should be stored securely on the client side to prevent unauthorized access. Avoid storing JWTs in local storage if the application is vulnerable to XSS attacks. Instead, consider using secure cookies with the HttpOnly and Secure flags.

5.3 Token Expiration

JWTs should include an expiration time (exp) to limit the period during which they are valid. Short-lived tokens reduce the risk of misuse, especially in cases where a token is leaked or stolen.

5.4 Token Revocation

JWTs are stateless, meaning they cannot be revoked directly by the server. To handle token revocation, consider implementing a blacklist or a token revocation strategy, where compromised tokens are invalidated by the server.

5.4.1 Example: Implementing a JWT Blacklist

blacklisted_tokens = set()

def is_token_blacklisted(token):
    return token in blacklisted_tokens

def blacklist_token(token):
    blacklisted_tokens.add(token)

6. JWT Best Practices

Following best practices ensures that JWTs are used securely and effectively:

6.1 Use HTTPS

Always transmit JWTs over HTTPS to protect them from being intercepted by man-in-the-middle attacks.

6.2 Avoid Sensitive Data

JWTs are not encrypted by default, so avoid including sensitive information (like passwords or personally identifiable information) in the token payload. If sensitive data must be included, consider encrypting the JWT.

6.3 Validate Tokens Carefully

Always validate the JWT’s signature, claims, and expiration time before processing any request. Ensure that the token was issued by a trusted source and is intended for the current audience.

6.4 Implement Refresh Tokens

For long-lived sessions, consider using short-lived JWT s along with refresh tokens. Refresh tokens can be used to obtain new JWTs without requiring the user to log in again.

6.4.1 Example: Refresh Token Workflow

1. User logs in and receives a JWT and a refresh token.
2. When the JWT expires, the client sends the refresh token to the server.
3. The server validates the refresh token and issues a new JWT.
4. The client continues to use the new JWT for subsequent requests.

7. JWT in Modern Applications

JWTs are commonly used in modern web applications, particularly in single-page applications (SPAs) and microservices architectures:

7.1 JWT in Single-Page Applications (SPAs)

In SPAs, JWTs are often used for managing user sessions. The token is stored on the client side and included in each API request to authenticate the user. This approach eliminates the need for server-side sessions and simplifies scaling.

7.1.1 Example: Using JWT in an SPA

// Assuming the JWT is stored in localStorage
function getAuthHeader() {
    const token = localStorage.getItem('jwt');
    return token ? `Bearer ${token}` : '';
}

// Use the JWT in an API request
fetch('/api/protected-resource', {
    method: 'GET',
    headers: {
        'Authorization': getAuthHeader()
    }
})
.then(response => response.json())
.then(data => console.log(data));

7.2 JWT in Microservices

In microservices architectures, JWTs are used to propagate identity and authorization information between services. Each microservice verifies the JWT and uses the claims to enforce access control.

7.2.1 Example: JWT in a Microservices Environment

1. User authenticates and receives a JWT.
2. User sends the JWT with each request to a microservice.
3. The microservice verifies the JWT and extracts user information from the claims.
4. The microservice processes the request based on the user’s roles and permissions.