Exemplo n.º 1
0
 def test_fetching_oidc_keys(self):
     """Test to fetch OpenID Connect formatted keys."""
     test_jwt = create_mock_jwt(
         MOCK_RSA_PRIVATE_KEY_PEM, algorithm=OIDC_JWT_ALGORITHM,
         key_id='oidc_1234', audience=OIDC_VALID_AUDIENCE,
         issuer=OIDC_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, OIDC_PUBLIC_KEY_URL)
     self.assertIsInstance(public_key, _RSAPublicKey)
Exemplo n.º 2
0
 def test_fetching_oidc_keys(self):
     """Test to fetch OpenID Connect formatted keys."""
     test_jwt = create_mock_jwt(MOCK_RSA_PRIVATE_KEY_PEM,
                                algorithm=OIDC_JWT_ALGORITHM,
                                key_id='oidc_1234',
                                audience=OIDC_VALID_AUDIENCE,
                                issuer=OIDC_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, OIDC_PUBLIC_KEY_URL)
     self.assertIsInstance(public_key, _RSAPublicKey)
Exemplo n.º 3
0
 def test_invalid_audience_raises_jwt_validation_error(self):
     """Test to validate a JWT with wrong audience."""
     test_jwt = create_mock_jwt(
         MOCK_EC_PRIVATE_KEY, algorithm=IAP_JWT_ALGORITHM, key_id='iap_1234',
         audience=IAP_VALID_AUDIENCE, issuer=IAP_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)
     self.assertRaises(
         JwtValidationError, validate_jwt, test_jwt, public_key,
         IAP_JWT_ALGORITHM, IAP_INVALID_AUDIENCE, IAP_VALID_ISSUER)
Exemplo n.º 4
0
 def _test_header_raises_jwt_validation_error(self, header):
     """Test JWT with supplied header."""
     test_jwt = create_mock_jwt(
         MOCK_EC_PRIVATE_KEY, algorithm=IAP_JWT_ALGORITHM, key_id='iap_1234',
         audience=IAP_VALID_AUDIENCE, issuer=IAP_VALID_ISSUER, header=header)
     public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)
     self.assertRaises(
         JwtValidationError, validate_jwt, test_jwt, public_key,
         IAP_JWT_ALGORITHM, IAP_VALID_AUDIENCE, IAP_VALID_ISSUER)
Exemplo n.º 5
0
 def test_invalid_audience_raises_jwt_validation_error(self):
     """Test to validate a JWT with wrong audience."""
     test_jwt = create_mock_jwt(MOCK_EC_PRIVATE_KEY,
                                algorithm=IAP_JWT_ALGORITHM,
                                key_id='iap_1234',
                                audience=IAP_VALID_AUDIENCE,
                                issuer=IAP_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)
     self.assertRaises(JwtValidationError, decode_jwt, test_jwt, public_key,
                       IAP_JWT_ALGORITHM, IAP_INVALID_AUDIENCE)
Exemplo n.º 6
0
 def test_valid_jwt(self):
     """Test to validate a valid JWT."""
     test_jwt = create_mock_jwt(
         MOCK_EC_PRIVATE_KEY, algorithm=IAP_JWT_ALGORITHM, key_id='iap_1234',
         audience=IAP_VALID_AUDIENCE, issuer=IAP_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)
     valid_jwt = validate_jwt(
         test_jwt, public_key, IAP_JWT_ALGORITHM, IAP_VALID_AUDIENCE,
         IAP_VALID_ISSUER)
     self.assertIsInstance(valid_jwt, dict)
     self.assertEqual(valid_jwt.get('email'), '*****@*****.**')
Exemplo n.º 7
0
 def test_valid_oidc_jwt(self):
     """Test to validate a valid OpenID Connect JWT."""
     test_jwt = create_mock_jwt(
         MOCK_RSA_PRIVATE_KEY_PEM, algorithm=OIDC_JWT_ALGORITHM,
         key_id='oidc_1234', audience=OIDC_VALID_AUDIENCE,
         issuer=OIDC_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, OIDC_PUBLIC_KEY_URL)
     valid_jwt = validate_jwt(
         test_jwt, public_key, OIDC_JWT_ALGORITHM, OIDC_VALID_AUDIENCE,
         OIDC_VALID_ISSUER)
     self.assertIsInstance(valid_jwt, dict)
     self.assertEqual(valid_jwt.get('email'), '*****@*****.**')
Exemplo n.º 8
0
 def _test_header_raises_jwt_validation_error(self, header):
     """Test JWT with supplied header."""
     test_jwt = create_mock_jwt(MOCK_EC_PRIVATE_KEY,
                                algorithm=IAP_JWT_ALGORITHM,
                                key_id='iap_1234',
                                audience=IAP_VALID_AUDIENCE,
                                issuer=IAP_VALID_ISSUER,
                                header=header)
     public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)
     self.assertRaises(JwtValidationError, validate_jwt, test_jwt,
                       public_key, IAP_JWT_ALGORITHM, IAP_VALID_AUDIENCE,
                       IAP_VALID_ISSUER)
Exemplo n.º 9
0
 def test_valid_oidc_jwt(self):
     """Test to validate a valid OpenID Connect JWT."""
     test_jwt = create_mock_jwt(MOCK_RSA_PRIVATE_KEY_PEM,
                                algorithm=OIDC_JWT_ALGORITHM,
                                key_id='oidc_1234',
                                audience=OIDC_VALID_AUDIENCE,
                                issuer=OIDC_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, OIDC_PUBLIC_KEY_URL)
     valid_jwt = validate_jwt(test_jwt, public_key, OIDC_JWT_ALGORITHM,
                              OIDC_VALID_AUDIENCE, OIDC_VALID_ISSUER)
     self.assertIsInstance(valid_jwt, dict)
     self.assertEqual(valid_jwt.get('email'), '*****@*****.**')
Exemplo n.º 10
0
 def test_valid_jwt(self):
     """Test to validate a valid JWT."""
     test_jwt = create_mock_jwt(MOCK_EC_PRIVATE_KEY,
                                algorithm=IAP_JWT_ALGORITHM,
                                key_id='iap_1234',
                                audience=IAP_VALID_AUDIENCE,
                                issuer=IAP_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)
     valid_jwt = validate_jwt(test_jwt, public_key, IAP_JWT_ALGORITHM,
                              IAP_VALID_AUDIENCE, IAP_VALID_ISSUER)
     self.assertIsInstance(valid_jwt, dict)
     self.assertEqual(valid_jwt.get('email'), '*****@*****.**')
Exemplo n.º 11
0
 def _test_payload_raises_jwt_validation_error(self, payload, domain=None):
     """Test JWT with supplied payload."""
     test_jwt = create_mock_jwt(MOCK_EC_PRIVATE_KEY,
                                algorithm=IAP_JWT_ALGORITHM,
                                key_id='iap_1234',
                                audience=IAP_VALID_AUDIENCE,
                                issuer=IAP_VALID_ISSUER,
                                payload=payload)
     public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)
     with self.assertRaises(JwtValidationError):
         test_decoded_jwt = decode_jwt(test_jwt, public_key,
                                       IAP_JWT_ALGORITHM,
                                       IAP_VALID_AUDIENCE)
         validate_jwt(test_decoded_jwt, IAP_VALID_ISSUER, domain)
Exemplo n.º 12
0
 def test_valid_domain(self):
     """Test to validate a JWT with domain."""
     valid_domain = 'example.com'
     test_jwt = create_mock_jwt(MOCK_EC_PRIVATE_KEY,
                                algorithm=IAP_JWT_ALGORITHM,
                                key_id='iap_1234',
                                audience=IAP_VALID_AUDIENCE,
                                issuer=IAP_VALID_ISSUER)
     public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)
     test_decoded_jwt = decode_jwt(test_jwt, public_key, IAP_JWT_ALGORITHM,
                                   IAP_VALID_AUDIENCE)
     validate_jwt(test_decoded_jwt, IAP_VALID_ISSUER, valid_domain)
     self.assertIsInstance(test_decoded_jwt, dict)
     self.assertEqual(test_decoded_jwt.get('hd'), 'example.com')
Exemplo n.º 13
0
    def test_invalid_algorithm_raises_jwt_validation_error(self):
        """Test to validate a JWT with invalid algorithm."""

        # Hard coding a JWT with MOCK_EC_PRIVATE_KEY as key and "HS256" as alg
        # in the header. Newer versions of PyJWT won't encode JWTs with this
        # configuration.
        test_jwt = (
            b'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImlhcF8xMjM0In0.eyJzd'
            b'WIiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIiwiaGQiOi'
            b'JleGFtcGxlLmNvbSIsImlhdCI6MTY1NzU5NDE4NSwiZXhwIjoxNjU3NTk0Nzg1LCJ'
            b'hdWQiOiIvcHJvamVjdHMvMTIzNC9nbG9iYWwvYmFja2VuZFNlcnZpY2VzLzEyMzQi'
            b'LCJpc3MiOiJodHRwczovL2Nsb3VkLmdvb2dsZS5jb20vaWFwIn0.s49RJ_Fhoaqpo'
            b'GHfXTjEi5Ma373Zr69BU8rG3ZObNq0EJJXGgBq4E48LwaD_WMR4z3dMxv-UkcShmU'
            b'3p6qnv7w')

        public_key = get_public_key_for_jwt(test_jwt, IAP_PUBLIC_KEY_URL)

        with self.assertRaises(JwtValidationError):
            test_decoded_jwt = decode_jwt(test_jwt, public_key,
                                          IAP_JWT_ALGORITHM,
                                          IAP_VALID_AUDIENCE)
            validate_jwt(test_decoded_jwt, IAP_VALID_ISSUER)
Exemplo n.º 14
0
def login():
    """Handler for the login page view.

    There are three ways of authentication.
    1) Google Cloud Identity-Aware Proxy.
    2) If Single Sign On (SSO) is enabled in the configuration and the
       environment variable is present, e.g. REMOTE_USER then the system will
       get or create the user object and setup a session for the user.
    3) Local authentication is used if SSO login is not enabled. This will
       authenticate the user against the local user database.

    Returns:
        Redirect if authentication is successful or template with context
        otherwise.
    """
    # Google OpenID Connect authentication.
    if current_app.config.get('GOOGLE_OIDC_ENABLED', False):
        hosted_domain = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN')
        return redirect(get_oauth2_authorize_url(hosted_domain))

    # Google Identity-Aware Proxy authentication (using JSON Web Tokens)
    if current_app.config.get('GOOGLE_IAP_ENABLED', False):
        encoded_jwt = request.environ.get('HTTP_X_GOOG_IAP_JWT_ASSERTION',
                                          None)
        if encoded_jwt:
            expected_audience = current_app.config.get('GOOGLE_IAP_AUDIENCE')
            expected_issuer = current_app.config.get('GOOGLE_IAP_ISSUER')
            algorithm = current_app.config.get('GOOGLE_IAP_ALGORITHM')
            url = current_app.config.get('GOOGLE_IAP_PUBLIC_KEY_URL')
            try:
                public_key = get_public_key_for_jwt(encoded_jwt, url)
                decoded_jwt = decode_jwt(encoded_jwt, public_key, algorithm,
                                         expected_audience)
                validate_jwt(decoded_jwt, expected_issuer)
                email = decoded_jwt.get('email')
                if email:
                    user = User.get_or_create(username=email, name=email)
                    login_user(user)

            except (ImportError, NameError, UnboundLocalError):
                raise

            except (JwtValidationError, JwtKeyError, Exception) as e:  # pylint: disable=broad-except
                current_app.logger.error('{}'.format(e))

    # SSO login based on environment variable, e.g. REMOTE_USER.
    if current_app.config.get('SSO_ENABLED', False):
        remote_user_env = current_app.config.get('SSO_USER_ENV_VARIABLE',
                                                 'REMOTE_USER')
        sso_group_env = current_app.config.get('SSO_GROUP_ENV_VARIABLE', None)

        remote_user = request.environ.get(remote_user_env, None)
        if remote_user:
            user = User.get_or_create(username=remote_user, name=remote_user)
            login_user(user)

        # If we get groups from the SSO system create the group(s) in
        # Timesketch and add/remove the user from it.
        if sso_group_env:
            groups_string = request.environ.get(sso_group_env, '')
            separator = current_app.config.get('SSO_GROUP_SEPARATOR', ';')
            not_member_sign = current_app.config.get(
                'SSO_GROUP_NOT_MEMBER_SIGN', None)
            for group_name in groups_string.split(separator):
                remove_group = False
                if not_member_sign:
                    remove_group = group_name.startswith(not_member_sign)
                    group_name = group_name.lstrip(not_member_sign)

                # Get or create the group in the Timesketch database.
                group = Group.get_or_create(name=group_name)

                if remove_group:
                    if group in user.groups:
                        user.groups.remove(group)
                else:
                    if group not in user.groups:
                        user.groups.append(group)
            # Commit the changes to the database.
            db_session.commit()

    # Login form POST
    form = UsernamePasswordForm()
    if form.validate_on_submit:
        user = User.query.filter_by(username=form.username.data).first()
        if user:
            if user.check_password(plaintext=form.password.data):
                login_user(user)

    # Log the user in and setup the session.
    if current_user.is_authenticated:
        return redirect(request.args.get('next') or '/')

    return render_template('login.html', form=form)
Exemplo n.º 15
0
def google_openid_connect():
    """Handler for the Google OpenID Connect callback.

    Reference:
    https://developers.google.com/identity/protocols/OpenIDConnect

    Returns:
        Redirect response.
    """
    error = request.args.get('error', None)

    if error:
        current_app.logger.error('OAuth2 flow error: {}'.format(error))
        return abort(HTTP_STATUS_CODE_BAD_REQUEST,
                     'OAuth2 flow error: {0!s}'.format(error))

    try:
        code = request.args['code']
        client_csrf_token = request.args.get('state')
        server_csrf_token = session[CSRF_KEY]
    except KeyError as e:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST,
                     'Client CSRF error, no CSRF key stored')

    if client_csrf_token != server_csrf_token:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Invalid CSRF token')

    try:
        encoded_jwt = get_encoded_jwt_over_https(code)
    except JwtFetchError as e:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST,
                     'Jwt Fetch error, {0!s}'.format(e))

    try:
        discovery_document = get_oauth2_discovery_document()
    except DiscoveryDocumentError as e:
        return abort(
            HTTP_STATUS_CODE_BAD_REQUEST,
            'Unable to discover document, with error: {0!s}'.format(e))

    algorithm = discovery_document['id_token_signing_alg_values_supported'][0]
    expected_audience = current_app.config.get('GOOGLE_OIDC_CLIENT_ID')
    expected_domain = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN')
    expected_issuer = discovery_document['issuer']

    # Fetch the public key and try to validate the JWT.
    try:
        public_key = get_public_key_for_jwt(encoded_jwt,
                                            discovery_document['jwks_uri'])
        decoded_jwt = decode_jwt(encoded_jwt, public_key, algorithm,
                                 expected_audience)
        validate_jwt(decoded_jwt, expected_issuer, expected_domain)
    except (JwtValidationError, JwtKeyError) as e:
        current_app.logger.error('{}'.format(e))
        return abort(HTTP_STATUS_CODE_UNAUTHORIZED,
                     'Unable to validate request, with error: {0!s}'.format(e))

    validated_email = decoded_jwt.get('email')
    user_whitelist = current_app.config.get('GOOGLE_OIDC_USER_WHITELIST')

    # Check if the authenticating user is on the whitelist.
    if user_whitelist:
        if validated_email not in user_whitelist:
            return abort(HTTP_STATUS_CODE_UNAUTHORIZED,
                         'Unauthorized request, user not in whitelist')

    user = User.get_or_create(username=validated_email, name=validated_email)
    login_user(user)

    # Log the user in and setup the session.
    if current_user.is_authenticated:
        return redirect(request.args.get('next') or '/')

    return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'User is not authenticated.')
Exemplo n.º 16
0
def login():
    """Handler for the login page view.

    There are three ways of authentication.
    1) Google Cloud Identity-Aware Proxy.
    2) If Single Sign On (SSO) is enabled in the configuration and the
       environment variable is present, e.g. REMOTE_USER then the system will
       get or create the user object and setup a session for the user.
    3) Local authentication is used if SSO login is not enabled. This will
       authenticate the user against the local user database.

    Returns:
        Redirect if authentication is successful or template with context
        otherwise.
    """
    # Google OpenID Connect authentication.
    if current_app.config.get('GOOGLE_OIDC_ENABLED', False):
        hosted_domain = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN')
        return redirect(get_oauth2_authorize_url(hosted_domain))

    # Google Identity-Aware Proxy authentication (using JSON Web Tokens)
    if current_app.config.get('GOOGLE_IAP_ENABLED', False):
        encoded_jwt = request.environ.get(
            'HTTP_X_GOOG_IAP_JWT_ASSERTION', None)
        if encoded_jwt:
            expected_audience = current_app.config.get('GOOGLE_IAP_AUDIENCE')
            expected_issuer = current_app.config.get('GOOGLE_IAP_ISSUER')
            algorithm = current_app.config.get('GOOGLE_IAP_ALGORITHM')
            url = current_app.config.get('GOOGLE_IAP_PUBLIC_KEY_URL')
            try:
                public_key = get_public_key_for_jwt(encoded_jwt, url)
                validated_jwt = validate_jwt(
                    encoded_jwt, public_key, algorithm, expected_audience,
                    expected_issuer)
                email = validated_jwt.get('email')
                if email:
                    user = User.get_or_create(username=email, name=email)
                    login_user(user)

            except (ImportError, NameError, UnboundLocalError):  # pylint: disable=try-except-raise
                raise

            except (JwtValidationError, JwtKeyError, Exception) as e:  # pylint: disable=broad-except
                current_app.logger.error('{}'.format(e))

    # SSO login based on environment variable, e.g. REMOTE_USER.
    if current_app.config.get('SSO_ENABLED', False):
        remote_user_env = current_app.config.get('SSO_USER_ENV_VARIABLE',
                                                 'REMOTE_USER')
        sso_group_env = current_app.config.get('SSO_GROUP_ENV_VARIABLE', None)

        remote_user = request.environ.get(remote_user_env, None)
        if remote_user:
            user = User.get_or_create(username=remote_user, name=remote_user)
            login_user(user)

        # If we get groups from the SSO system create the group(s) in
        # Timesketch and add/remove the user from it.
        if sso_group_env:
            groups_string = request.environ.get(sso_group_env, '')
            separator = current_app.config.get('SSO_GROUP_SEPARATOR', ';')
            not_member_sign = current_app.config.get(
                'SSO_GROUP_NOT_MEMBER_SIGN', None)
            for group_name in groups_string.split(separator):
                remove_group = False
                if not_member_sign:
                    remove_group = group_name.startswith(not_member_sign)
                    group_name = group_name.lstrip(not_member_sign)

                # Get or create the group in the Timesketch database.
                group = Group.get_or_create(name=group_name)

                if remove_group:
                    if group in user.groups:
                        user.groups.remove(group)
                else:
                    if group not in user.groups:
                        user.groups.append(group)
            # Commit the changes to the database.
            db_session.commit()

    # Login form POST
    form = UsernamePasswordForm()
    if form.validate_on_submit:
        user = User.query.filter_by(username=form.username.data).first()
        if user:
            if user.check_password(plaintext=form.password.data):
                login_user(user)

    # Log the user in and setup the session.
    if current_user.is_authenticated:
        return redirect(request.args.get('next') or '/')

    return render_template('user/login.html', form=form)
Exemplo n.º 17
0
def google_openid_connect():
    """Handler for the Google OpenID Connect callback.

    Reference:
    https://developers.google.com/identity/protocols/OpenIDConnect

    Returns:
        Redirect response.
    """
    error = request.args.get('error', None)

    if error:
        current_app.logger.error('OAuth2 flow error: {}'.format(error))
        return abort(HTTP_STATUS_CODE_BAD_REQUEST)

    try:
        code = request.args['code']
        client_csrf_token = request.args.get('state')
        server_csrf_token = session[CSRF_KEY]
    except KeyError:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST)

    if client_csrf_token != server_csrf_token:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Invalid CSRF token')

    try:
        encoded_jwt = get_encoded_jwt_over_https(code)
    except JwtFetchError:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST)

    try:
        discovery_document = get_oauth2_discovery_document()
    except DiscoveryDocumentError:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST)

    algorithm = discovery_document['id_token_signing_alg_values_supported'][0]
    expected_audience = current_app.config.get('GOOGLE_OIDC_CLIENT_ID')
    expected_domain = current_app.config.get('GOOGLE_OIDC_HOSTED_DOMAIN')
    expected_issuer = discovery_document['issuer']

    # Fetch the public key and try to validate the JWT.
    try:
        public_key = get_public_key_for_jwt(
            encoded_jwt, discovery_document['jwks_uri'])
        validated_jwt = validate_jwt(
            encoded_jwt, public_key, algorithm, expected_audience,
            expected_issuer, expected_domain)
    except (JwtValidationError, JwtKeyError) as e:
        current_app.logger.error('{}'.format(e))
        return abort(HTTP_STATUS_CODE_UNAUTHORIZED)

    validated_email = validated_jwt.get('email')
    user_whitelist = current_app.config.get('GOOGLE_OIDC_USER_WHITELIST')

    # Check if the authenticating user is on the whitelist.
    if user_whitelist:
        if validated_email not in user_whitelist:
            return abort(HTTP_STATUS_CODE_UNAUTHORIZED)

    user = User.get_or_create(username=validated_email, name=validated_email)
    login_user(user)

    # Log the user in and setup the session.
    if current_user.is_authenticated:
        return redirect(request.args.get('next') or '/')

    return abort(HTTP_STATUS_CODE_BAD_REQUEST)
Exemplo n.º 18
0
def google_openid_connect():
    """Handler for the Google OpenID Connect callback.

    Reference:
    https://developers.google.com/identity/protocols/OpenIDConnect

    Returns:
        Redirect response.
    """
    error = request.args.get("error", None)

    if error:
        current_app.logger.error("OAuth2 flow error: {}".format(error))
        return abort(HTTP_STATUS_CODE_BAD_REQUEST,
                     "OAuth2 flow error: {0!s}".format(error))

    try:
        code = request.args["code"]
        client_csrf_token = request.args.get("state")
        server_csrf_token = session[CSRF_KEY]
    except KeyError as e:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST,
                     "Client CSRF error, no CSRF key stored")

    if client_csrf_token != server_csrf_token:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST, "Invalid CSRF token")

    try:
        encoded_jwt = get_encoded_jwt_over_https(code)
    except JwtFetchError as e:
        return abort(HTTP_STATUS_CODE_BAD_REQUEST,
                     "Jwt Fetch error, {0!s}".format(e))

    try:
        discovery_document = get_oauth2_discovery_document()
    except DiscoveryDocumentError as e:
        return abort(
            HTTP_STATUS_CODE_BAD_REQUEST,
            "Unable to discover document, with error: {0!s}".format(e),
        )

    algorithm = discovery_document["id_token_signing_alg_values_supported"][0]
    expected_audience = current_app.config.get("GOOGLE_OIDC_CLIENT_ID")
    expected_domain = current_app.config.get("GOOGLE_OIDC_HOSTED_DOMAIN")
    expected_issuer = discovery_document["issuer"]

    # Fetch the public key and try to validate the JWT.
    try:
        public_key = get_public_key_for_jwt(encoded_jwt,
                                            discovery_document["jwks_uri"])
        decoded_jwt = decode_jwt(encoded_jwt, public_key, algorithm,
                                 expected_audience)
        validate_jwt(decoded_jwt, expected_issuer, expected_domain)
    except (JwtValidationError, JwtKeyError) as e:
        current_app.logger.error("{}".format(e))
        return abort(
            HTTP_STATUS_CODE_UNAUTHORIZED,
            "Unable to validate request, with error: {0!s}".format(e),
        )

    validated_email = decoded_jwt.get("email")
    allowed_users = current_app.config.get("GOOGLE_OIDC_ALLOWED_USERS")

    # Check if the authenticating user is allowed.
    if allowed_users:
        if validated_email not in allowed_users:
            return abort(HTTP_STATUS_CODE_UNAUTHORIZED,
                         "Unauthorized request, user not allowed")

    user = User.get_or_create(username=validated_email, name=validated_email)
    login_user(user)

    # Log the user in and setup the session.
    if current_user.is_authenticated:
        return redirect(request.args.get("next") or "/")

    return abort(HTTP_STATUS_CODE_BAD_REQUEST, "User is not authenticated.")