def __init__(self,
              app,
              client_id,
              client_secret,
              expected_issuer,
              expected_audience,
              jwks_url,
              tenant,
              session_secret,
              scopes=None,
              e2e_expected_audience=None,
              e2e_client_id=None):
     super(AzureOAuth, self).__init__(app)
     azure_bp = make_azure_blueprint(
         client_id=client_id,
         client_secret=client_secret,
         scope=scopes,
         tenant=tenant,
     )
     app.server.register_blueprint(azure_bp, url_prefix="/login")
     app.server.secret_key = session_secret
     self._jwkaas = JWKaas(expected_audience,
                           expected_issuer,
                           jwks_url=jwks_url)
     self._e2e_jwkaas = JWKaas(e2e_expected_audience,
                               expected_issuer,
                               jwks_url=jwks_url)
     self.client_id = client_id
     self.e2e_client_id = e2e_client_id
     self.logout_url = None
     self.user = None
示例#2
0
 def setUp(self):
     self.private_key = open("jwkaas/test/test-key").read()
     self.my_jwkaas = JWKaas(
         "expected_audience",
         "expected_issuer",
         jwks_file="jwkaas/test/test-jwks.json",
     )
示例#3
0
class TestJWKaas(unittest.TestCase):
    """JWKaas integration test stubs"""
    def setUp(self):
        self.private_key = open('jwkaas/test/test-key').read()
        self.my_jwkaas = JWKaas("expected_audience",
                                "expected_issuer",
                                jwks_file='jwkaas/test/test-jwks.json')

    def generate_jwks(self):
        public_key = open('jwkaas/test/test-key.pub').read()
        rsa_pub_key = RSAAlgorithm(RSAAlgorithm.SHA256).prepare_key(public_key)
        jwks_key = RSAAlgorithm.to_jwk(rsa_pub_key)
        logging.info('to_jwk {}'.format(jwks_key))
        return jwks_key

    def test_decode_valid_token(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_token_info(token)
        logging.info('Token info: {}'.format(info))
        self.assertEqual(info, token_body)

    def test_decode_expired_token(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) - 50,
            'nbf': int(time()) - 100
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_token_info(token)
        logging.info('Token info: {}'.format(info))
        self.assertEqual(info, None, "Expired token should not be decoded")

    def test_decode_premature_token(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 200,
            'nbf': int(time()) + 100
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_token_info(token)
        logging.info('Token info: {}'.format(info))
        self.assertEqual(info, None, "Premature token should not be decoded")

    def test_decode_unexpected_issuer_token(self):
        token_body = {
            'iss': 'unexpected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_token_info(token)
        logging.info('Token info: {}'.format(info))
        self.assertEqual(info, None,
                         "Token with unexpected issuer should not be decoded")

    def test_decode_unexpected_audience_token(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'unexpected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_token_info(token)
        logging.info('Token info: {}'.format(info))
        self.assertEqual(
            info, None, "Token with unexpected audience should not be decoded")

    def test_decode_hmac_token(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='HS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_token_info(token)
        logging.info('Token info: {}'.format(info))
        self.assertEqual(
            info, None, "Token with unexpected audience should not be decoded")

    def test_decode_no_kid_token(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10
        }
        token = jwt.encode(token_body, self.private_key, algorithm='RS256')
        info = self.my_jwkaas.get_token_info(token)
        logging.info('Token info: {}'.format(info))
        self.assertEqual(info, None, "Token without kid should not be decoded")

    def test_decode_invalid_kid_token(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'invalid-key'})
        info = self.my_jwkaas.get_token_info(token)
        logging.info('Token info: {}'.format(info))
        self.assertEqual(info, None,
                         "Token with invalid kid should not be decoded")

    def test_decode_rubbish_header_token(self):
        info = self.my_jwkaas.get_token_info(
            "eydfkjdflkjdlflkjDJFLKJEFLKJDLKJFdlkfjlkj")
        logging.info('Token info: {}'.format(info))
        self.assertEqual(info, None,
                         "Rubbish header token should not be decoded")

    def test_decode_tampered_body_token(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10,
            'scp': 'scope'
        }
        tampered_token_body = token_body.copy()
        tampered_token_body['scp'] = 'scape'
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={
                               'kid': 'test-key'
                           }).decode('utf-8')
        new_body = base64.b64encode(
            json.dumps(tampered_token_body).encode('utf-8')).decode('utf-8')
        tampered_body_token = f"{token.split('.')[0]}.{new_body}.{token.split('.')[2]}"
        logging.info("rubtoken " + tampered_body_token)
        info = self.my_jwkaas.get_token_info(
            tampered_body_token.encode('utf-8'))
        logging.info('Token info: {}'.format(info))
        self.assertEqual(info, None, "Tampered token should not be decoded")

    def test_connexion_decode_valid_token_with_scp(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10,
            'scp': 'scope'
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_connexion_token_info(token)
        logging.info('Token info: {}'.format(info))
        expected_token_body = token_body.copy()
        expected_token_body['scopes'] = [token_body['scp']]
        self.assertEqual(info, expected_token_body)

    def test_connexion_decode_valid_token_with_scp_and_roles(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10,
            'scp': 'scope',
            'roles': ['role1', 'role2']
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_connexion_token_info(token)
        logging.info('Token info: {}'.format(info))
        expected_token_body = token_body.copy()
        expected_token_body['scopes'] = [
            token_body['scp'], *token_body['roles']
        ]
        self.assertEqual(info, expected_token_body)

    def test_connexion_decode_invalid_token_with_scp_and_roles(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) - 10,
            'nbf': int(time()) - 20,
            'scp': 'scope',
            'roles': ['role1', 'role2']
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_connexion_token_info(token)
        self.assertEqual(
            info, None,
            "Invalid token should not be decoded by connexion_decode")

    def test_connexion_decode_valid_token_only_roles(self):
        token_body = {
            'iss': 'expected_issuer',
            'aud': 'expected_audience',
            'exp': int(time()) + 100,
            'nbf': int(time()) - 10,
            'roles': ['role1', 'role2']
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm='RS256',
                           headers={'kid': 'test-key'})
        info = self.my_jwkaas.get_connexion_token_info(token)
        logging.info('Token info: {}'.format(info))
        expected_token_body = token_body.copy()
        expected_token_body['scopes'] = token_body['roles']
        self.assertEqual(info, expected_token_body)
import config
from flask import g, request
from jwkaas import JWKaas
from requests.exceptions import ConnectionError

from utils import get_secret

my_jwkaas = None
api_key_secret = None
api_key_secret_conversion = None

if hasattr(config, "OAUTH_JWKS_URL"):
    my_jwkaas = JWKaas(
        config.OAUTH_EXPECTED_AUDIENCE,
        config.OAUTH_EXPECTED_ISSUER,
        jwks_url=config.OAUTH_JWKS_URL,
    )

if hasattr(config, "API_KEY_SECRET"):
    api_key_secret = get_secret(os.environ["PROJECT_ID"],
                                config.API_KEY_SECRET)

if hasattr(config, "API_KEY_SECRET_CONVERSION"):
    api_key_secret_conversion = config.API_KEY_SECRET_CONVERSION


class LevelRaiser(logging.Filter):
    def filter(self, record):
        if record.levelno == logging.WARNING:
            record.levelno = logging.INFO
class AzureOAuth(Auth):
    def __init__(self,
                 app,
                 client_id,
                 client_secret,
                 expected_issuer,
                 expected_audience,
                 jwks_url,
                 tenant,
                 session_secret,
                 scopes=None,
                 e2e_expected_audience=None,
                 e2e_client_id=None):
        super(AzureOAuth, self).__init__(app)
        azure_bp = make_azure_blueprint(
            client_id=client_id,
            client_secret=client_secret,
            scope=scopes,
            tenant=tenant,
        )
        app.server.register_blueprint(azure_bp, url_prefix="/login")
        app.server.secret_key = session_secret
        self._jwkaas = JWKaas(expected_audience,
                              expected_issuer,
                              jwks_url=jwks_url)
        self._e2e_jwkaas = JWKaas(e2e_expected_audience,
                                  expected_issuer,
                                  jwks_url=jwks_url)
        self.client_id = client_id
        self.e2e_client_id = e2e_client_id
        self.logout_url = None
        self.user = None

    def is_authorized(self):
        if request.args.get('access_token') or session.get('access_token'):
            if request.args.get('access_token'):
                session['access_token'] = request.args.get('access_token')
            token_info = self._e2e_jwkaas.get_token_info(
                (session['access_token'] if session.get('access_token') else
                 request.args.get('access_token')))
            if not token_info or token_info['appid'] != self.e2e_client_id:
                logging.warning('Invalid access token')
                return abort(401)
            self.user = '******'
            token_info['roles'] = ['czdashboard.user']
        else:
            if not azure.authorized or azure.token['expires_in'] < 10:
                # send to azure login
                return False
            token_info = self._jwkaas.get_token_info(azure.access_token)
            self.user = token_info['unique_name']
            self.logout_url = "https://login.microsoftonline.com/{}/oauth2/v2.0/logout".format(
                token_info['tid'])

        if token_info:
            if 'roles' in token_info and 'czdashboard.user' in token_info[
                    'roles']:
                return True
            else:
                logging.warning('Missing required role czdashboard.user')
                return abort(401)
        else:
            logging.warning('Invalid access token')
            return abort(401)

    def login_request(self):
        # send to azure auth page
        return redirect(url_for("azure.login"))

    def logout_user(self):
        flask_login.logout_user()

    def get_logout_url(self):
        # returns logout url
        return self.logout_url

    def get_user(self):
        # returns username
        return self.user

    def auth_wrapper(self, f):
        def wrap(*args, **kwargs):
            if not self.is_authorized():
                return Response(status=401)

            response = f(*args, **kwargs)
            logging.info(response)
            return response

        return wrap

    def index_auth_wrapper(self, original_index):
        def wrap(*args, **kwargs):
            if self.is_authorized():
                return original_index(*args, **kwargs)
            else:
                return self.login_request()

        return wrap
示例#6
0
import config
import logging
from jwkaas import JWKaas

from flask import g

my_jwkaas = None
my_e2e_jwkaas = None
my_b2b_jwkaas = None

if hasattr(config, 'OAUTH_JWKS_URL'):
    my_jwkaas = JWKaas(config.OAUTH_EXPECTED_AUDIENCE,
                       config.OAUTH_EXPECTED_ISSUER,
                       jwks_url=config.OAUTH_JWKS_URL)

if hasattr(config, 'OAUTH_B2B_JWKS_URL'):
    my_b2b_jwkaas = JWKaas(config.OAUTH_B2B_EXPECTED_AUDIENCE,
                           config.OAUTH_B2B_EXPECTED_ISSUER,
                           jwks_url=config.OAUTH_B2B_JWKS_URL)

if hasattr(config, 'OAUTH_E2E_JWKS_URL'):
    my_e2e_jwkaas = JWKaas(config.OAUTH_E2E_EXPECTED_AUDIENCE,
                           config.OAUTH_E2E_EXPECTED_ISSUER,
                           jwks_url=config.OAUTH_E2E_JWKS_URL)


def refine_token_info(token_info):
    if token_info and 'scopes' in token_info:
        if 'snbplanningapi.editor' in token_info['scopes']:
            token_info['scopes'].append('snbplanningapi.planner')
    return token_info
示例#7
0
class TestJWKaas(unittest.TestCase):
    """JWKaas integration test stubs"""
    def setUp(self):
        self.private_key = open("jwkaas/test/test-key").read()
        self.my_jwkaas = JWKaas(
            "expected_audience",
            "expected_issuer",
            jwks_file="jwkaas/test/test-jwks.json",
        )

    def generate_jwks(self):
        public_key = open("jwkaas/test/test-key.pub").read()
        rsa_pub_key = RSAAlgorithm(RSAAlgorithm.SHA256).prepare_key(public_key)
        jwks_key = RSAAlgorithm.to_jwk(rsa_pub_key)
        logging.info("to_jwk {}".format(jwks_key))
        return jwks_key

    def test_decode_valid_token(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_token_info(token)
        logging.info("Token info: {}".format(info))
        self.assertEqual(info, token_body)

    def test_decode_expired_token(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) - 50,
            "nbf": int(time()) - 100,
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_token_info(token)
        logging.info("Token info: {}".format(info))
        self.assertEqual(info, None, "Expired token should not be decoded")

    def test_decode_premature_token(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 200,
            "nbf": int(time()) + 100,
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_token_info(token)
        logging.info("Token info: {}".format(info))
        self.assertEqual(info, None, "Premature token should not be decoded")

    def test_decode_unexpected_issuer_token(self):
        token_body = {
            "iss": "unexpected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_token_info(token)
        logging.info("Token info: {}".format(info))
        self.assertEqual(info, None,
                         "Token with unexpected issuer should not be decoded")

    def test_decode_unexpected_audience_token(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "unexpected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_token_info(token)
        logging.info("Token info: {}".format(info))
        self.assertEqual(
            info, None, "Token with unexpected audience should not be decoded")

    def test_decode_hmac_token(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="HS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_token_info(token)
        logging.info("Token info: {}".format(info))
        self.assertEqual(
            info, None, "Token with unexpected audience should not be decoded")

    def test_decode_no_kid_token(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
        }
        token = jwt.encode(token_body, self.private_key, algorithm="RS256")
        info = self.my_jwkaas.get_token_info(token)
        logging.info("Token info: {}".format(info))
        self.assertEqual(info, None, "Token without kid should not be decoded")

    def test_decode_invalid_kid_token(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
        }
        token = jwt.encode(
            token_body,
            self.private_key,
            algorithm="RS256",
            headers={"kid": "invalid-key"},
        )
        info = self.my_jwkaas.get_token_info(token)
        logging.info("Token info: {}".format(info))
        self.assertEqual(info, None,
                         "Token with invalid kid should not be decoded")

    def test_decode_rubbish_header_token(self):
        info = self.my_jwkaas.get_token_info(
            "eydfkjdflkjdlflkjDJFLKJEFLKJDLKJFdlkfjlkj")
        logging.info("Token info: {}".format(info))
        self.assertEqual(info, None,
                         "Rubbish header token should not be decoded")

    def test_decode_tampered_body_token(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
            "scp": "scope",
        }
        tampered_token_body = token_body.copy()
        tampered_token_body["scp"] = "scape"
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        new_body = base64.b64encode(
            json.dumps(tampered_token_body).encode("utf-8")).decode("utf-8")
        tampered_body_token = f"{token.split('.')[0]}.{new_body}.{token.split('.')[2]}"
        logging.info("rubtoken " + tampered_body_token)
        info = self.my_jwkaas.get_token_info(
            tampered_body_token.encode("utf-8"))
        logging.info("Token info: {}".format(info))
        self.assertEqual(info, None, "Tampered token should not be decoded")

    def test_connexion_decode_valid_token_with_scp(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
            "scp": "scope",
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_connexion_token_info(token)
        logging.info("Token info: {}".format(info))
        expected_token_body = token_body.copy()
        expected_token_body["scopes"] = [token_body["scp"]]
        self.assertEqual(info, expected_token_body)

    def test_connexion_decode_valid_token_with_scp_and_roles(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
            "scp": "scope",
            "roles": ["role1", "role2"],
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_connexion_token_info(token)
        logging.info("Token info: {}".format(info))
        expected_token_body = token_body.copy()
        expected_token_body["scopes"] = [
            token_body["scp"], *token_body["roles"]
        ]
        self.assertEqual(info, expected_token_body)

    def test_connexion_decode_invalid_token_with_scp_and_roles(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) - 10,
            "nbf": int(time()) - 20,
            "scp": "scope",
            "roles": ["role1", "role2"],
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_connexion_token_info(token)
        self.assertEqual(
            info, None,
            "Invalid token should not be decoded by connexion_decode")

    def test_connexion_decode_valid_token_only_roles(self):
        token_body = {
            "iss": "expected_issuer",
            "aud": "expected_audience",
            "exp": int(time()) + 100,
            "nbf": int(time()) - 10,
            "roles": ["role1", "role2"],
        }
        token = jwt.encode(token_body,
                           self.private_key,
                           algorithm="RS256",
                           headers={"kid": "test-key"})
        info = self.my_jwkaas.get_connexion_token_info(token)
        logging.info("Token info: {}".format(info))
        expected_token_body = token_body.copy()
        expected_token_body["scopes"] = token_body["roles"]
        self.assertEqual(info, expected_token_body)