Henry Hoang

JWT authentication test examples

JWT authentication test examples

JWT authentication test examples

  • Creating JWT tokens
  • Secured endpoints
  • Async tests
  • Token validation tests
  • Expired token tests
  • Invalid token tests

This example uses:

  • PyJWT
  • httpx.AsyncClient
  • pytest + pytest-asyncio

Example Project Structure

app/
 ├── auth.py
 ├── main.py
tests/
 └── test_jwt_auth.py

1. JWT Utility File (auth.py)

from datetime import datetime, timedelta
from typing import Optional
import jwt

SECRET_KEY = "TEST_SECRET_KEY"
ALGORITHM = "HS256"


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

2. FastAPI App With JWT Auth (main.py)

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
import jwt

from app.auth import SECRET_KEY, ALGORITHM

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


def verify_token(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload  # contains user_id or email
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")


@app.get("/secure")
async def secure_endpoint(payload: dict = Depends(verify_token)):
    return {"message": "Access granted", "payload": payload}

3. Async JWT Tests (test_jwt_auth.py)

Install requirements:

pip install pytest pytest-asyncio httpx PyJWT

Test valid JWT

import pytest
from datetime import timedelta
from httpx import AsyncClient
from app.main import app
from app.auth import create_access_token


@pytest.mark.asyncio
async def test_valid_jwt():
    token = create_access_token({"sub": "alice"}, expires_delta=timedelta(minutes=5))

    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get("/secure", headers={"Authorization": f"Bearer {token}"})

    assert response.status_code == 200
    assert response.json()["message"] == "Access granted"
    assert response.json()["payload"]["sub"] == "alice"

Test expired JWT

@pytest.mark.asyncio
async def test_expired_jwt():
    # Token expired 1 minute ago
    token = create_access_token(
        {"sub": "user"},
        expires_delta=timedelta(minutes=-1)
    )

    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get("/secure", headers={"Authorization": f"Bearer {token}"})

    assert response.status_code == 401
    assert response.json() == {"detail": "Token expired"}

Test missing token

@pytest.mark.asyncio
async def test_missing_token():
    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get("/secure")

    assert response.status_code == 401
    assert response.json()["detail"] == "Not authenticated"

Test malformed/invalid token

@pytest.mark.asyncio
async def test_invalid_token():
    invalid_token = "abc.def.ghi"

    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get(
            "/secure",
            headers={"Authorization": f"Bearer {invalid_token}"}
        )

    assert response.status_code == 401
    assert response.json() == {"detail": "Invalid token"}

Test token with wrong secret key

import jwt

@pytest.mark.asyncio
async def test_wrong_secret_jwt():
    # Token signed with a different key
    fake_token = jwt.encode({"sub": "hacker"}, "WRONG_KEY", algorithm="HS256")

    async with AsyncClient(app=app, base_url="http://test") as client:
        response = await client.get("/secure", headers={"Authorization": f"Bearer {fake_token}"})

    assert response.status_code == 401
    assert response.json() == {"detail": "Invalid token"}

Summary of What You Get

These tests validate:

TestValidates
test_valid_jwtProper access to protected routes
test_expired_jwtToken expiration handling
test_missing_tokenOAuth2 missing header logic
test_invalid_tokenToken structure errors
test_wrong_secret_jwtSignature mismatch handling

On this page