def use_jti_claim(jti_claim: str, expires_at: datetime) -> None:
    """
    Use a jti claim
    :param jti_claim: jti claim to mark as used.
    :param expires_at: the datetime at which the jti claim expires.
    :raises ValueError: when jti_claim is None.
    :raises TypeError: when jti_claim is not a valid uuid4.
    :raises JtiTokenUsed: when jti_claim has already been used.
    """
    if jti_claim is None:
        raise ValueError
    if not is_valid_uuid(jti_claim):
        logger.info("jti claim is invalid", jti_claim=jti_claim)
        raise TypeError

    try:
        # Make claim expire a little later than exp to avoid race conditions with out of sync clocks.
        expires_at += timedelta(seconds=60)

        jti = UsedJtiClaim(jti_claim, expires_at)
        current_app.eq["ephemeral_storage"].put(
            jti, overwrite=False)  # type: ignore
    except ItemAlreadyExistsError as e:
        logger.error("jti claim has already been used", jti_claim=jti_claim)
        raise JtiTokenUsed(jti_claim) from e
Exemple #2
0
    def test_duplicate_put_jti_fails(self):
        used_at = datetime.now(tz=timezone.utc)
        expires_at = used_at + timedelta(seconds=60)

        jti = UsedJtiClaim(str(uuid.uuid4()), expires_at)

        self.redis.put(jti, overwrite=False)

        with self.assertRaises(ItemAlreadyExistsError):
            self.redis.put(jti, overwrite=False)
Exemple #3
0
    def test_put_jti_stores_empty_value(self):
        used_at = datetime.now(tz=timezone.utc)
        expires_at = used_at + timedelta(seconds=60)

        jti = UsedJtiClaim(str(uuid.uuid4()), expires_at)

        self.redis.put(jti, overwrite=False)

        stored_data = self.mock_client.get(jti.jti_claim)

        self.assertEqual(b"", stored_data)
Exemple #4
0
    def test_delete_handles_connection_error_once(self):
        # Given
        used_at = datetime.now(tz=timezone.utc)
        expires_at = used_at + timedelta(seconds=60)
        jti = UsedJtiClaim(str(uuid.uuid4()), expires_at)

        self.redis.client.delete = mock.Mock(
            side_effect=[RedisConnectionError, RedisConnectionError])

        # When
        with self.assertRaises(RedisConnectionError):
            self.redis.delete(jti)

        # Then
        assert self.redis.client.delete.call_count == 2
Exemple #5
0
    def test_put_handles_connection_error_once(self):
        # Given
        used_at = datetime.now()
        expires_at = used_at + timedelta(seconds=60)
        jti = UsedJtiClaim(str(uuid.uuid4()), expires_at)

        self.redis.client.set = mock.Mock(
            side_effect=[RedisConnectionError, RedisConnectionError])

        # When
        with self.assertRaises(RedisConnectionError):
            self.redis.put(jti, overwrite=False)

        # Then
        assert self.redis.client.set.call_count == 2
Exemple #6
0
def test_used_jti_claim():
    model = UsedJtiClaim("claimid", NOW)

    assert create_model(model).__dict__ == model.__dict__
Exemple #7
0
 def test_used_jti_claim(self):
     self._test_model(UsedJtiClaim("claimid", NOW))