Updated `decode_token` for stricter validation of token claims and explicit error handling. Added utilities for token revocation and verification, improving
111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
import pytest
|
|
from datetime import timedelta, datetime, timezone
|
|
from jose import jwt, JWTError, ExpiredSignatureError
|
|
from app.auth.security import (
|
|
get_password_hash, verify_password,
|
|
create_access_token, create_refresh_token,
|
|
decode_token, SECRET_KEY, ALGORITHM
|
|
)
|
|
from app.schemas.token import TokenPayload
|
|
|
|
|
|
def test_password_hashing():
|
|
plain_password = "securepassword123"
|
|
hashed_password = get_password_hash(plain_password)
|
|
|
|
# Ensure hashed passwords are not the same
|
|
assert hashed_password != plain_password
|
|
# Test password verification
|
|
assert verify_password(plain_password, hashed_password)
|
|
assert not verify_password("wrongpassword", hashed_password)
|
|
|
|
|
|
def test_access_token_creation():
|
|
user_id = "123e4567-e89b-12d3-a456-426614174000"
|
|
token = create_access_token({"sub": user_id})
|
|
decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
|
|
|
assert decoded_payload.get("sub") == user_id
|
|
assert decoded_payload.get("type") == "access"
|
|
|
|
|
|
def test_refresh_token_creation():
|
|
user_id = "123e4567-e89b-12d3-a456-426614174000"
|
|
token = create_refresh_token({"sub": user_id})
|
|
decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
|
|
|
assert decoded_payload.get("sub") == user_id
|
|
assert decoded_payload.get("type") == "refresh"
|
|
|
|
|
|
def test_decode_token_valid():
|
|
user_id = "123e4567-e89b-12d3-a456-426614174000"
|
|
access_token = create_access_token({"sub": user_id})
|
|
token_payload = decode_token(access_token)
|
|
|
|
assert isinstance(token_payload, TokenPayload)
|
|
assert token_payload.sub == user_id
|
|
assert token_payload.type == "access"
|
|
|
|
|
|
def test_decode_token_expired():
|
|
user_id = "123e4567-e89b-12d3-a456-426614174000"
|
|
token = create_access_token({"sub": user_id}, expires_delta=timedelta(seconds=-1))
|
|
|
|
with pytest.raises(JWTError) as exc_info:
|
|
decode_token(token)
|
|
|
|
assert str(exc_info.value) == "Token expired. Please refresh your token to continue."
|
|
|
|
|
|
def test_decode_token_missing_exp():
|
|
# Create a token without the `exp` claim
|
|
payload = {"sub": "123e4567-e89b-12d3-a456-426614174000", "type": "access"}
|
|
token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
|
|
|
|
with pytest.raises(JWTError) as exc_info:
|
|
decode_token(token)
|
|
|
|
assert str(exc_info.value) == "Malformed token. Missing required claim."
|
|
|
|
|
|
|
|
def test_decode_token_missing_sub():
|
|
# Create a token without the `sub` claim
|
|
payload = {"exp": datetime.now().timestamp() + 60, "type": "access"}
|
|
token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
|
|
|
|
with pytest.raises(JWTError) as exc_info:
|
|
decode_token(token)
|
|
|
|
assert str(exc_info.value) == "Malformed token. Missing required claim."
|
|
|
|
|
|
def test_decode_token_invalid_signature():
|
|
# Use a different secret key for signing
|
|
token = jwt.encode({"sub": "123", "type": "access"}, "wrong_secret", algorithm=ALGORITHM)
|
|
|
|
with pytest.raises(JWTError) as exc_info:
|
|
decode_token(token)
|
|
|
|
assert str(exc_info.value) == "Invalid token."
|
|
|
|
|
|
def test_decode_token_malformed():
|
|
malformed_token = "malformed.header.payload"
|
|
|
|
with pytest.raises(JWTError) as exc_info:
|
|
decode_token(malformed_token)
|
|
|
|
assert str(exc_info.value) == "Invalid token."
|
|
|
|
|
|
def test_decode_token_invalid_type():
|
|
user_id = "123e4567-e89b-12d3-a456-426614174000"
|
|
token = create_refresh_token({"sub": user_id}) # Token type is "refresh"
|
|
|
|
with pytest.raises(JWTError) as exc_info:
|
|
decode_token(token, required_type="access") # Expecting an access token
|
|
|
|
assert str(exc_info.value) == "Invalid token type: expected 'access', got 'refresh'."
|