JWT Authentication: Tutorial

author

By Freecoderteam

Oct 11, 2025

4

image

JWT Authentication: Tutorial

JSON Web Tokens (JWT) have become a popular choice for stateless authentication in web applications, especially in microservices and RESTful APIs. JWTs provide a secure and efficient way to manage user sessions without relying on server-side session storage. In this comprehensive tutorial, we'll explore the fundamentals of JWT, how they work, and how to implement JWT authentication in a practical application. We'll also cover best practices and actionable insights to help you secure your applications effectively.

Table of Contents


Understanding JWT

JWT is an open standard (RFC 7519) that defines a compact and self-contained way to securely transmit information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair (using RSA or ECDSA).

JWTs are widely used in authentication systems because they are:

  • Stateless: The server does not need to store session data, reducing complexity and scaling requirements.
  • Compact: JWTs are small and can be transmitted efficiently over the network.
  • Self-contained: All necessary information is embedded in the token, reducing the need for additional database queries.

How JWT Works

Here’s a simplified explanation of how JWT works in an authentication flow:

  1. User Authentication: When a user logs in, the server verifies the user's credentials (e.g., username and password).
  2. JWT Generation: If the credentials are valid, the server generates a JWT containing user information (e.g., user ID, roles, expiration time).
  3. Token Storage: The JWT is sent to the client, which typically stores it in localStorage, sessionStorage, or cookies.
  4. Token Transmission: On subsequent requests, the client sends the JWT in the Authorization header (e.g., Bearer <token>).
  5. Token Validation: The server verifies the JWT's signature, expiration, and other claims before allowing access to protected resources.

JWT Structure

A JWT is composed of three parts, separated by dots (.):

  1. Header: Metadata about the token, such as the signing algorithm (e.g., HS256 for HMAC SHA256) and the token type (JWT).
  2. Payload: Claims about the user or the request, such as user_id, role, and exp (expiration time).
  3. Signature: A hash that ensures the integrity of the token. It is generated by encoding the header and payload, signing them with a secret key or public/private key pair.

Example JWT

{
  "header": {
    "alg": "HS256",
    "typ": "JWT"
  },
  "payload": {
    "sub": "1234567890",
    "name": "John Doe",
    "role": "admin",
    "exp": 1607116200
  },
  "signature": "dJjS~B.nIZs8w2p3cD4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6"
}

When encoded, the JWT looks like this:

<base64UrlEncodedHeader>.<base64UrlEncodedPayload>.<signature>

Implementing JWT Authentication

Setup

To implement JWT authentication, we'll use Python with the fastapi framework and the python-jose library. Install the required packages:

pip install fastapi uvicorn python-jose

Generating a JWT

First, let's create a simple JWT generation function. We'll use the HS256 algorithm for signing.

from typing import Dict
from datetime import datetime, timedelta
import jwt

# Secret key for signing the JWT
SECRET_KEY = "your-very-secret-key"
ALGORITHM = "HS256"

def create_access_token(data: Dict[str, any], expires_delta: timedelta = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# Example usage
data = {"user_id": 1, "role": "admin"}
token = create_access_token(data, expires_delta=timedelta(hours=1))
print(token)

Verifying a JWT

To verify a JWT, we need to decode it and validate its signature and expiration.

def verify_token(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except jwt.ExpiredSignatureError:
        return {"error": "Token has expired"}
    except jwt.InvalidTokenError:
        return {"error": "Invalid token"}

# Example usage
token = "your_jwt_token_here"
payload = verify_token(token)
print(payload)

Integrating JWT with FastAPI

Now, let's create a simple FastAPI application that uses JWT for authentication.

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from typing import Annotated
import jwt
from datetime import datetime, timedelta

app = FastAPI()

# Secret key for signing JWT
SECRET_KEY = "your-very-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class Token(BaseModel):
    access_token: str
    token_type: str

class User(BaseModel):
    username: str
    role: str

def create_access_token(data: dict, expires_delta: timedelta = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        role: str = payload.get("role")
        if username is None:
            raise credentials_exception
        user = User(username=username, role=role)
    except jwt.ExpiredSignatureError:
        raise credentials_exception
    except jwt.InvalidTokenError:
        raise credentials_exception
    return user

@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username, "role": user.role}, expires_delta=access_token_expires
    )
    return {"access_token": access_token, "token_type": "bearer"}

@app.get("/users/me", response_model=User)
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
    return current_user

Best Practices and Security Considerations

  1. Use Strong Secret Keys: Ensure that your secret key is long, random, and kept secure. Never expose it in version control.

  2. Set Reasonable Token Lifetimes: Short-lived tokens (e.g., 15 minutes) reduce the risk of token theft. Use refresh tokens for long-term access.

  3. Use HTTPS: Always transmit JWTs over HTTPS to prevent interception.

  4. Validate All Claims: Ensure that the token's expiration (exp) and other claims are properly validated.

  5. Store Tokens Securely: Use HttpOnly cookies or localStorage with proper security attributes to prevent XSS attacks.

  6. Avoid Storing Sensitive Information in JWT: Sensitive data like passwords or private keys should never be included in JWTs.

  7. Implement Token Revocation: For critical applications, implement a token revocation mechanism to invalidate tokens prematurely.

  8. Use Multi-factor Authentication: Combine JWT with MFA for added security.


Conclusion

JWT is a powerful tool for managing authentication in modern web applications. By following best practices and using secure implementations, you can leverage JWT to build robust and scalable authentication systems. In this tutorial, we covered the fundamentals of JWT, how to generate and verify tokens, and how to integrate JWT with FastAPI. Remember to prioritize security by using strong secret keys, validating claims, and transmitting tokens securely.

JWT authentication is a foundational skill in web development, and mastering it will equip you to build secure and efficient applications. Happy coding! 😊


References:


Feel free to reach out if you have any questions or need further clarification!

Subscribe to Receive Future Updates

Stay informed about our latest updates, services, and special offers. Subscribe now to receive valuable insights and news directly to your inbox.

No spam guaranteed, So please don’t send any spam mail.