def validate(self, data): token = data['token'] payload = _check_payload(token=token) user = _check_user(payload=payload) # Get and check 'orig_iat' orig_iat = payload.get('orig_iat') if orig_iat is None: msg = _('orig_iat field not found in token.') raise serializers.ValidationError(msg) # Verify expiration refresh_limit = \ api_settings.JWT_REFRESH_EXPIRATION_DELTA.total_seconds() expiration_timestamp = orig_iat + refresh_limit now_timestamp = unix_epoch() if now_timestamp > expiration_timestamp: msg = _('Refresh has expired.') raise serializers.ValidationError(msg) new_payload = JSONWebTokenAuthentication.jwt_create_payload(user) new_payload['orig_iat'] = orig_iat return { 'token': JSONWebTokenAuthentication.jwt_encode_payload(new_payload), 'user': user, 'issued_at': new_payload.get('iat', unix_epoch()) }
def test_valid_token__returns_new_token(call_auth_refresh_endpoint, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["exp"] = payload["iat"] + 100 # add 100 seconds to issued at time auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) refresh_response = call_auth_refresh_endpoint(auth_token) refresh_token = refresh_response.json()["token"] assert refresh_token != auth_token
def _create_authenticated_client(user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) token = JSONWebTokenAuthentication.jwt_encode_payload(payload) api_client.credentials( HTTP_AUTHORIZATION="{prefix} {token}".format( prefix=api_settings.JWT_AUTH_HEADER_PREFIX, token=token ) ) return api_client
def test_refresh_limit_expired__returns_validation_error( call_auth_refresh_endpoint, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["orig_iat"] = 0 # beginning of time auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {"non_field_errors": [_("Refresh has expired.")]} response = call_auth_refresh_endpoint(auth_token) assert response.json() == expected_output
def test_valid_token__returns_new_token_with_new_token_id( call_auth_refresh_endpoint, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) refresh_response = call_auth_refresh_endpoint(auth_token) refresh_token = refresh_response.json()["token"] refresh_token_payload = JSONWebTokenAuthentication.jwt_decode_token( refresh_token) assert refresh_token_payload["jti"] != str(payload["jti"])
def test_auth_refresh__valid_token__returns_new_token(self): payload = JSONWebTokenAuthentication.jwt_create_payload( self.active_user) payload[ 'exp'] = payload['iat'] + 100 # add 100 seconds to issued at time auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) refresh_response = call_auth_refresh_endpoint(self.client, auth_token) refresh_token = refresh_response.json()['token'] self.assertNotEqual(refresh_token, auth_token)
def test_token_with_invalid_username_returns_validation_error( user, call_auth_verify_endpoint): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["username"] = "******" auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {"non_field_errors": [_("User doesn't exist.")]} verify_response = call_auth_verify_endpoint(auth_token) assert verify_response.json() == expected_output
def test_token_without_username_returns_validation_error( user, call_auth_verify_endpoint): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload.pop("username") auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {"non_field_errors": [_("Invalid token.")]} verify_response = call_auth_verify_endpoint(auth_token) assert verify_response.json() == expected_output
def test_valid_token__returns_new_token_preserving_token_id_for_first_refresh( call_auth_refresh_endpoint, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) assert "orig_jti" not in payload refresh_response = call_auth_refresh_endpoint(auth_token) refresh_token = refresh_response.json()["token"] refresh_token_payload = JSONWebTokenAuthentication.jwt_decode_token( refresh_token) assert refresh_token_payload["orig_jti"] == str(payload["jti"])
def test_valid_token__returns_new_token_preserving_original_token_id_for_subsequent_refreshes( call_auth_refresh_endpoint, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["orig_jti"] = uuid.uuid4() auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) refresh_response = call_auth_refresh_endpoint(auth_token) refresh_token = refresh_response.json()["token"] refresh_token_payload = JSONWebTokenAuthentication.jwt_decode_token( refresh_token) assert refresh_token_payload["orig_jti"] == str(payload["orig_jti"])
def test_auth_refresh__expired_token__returns_validation_error(self): payload = JSONWebTokenAuthentication.jwt_create_payload( self.active_user) payload['iat'] = 0 payload['exp'] = 1 auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {'non_field_errors': [_('Token has expired.')]} refresh_response = call_auth_refresh_endpoint(self.client, auth_token) self.assertEqual(refresh_response.json(), expected_output)
def test_expired_token__returns_validation_error(call_auth_refresh_endpoint, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["iat"] = 0 payload["exp"] = 1 auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {"non_field_errors": [_("Token has expired.")]} refresh_response = call_auth_refresh_endpoint(auth_token) assert refresh_response.json() == expected_output
def test_expired_token_returns_validation_error(user, call_auth_verify_endpoint): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["iat"] = 0 # beginning of time payload["exp"] = 1 # one second after beginning of time auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {"non_field_errors": [_("Token has expired.")]} verify_response = call_auth_verify_endpoint(auth_token) assert verify_response.json() == expected_output
def test_token_for_inactive_user_returns_validation_error( create_user, call_auth_verify_endpoint): inactive_user = create_user(username="******", password="******", is_active=False) payload = JSONWebTokenAuthentication.jwt_create_payload(inactive_user) auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {"non_field_errors": [_("User account is disabled.")]} verify_response = call_auth_verify_endpoint(auth_token) assert verify_response.json() == expected_output
def test_auth_verify__expired_token__returns_validation_error(self): payload = JSONWebTokenAuthentication.jwt_create_payload( self.active_user) payload['iat'] = 0 # beginning of time payload['exp'] = 1 # one second after beginning of time auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {'non_field_errors': [_('Token has expired.')]} verify_response = call_auth_verify_endpoint(self.client, auth_token) self.assertEqual(verify_response.json(), expected_output)
def test_auth_verify__token_with_invalid_username__returns_validation_error( self): # create token with invalid username payload = JSONWebTokenAuthentication.jwt_create_payload( self.active_user) payload['username'] = "******" auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {'non_field_errors': [_("User doesn't exist.")]} verify_response = call_auth_verify_endpoint(self.client, auth_token) self.assertEqual(verify_response.json(), expected_output)
def test_view_returns_401_for_corrupt_signature(api_client, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) token = JSONWebTokenAuthentication.jwt_encode_payload(payload) + "x" api_client.credentials(HTTP_AUTHORIZATION="Bearer " + token) expected_output = {"detail": _("Error decoding token.")} url = reverse("test-view") response = api_client.get(url) assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.json() == expected_output
def validate(self, data): user = data["user"] payload = JSONWebTokenAuthentication.jwt_create_payload(user) check_user(payload) token = JSONWebTokenAuthentication.jwt_encode_payload(payload) return { "user": user, "token": token, "issued_at": payload.get('iat', unix_epoch()) }
def test_create_or_update_blacklist_entry(user, create_authenticated_client): url = reverse('blacklist-list') api_client = create_authenticated_client(user) # Create a different token to try to blacklist twice payload = JSONWebTokenAuthentication.jwt_create_payload(user) token = JSONWebTokenAuthentication.jwt_encode_payload(payload) api_client.post(url, data={"token": token}) assert BlacklistedToken.objects.count() == 1 api_client.post(url, data={"token": token}) assert BlacklistedToken.objects.count() == 1
def test_auth_verify__token_without_username__returns_validation_error( self): # create token without username field payload = JSONWebTokenAuthentication.jwt_create_payload( self.active_user) del payload['username'] auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {'non_field_errors': [_('Invalid token.')]} verify_response = call_auth_verify_endpoint(self.client, auth_token) self.assertEqual(verify_response.json(), expected_output)
def test_token_without_required_token_id_returns_validation_error( monkeypatch, user, call_auth_verify_endpoint ): monkeypatch.setattr(api_settings, "JWT_TOKEN_ID", "require") payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload.pop("jti") auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = {"non_field_errors": [_("Invalid token.")]} verify_response = call_auth_verify_endpoint(auth_token) assert verify_response.json() == expected_output
def test_token_is_blocked_by_id(user, monkeypatch, id_setting): monkeypatch.setattr(api_settings, "JWT_TOKEN_ID", id_setting) payload = JSONWebTokenAuthentication.jwt_create_payload(user) token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expiration = timezone.now() + timedelta(days=1) BlacklistedToken( token_id=payload['jti'], expires_at=expiration, user=user, ).save() assert BlacklistedToken.is_blocked(token, payload) is True
def test_auth_verify__token_for_inactive_user__returns_validation_error( self): # create token with invalid username payload = JSONWebTokenAuthentication.jwt_create_payload( self.inactive_user) auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = { 'non_field_errors': [_('User account is disabled.')] } verify_response = call_auth_verify_endpoint(self.client, auth_token) self.assertEqual(verify_response.json(), expected_output)
def test_token_is_not_blocked_by_value_when_ids_required(user, monkeypatch): monkeypatch.setattr(api_settings, "JWT_TOKEN_ID", "require") payload = JSONWebTokenAuthentication.jwt_create_payload(user) token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expiration = timezone.now() + timedelta(days=1) BlacklistedToken( token=token, expires_at=expiration, user=user, ).save() assert BlacklistedToken.is_blocked(token, payload) is False
def test_without_orig_iat_in_payload__returns_validation_error( call_auth_refresh_endpoint, user): # create token without orig_iat in payload payload = JSONWebTokenAuthentication.jwt_create_payload(user) del payload["orig_iat"] auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = { "non_field_errors": [_("orig_iat field not found in token.")] } response = call_auth_refresh_endpoint(auth_token) assert response.json() == expected_output
def test_blacklisted_token__returns_validation_error( call_auth_refresh_endpoint, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) BlacklistedToken.objects.create( token=auth_token, user=user, expires_at=timezone.now() - timedelta(days=7), ) expected_output = {"non_field_errors": [_("Token is blacklisted.")]} refresh_response = call_auth_refresh_endpoint(auth_token) assert refresh_response.json() == expected_output
def test_auth_refresh__without_orig_iat_in_payload__returns_validation_error( self): # create token without orig_iat in payload payload = JSONWebTokenAuthentication.jwt_create_payload( self.active_user) del payload['orig_iat'] auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = { 'non_field_errors': [_('orig_iat field not found in token.')] } response = call_auth_refresh_endpoint(self.client, auth_token) self.assertEqual(response.json(), expected_output)
def test_view_returns_401_for_bogus_issued_at(api_client, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["iat"] = 'banana' token = JSONWebTokenAuthentication.jwt_encode_payload(payload) api_client.credentials(HTTP_AUTHORIZATION="Bearer " + token) expected_output = {"detail": _("Invalid token.")} url = reverse("test-view") response = api_client.get(url) assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.json() == expected_output
def test_view_returns_401_when_username_does_not_exist(api_client, user): payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["username"] = "******" token = JSONWebTokenAuthentication.jwt_encode_payload(payload) api_client.credentials(HTTP_AUTHORIZATION="Bearer " + token) expected_output = {"detail": _("Invalid token.")} url = reverse("test-view") response = api_client.get(url) assert response.status_code == status.HTTP_401_UNAUTHORIZED assert response.json() == expected_output
def test_with_JWT_ALLOW_REFRESH_disabled__returns_validation_error( monkeypatch, call_auth_refresh_endpoint, user): monkeypatch.setattr(api_settings, "JWT_ALLOW_REFRESH", False) payload = JSONWebTokenAuthentication.jwt_create_payload(user) payload["exp"] = payload["iat"] + 100 # add 100 seconds to issued at time auth_token = JSONWebTokenAuthentication.jwt_encode_payload(payload) expected_output = { "non_field_errors": ["orig_iat field not found in token."] } refresh_response = call_auth_refresh_endpoint(auth_token) assert refresh_response.json() == expected_output