def _decode_and_verify_token(token, jwt_issuer):
    options = {
        'require': ["exp", "iat"],
        'verify_exp': api_settings.JWT_VERIFY_EXPIRATION,
        'verify_aud': settings.JWT_AUTH.get('JWT_VERIFY_AUDIENCE', True),
        'verify_iss':
        False,  # TODO (ARCH-204): manually verify until issuer is configured correctly.
        'verify_signature': False,  # Verified with JWS already
    }

    decoded_token = jwt.decode(
        token,
        jwt_issuer['SECRET_KEY'],
        options=options,
        leeway=api_settings.JWT_LEEWAY,
        audience=jwt_issuer['AUDIENCE'],
        issuer=jwt_issuer['ISSUER'],
        algorithms=[api_settings.JWT_ALGORITHM],
    )

    # TODO (ARCH-204): verify issuer manually until it is properly configured.
    token_issuer = decoded_token.get('iss')
    issuer_matched = any(issuer['ISSUER'] == token_issuer
                         for issuer in get_jwt_issuers())
    if not issuer_matched:
        logger.info('Token decode failed due to mismatched issuer [%s]',
                    token_issuer)
        raise jwt.InvalidTokenError('%s is not a valid issuer.' % token_issuer)

    return decoded_token
Example #2
0
def _decode_and_verify_token(token, jwt_issuer):
    options = {
        'require_exp': True,
        'require_iat': True,

        'verify_exp': api_settings.JWT_VERIFY_EXPIRATION,
        'verify_aud': settings.JWT_AUTH.get('JWT_VERIFY_AUDIENCE', True),
        'verify_iss': False,  # TODO (ARCH-204): manually verify until issuer is configured correctly.
        'verify_signature': False,  # Verified with JWS already
    }

    decoded_token = jwt.decode(
        token,
        jwt_issuer['SECRET_KEY'],
        api_settings.JWT_VERIFY,
        options=options,
        leeway=api_settings.JWT_LEEWAY,
        audience=jwt_issuer['AUDIENCE'],
        issuer=jwt_issuer['ISSUER'],
        algorithms=[api_settings.JWT_ALGORITHM],
    )

    # TODO (ARCH-204): verify issuer manually until it is properly configured.
    token_issuer = decoded_token.get('iss')
    issuer_matched = any(issuer['ISSUER'] == token_issuer for issuer in get_jwt_issuers())
    if not issuer_matched:
        logger.info('Token decode failed due to mismatched issuer [%s]', token_issuer)
        raise jwt.InvalidTokenError('%s is not a valid issuer.', token_issuer)

    return decoded_token
    def test_get_deprecated_jwt_issuers(self):
        """
        Verify the get_jwt_issuers operation returns the deprecated issuer information when current
        issuers are not configured for the system.
        """
        _deprecated = [{
            'ISSUER': settings.JWT_AUTH['JWT_ISSUER'],
            'SECRET_KEY': settings.JWT_AUTH['JWT_SECRET_KEY'],
            'AUDIENCE': settings.JWT_AUTH['JWT_AUDIENCE'],
        }]

        mock_call = 'edx_rest_framework_extensions.settings._get_current_jwt_issuers'
        with mock.patch(mock_call, mock.Mock(return_value=None)):
            with warnings.catch_warnings(record=True) as warning_list:
                self.assertEqual(get_jwt_issuers(), _deprecated)
                warnings.simplefilter("default")
                self.assertEqual(len(warning_list), 1)
                self.assertTrue(
                    issubclass(warning_list[-1].category, DeprecationWarning))
                msg = "'JWT_ISSUERS' list not defined, checking for deprecated settings."
                self.assertIn(msg, str(warning_list[-1].message))
Example #4
0
def jwt_decode_handler(token):
    """
    Decodes a JSON Web Token (JWT).

    Notes:
        * Requires "exp" and "iat" claims to be present in the token's payload.
        * Supports multiple issuer decoding via settings.JWT_AUTH['JWT_ISSUERS'] (see below)
        * Aids debugging by logging DecodeError and InvalidTokenError log entries when decoding fails.

    Examples:
        Use with `djangorestframework-jwt <https://getblimp.github.io/django-rest-framework-jwt/>`_, by changing
        your Django settings:

        .. code-block:: python

            JWT_AUTH = {
                'JWT_DECODE_HANDLER': 'edx_rest_framework_extensions.utils.jwt_decode_handler',
                'JWT_ISSUER': 'https://the.jwt.issuer',
                'JWT_SECRET_KEY': 'the-jwt-secret-key',  (defaults to settings.SECRET_KEY)
                'JWT_AUDIENCE': 'the-jwt-audience',
            }

        Enable multi-issuer support by specifying a list of dictionaries as settings.JWT_AUTH['JWT_ISSUERS']:

        .. code-block:: python

            JWT_ISSUERS = [
                    {
                        'ISSUER': 'test-issuer-1',
                        'SECRET_KEY': 'test-secret-key-1',
                        'AUDIENCE': 'test-audience-1',
                    },
                    {
                        'ISSUER': 'test-issuer-2',
                        'SECRET_KEY': 'test-secret-key-2',
                        'AUDIENCE': 'test-audience-2',
                    }
                ]

    Args:
        token (str): JWT to be decoded.

    Returns:
        dict: Decoded JWT payload.

    Raises:
        MissingRequiredClaimError: Either the exp or iat claims is missing from the JWT payload.
        InvalidTokenError: Decoding fails.
    """

    options = {
        'verify_exp': api_settings.JWT_VERIFY_EXPIRATION,
        'verify_aud': settings.JWT_AUTH.get('JWT_VERIFY_AUDIENCE', True),
        'require_exp': True,
        'require_iat': True,
    }

    for jwt_issuer in get_jwt_issuers():
        try:
            decoded = jwt.decode(token,
                                 jwt_issuer['SECRET_KEY'],
                                 api_settings.JWT_VERIFY,
                                 options=options,
                                 leeway=api_settings.JWT_LEEWAY,
                                 audience=jwt_issuer['AUDIENCE'],
                                 issuer=jwt_issuer['ISSUER'],
                                 algorithms=[api_settings.JWT_ALGORITHM])
            return decoded
        except jwt.InvalidTokenError:
            msg = "Token decode failed for issuer '{issuer}'".format(
                issuer=jwt_issuer['ISSUER'])
            logger.info(msg, exc_info=True)

    msg = 'All combinations of JWT issuers and secret keys failed to validate the token.'
    logger.error(msg)
    raise jwt.InvalidTokenError(msg)
 def test_get_current_jwt_issuers(self):
     """
     Verify the get_jwt_issuers operation returns the current issuer information when configured
     """
     self.assertEqual(get_jwt_issuers(), settings.JWT_AUTH['JWT_ISSUERS'])