def test_report_unknown_issuer(self): token = self.create_auth_token(self.user, 'non-existant-issuer', 'some-secret') with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert ctx.exception.detail == 'Unknown JWT iss (issuer).'
def test_report_token_without_issuer(self): payload = self.auth_token_payload(self.user, 'some-issuer') del payload['iss'] token = self.encode_token_payload(payload, 'some-secret') with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert ctx.exception.detail == 'JWT iss (issuer) claim is missing.'
def test_expired_token(self): api_key = self.create_api_key(self.user) payload = self.auth_token_payload(self.user, api_key.key) payload['exp'] = (datetime.utcnow() - timedelta(seconds=10)) token = self.encode_token_payload(payload, api_key.secret) with self.assertRaises(jwt.ExpiredSignatureError): jwt_auth.jwt_decode_handler(token)
def test_missing_expiration(self): api_key = self.create_api_key(self.user) payload = self.auth_token_payload(self.user, api_key.key) del payload['exp'] token = self.encode_token_payload(payload, api_key.secret) with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert ctx.exception.detail == 'Invalid JWT: Token is missing the "exp" claim.'
def test_missing_expiration(self): api_key = self.create_api_key(self.user) payload = self.auth_token_payload(self.user, api_key.key) del payload['exp'] token = self.encode_token_payload(payload, api_key.secret) with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert (ctx.exception.detail == 'Invalid JWT: Token is missing the "exp" claim.')
def test_incorrect_signature(self): api_key = self.create_api_key(self.user) token = self.create_auth_token(api_key.user, api_key.key, api_key.secret) decoy_api_key = self.create_api_key( self.user, key='another-issuer', secret='another-secret') with self.assertRaises(jwt.DecodeError) as ctx: jwt_auth.jwt_decode_handler( token, get_api_key=lambda **k: decoy_api_key) assert ctx.exception.message == 'Signature verification failed'
def test_invalid_issued_at_time_not_number(self): api_key = self.create_api_key(self.user) payload = self.auth_token_payload(self.user, api_key.key) # Simulate clock skew... payload['iat'] = 'thisisnotanumber' token = self.encode_token_payload(payload, api_key.secret) with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert ctx.exception.detail.startswith( 'JWT iat (issued at time) is invalid.')
def test_incorrect_signature(self): api_key = self.create_api_key(self.user) token = self.create_auth_token(api_key.user, api_key.key, api_key.secret) decoy_api_key = APIKey( # Don't save in database, it would conflict. user=self.user, key=api_key.key, secret='another-secret') with self.assertRaises(jwt.DecodeError) as ctx: jwt_auth.jwt_decode_handler( token, get_api_key=lambda **k: decoy_api_key) assert str(ctx.exception) == 'Signature verification failed'
def test_disallow_long_expirations(self): api_key = self.create_api_key(self.user) payload = self.auth_token_payload(self.user, api_key.key) payload['exp'] = ( datetime.utcnow() + timedelta(seconds=settings.MAX_APIKEY_JWT_AUTH_TOKEN_LIFETIME) + timedelta(seconds=1)) token = self.encode_token_payload(payload, api_key.secret) with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert ctx.exception.detail == 'JWT exp (expiration) is too long.'
def test_disallow_long_expirations(self): api_key = self.create_api_key(self.user) payload = self.auth_token_payload(self.user, api_key.key) payload['exp'] = ( datetime.utcnow() + timedelta(seconds=settings.MAX_APIKEY_JWT_AUTH_TOKEN_LIFETIME) + timedelta(seconds=1) ) token = self.encode_token_payload(payload, api_key.secret) with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert ctx.exception.detail == 'JWT exp (expiration) is too long.'
def test_invalid_issued_at_time(self): api_key = self.create_api_key(self.user) payload = self.auth_token_payload(self.user, api_key.key) # Simulate clock skew: payload['iat'] = ( datetime.utcnow() + timedelta(seconds=settings.JWT_AUTH['JWT_LEEWAY'] + 10)) token = self.encode_token_payload(payload, api_key.secret) with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert ctx.exception.detail.startswith( 'JWT iat (issued at time) is invalid.')
def test_invalid_issued_at_time(self): api_key = self.create_api_key(self.user) payload = self.auth_token_payload(self.user, api_key.key) # Simulate clock skew... payload['iat'] = datetime.utcnow() + timedelta( seconds=settings.JWT_AUTH['JWT_LEEWAY'] + 10) token = self.encode_token_payload(payload, api_key.secret) with self.assertRaises(AuthenticationFailed) as ctx: jwt_auth.jwt_decode_handler(token) assert ctx.exception.detail.startswith( 'JWT iat (issued at time) is invalid.')
def authenticate(self, request): """ Returns a two-tuple of `User` and token if a valid signature has been supplied using JWT-based authentication. Otherwise returns `None`. Copied from rest_framework_jwt BaseJSONWebTokenAuthentication, with the decode_handler changed to our own - because we don't want that decoder to be the default one in settings - and logging added. """ jwt_value = self.get_jwt_value(request) if jwt_value is None: return None try: payload = jwt_auth.jwt_decode_handler(jwt_value) except Exception, exc: try: # Log all exceptions log.info('JWTKeyAuthentication failed; ' 'it raised %s (%s)', exc.__class__.__name__, exc) # Re-raise to deal with them properly. raise exc except jwt.ExpiredSignature: msg = ugettext('Signature has expired.') raise exceptions.AuthenticationFailed(msg) except jwt.DecodeError: msg = ugettext('Error decoding signature.') raise exceptions.AuthenticationFailed(msg) except jwt.InvalidTokenError: msg = ugettext('Invalid JWT Token.') raise exceptions.AuthenticationFailed(msg)
def authenticate(self, request): """ Returns a two-tuple of `User` and token if a valid signature has been supplied using JWT-based authentication. Otherwise returns `None`. Copied from rest_framework_jwt BaseJSONWebTokenAuthentication, with the decode_handler changed to our own - because we don't want that decoder to be the default one in settings - and logging added. """ jwt_value = self.get_jwt_value(request) if jwt_value is None: return None try: payload = jwt_auth.jwt_decode_handler(jwt_value) except Exception, exc: try: # Log all exceptions log.info('JWTKeyAuthentication failed; ' 'it raised %s (%s)', exc.__class__.__name__, exc) # Re-raise to deal with them properly. raise exc except jwt.ExpiredSignature: msg = _('Signature has expired.') raise exceptions.AuthenticationFailed(msg) except jwt.DecodeError: msg = _('Error decoding signature.') raise exceptions.AuthenticationFailed(msg) except jwt.InvalidTokenError: msg = _('Invalid JWT Token.') raise exceptions.AuthenticationFailed(msg)
def authenticate(self, request): """ Returns a two-tuple of `User` and token if a valid signature has been supplied using JWT-based authentication. Otherwise returns `None`. Copied from rest_framework_jwt BaseJSONWebTokenAuthentication, with the decode_handler changed to our own - because we don't want that decoder to be the default one in settings - and logging added. """ jwt_value = self.get_jwt_value(request) if jwt_value is None: return None try: payload = jwt_auth.jwt_decode_handler(jwt_value) except Exception as exc: try: # Log all exceptions log.info('JWTKeyAuthentication failed; ' 'it raised %s (%s)', exc.__class__.__name__, exc) # Re-raise to deal with them properly. raise exc except TypeError: msg = ugettext('Wrong type for one or more keys in payload') raise exceptions.AuthenticationFailed(msg) except jwt.ExpiredSignature: msg = ugettext('Signature has expired.') raise exceptions.AuthenticationFailed(msg) except jwt.DecodeError: msg = ugettext('Error decoding signature.') raise exceptions.AuthenticationFailed(msg) except jwt.InvalidTokenError: msg = ugettext('Invalid JWT Token.') raise exceptions.AuthenticationFailed(msg) # Note: AuthenticationFailed can also be raised directly from our # jwt_decode_handler. user = self.authenticate_credentials(payload) # Send user_logged_in signal when JWT is used to authenticate an user. # Otherwise, we'd never update the last_login information for users # who never visit the site but do use the API to upload new add-ons. user_logged_in.send(sender=self.__class__, request=request, user=user) return (user, jwt_value)
def authenticate(self, request): """ Returns a two-tuple of `User` and token if a valid signature has been supplied using JWT-based authentication. Otherwise returns `None`. Copied from rest_framework_jwt BaseJSONWebTokenAuthentication, with the decode_handler changed to our own - because we don't want that decoder to be the default one in settings - and logging added. """ jwt_value = self.get_jwt_value(request) if jwt_value is None: return None try: payload = jwt_auth.jwt_decode_handler(jwt_value) except Exception as exc: try: # Log all exceptions log.info('JWTKeyAuthentication failed; ' 'it raised %s (%s)', exc.__class__.__name__, exc) # Re-raise to deal with them properly. raise exc except jwt.ExpiredSignature: msg = ugettext('Signature has expired.') raise exceptions.AuthenticationFailed(msg) except jwt.DecodeError: msg = ugettext('Error decoding signature.') raise exceptions.AuthenticationFailed(msg) except jwt.InvalidTokenError: msg = ugettext('Invalid JWT Token.') raise exceptions.AuthenticationFailed(msg) # Note: AuthenticationFailed can also be raised directly from our # jwt_decode_handler. user = self.authenticate_credentials(payload) # Send user_logged_in signal when JWT is used to authenticate an user. # Otherwise, we'd never update the last_login information for users # who never visit the site but do use the API to upload new add-ons. user_logged_in.send(sender=self.__class__, request=request, user=user) return (user, jwt_value)
def test_decode_invalid_non_ascii_token(self): with self.assertRaises(jwt.DecodeError) as ctx: jwt_auth.jwt_decode_handler(u'Ivan Krsti\u0107') assert str(ctx.exception) == 'Not enough segments'
def test_decode_garbage_token(self): with self.assertRaises(jwt.DecodeError) as ctx: jwt_auth.jwt_decode_handler('}}garbage{{') assert str(ctx.exception) == 'Not enough segments'