|
10 | 10 |
|
11 | 11 | from fastapi_jwt_harmony import JWTHarmony, JWTHarmonyDep, JWTHarmonyRefresh |
12 | 12 | from fastapi_jwt_harmony.config import JWTHarmonyConfig |
13 | | -from fastapi_jwt_harmony.exceptions import JWTHarmonyException |
| 13 | +from fastapi_jwt_harmony.exceptions import JWTDecodeError, JWTHarmonyException, TokenExpired |
14 | 14 | from tests.user_models import SimpleUser |
15 | 15 |
|
16 | 16 |
|
@@ -147,6 +147,39 @@ def test_get_raw_jwt(default_access_token, encoded_token): |
147 | 147 | assert auth.get_raw_jwt(encoded_token) == default_access_token |
148 | 148 |
|
149 | 149 |
|
| 150 | +def test_get_unverified_jwt(default_access_token): |
| 151 | + """Test that get_unverified_jwt decodes tokens without verification.""" |
| 152 | + # Reset configuration |
| 153 | + JWTHarmony._config = None |
| 154 | + |
| 155 | + JWTHarmony.configure(SimpleUser, JWTHarmonyConfig(secret_key='secret-key')) |
| 156 | + |
| 157 | + auth = JWTHarmony() |
| 158 | + |
| 159 | + # Test with valid token |
| 160 | + token = jwt.encode(default_access_token, 'secret-key', algorithm='HS256') |
| 161 | + result = auth.get_unverified_jwt(token) |
| 162 | + assert result == default_access_token |
| 163 | + |
| 164 | + # Test with expired token (should still decode) |
| 165 | + expired_payload = {**default_access_token, 'exp': 0} |
| 166 | + expired_token = jwt.encode(expired_payload, 'secret-key', algorithm='HS256') |
| 167 | + result = auth.get_unverified_jwt(expired_token) |
| 168 | + assert result == expired_payload |
| 169 | + |
| 170 | + # Test with invalid signature (should still decode) |
| 171 | + token_wrong_sig = jwt.encode(default_access_token, 'wrong-key', algorithm='HS256') |
| 172 | + result = auth.get_unverified_jwt(token_wrong_sig) |
| 173 | + assert result == default_access_token |
| 174 | + |
| 175 | + # Test with completely malformed token (should raise JWTDecodeError) |
| 176 | + with pytest.raises(JWTDecodeError): |
| 177 | + auth.get_unverified_jwt('invalid.token') |
| 178 | + |
| 179 | + # Test with no token provided |
| 180 | + assert auth.get_unverified_jwt() is None |
| 181 | + |
| 182 | + |
150 | 183 | def test_get_jwt_jti(client, default_access_token, encoded_token): |
151 | 184 | # Reset configuration |
152 | 185 | JWTHarmony._config = None |
@@ -339,3 +372,24 @@ def test_invalid_asymmetric_algorithms(client): |
339 | 372 | token = auth2.create_access_token(user_claims=user) |
340 | 373 | with pytest.raises(RuntimeError, match=r'public_key'): |
341 | 374 | client.get('/protected', headers={'Authorization': f'Bearer {token}'}) |
| 375 | + |
| 376 | + |
| 377 | +def test_expired_token_includes_jti(default_access_token): |
| 378 | + """Test that TokenExpired exception includes the jti from expired tokens.""" |
| 379 | + # Reset configuration |
| 380 | + JWTHarmony._config = None |
| 381 | + |
| 382 | + JWTHarmony.configure(SimpleUser, JWTHarmonyConfig(secret_key='secret-key')) |
| 383 | + |
| 384 | + auth = JWTHarmony() |
| 385 | + |
| 386 | + # Create an expired token using the fixture |
| 387 | + expired_payload = {**default_access_token, 'exp': 0} # Expired in 1970 |
| 388 | + expired_token = jwt.encode(expired_payload, 'secret-key', algorithm='HS256') |
| 389 | + |
| 390 | + # Try to verify the expired token |
| 391 | + with pytest.raises(TokenExpired, match='Token expired') as exc_info: |
| 392 | + auth.get_raw_jwt(expired_token) |
| 393 | + |
| 394 | + # Verify the exception contains the jti |
| 395 | + assert exc_info.value.jti == default_access_token['jti'] |
0 commit comments