class TestRPHandlerWithMockOP(object): @pytest.fixture(autouse=True) def rphandler_setup(self): self.rph = RPHandler( base_url=BASE_URL, client_configs=CLIENT_CONFIG, http_lib=MockOP(issuer='https://github.com/login/oauth/authorize')) def test_finalize(self): auth_query = self.rph.begin(issuer_id='github') # The authorization query is sent and after successful authentication client = self.rph.get_client_from_session_key( state=auth_query['state']) _ = client.http(auth_query['url']) # the user is redirected back to the RP with a positive response auth_response = AuthorizationResponse(code='access_code', state=auth_query['state']) # need session information and the client instance _session = self.rph.get_session_information(auth_response['state']) client = self.rph.get_client_from_session_key( state=auth_response['state']) # Faking self.rph.httplib.keyjar = client.service_context.keyjar # do the rest (= get access token and user info) # assume code flow resp = self.rph.finalize(_session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'userinfo', 'state', 'token'}
def rphandler_setup(self): self.issuer = 'https://github.com/login/oauth/authorize' self.mock_op = MockOP(issuer=self.issuer) self.rph = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, http_lib=self.mock_op, keyjar=KeyJar())
def rphandler_setup(self, httpserver): self.rph = RPHandler(base_url=BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY) res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer=_github_id), _github_id) idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=GITHUB_KEY.get_signing_key( 'rsa', owner=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600, 'refresh_token': 'refreshing' } at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json(), headers={'Content-Type': 'application/json'}) client.service['accesstoken'].endpoint = httpserver.url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) token_resp = self.rph.get_access_and_id_token(auth_response, client=client) httpserver.serve_content('{"sub":"EndUserSubject"}', headers={'Content-Type': 'application/json'}) client.service['userinfo'].endpoint = httpserver.url self.rph.get_user_info(res['state'], client, token_resp['access_token']) self.state = res['state']
def init_oidc_rp_handler(app): _jwks_def = app.config.get('KEYS') _jwks_def['public_path'] = _jwks_def['public_path'].format(dir_path) _jwks_def['private_path'] = _jwks_def['private_path'].format(dir_path) _kj = init_key_jar(**_jwks_def) rph = RPHandler(base_url=app.config.get('BASE_URL'), hash_seed="BabyHoldOn", keyjar=_kj, jwks_path=app.config.get('PUBLIC_JWKS_PATH'), client_configs=app.config.get('CLIENTS'), services=app.config.get('SERVICES'), verify_ssl=app.config.get('VERIFY_SSL')) return rph
def init_oidc_rp_handler(app): _rp_conf = app.rp_config if _rp_conf.rp_keys: _kj = init_key_jar(**_rp_conf.rp_keys) _path = _rp_conf.rp_keys['public_path'] # removes ./ and / from the begin of the string _path = re.sub('^(.)/', '', _path) else: _kj = KeyJar() _path = '' _kj.httpc_params = _rp_conf.httpc_params rph = RPHandler(_rp_conf.base_url, _rp_conf.clients, services=_rp_conf.services, hash_seed=_rp_conf.hash_seed, keyjar=_kj, jwks_path=_path, httpc_params=_rp_conf.httpc_params) return rph
def init_oidc_rp_handler(app): oidc_keys_conf = app.config.get('OIDC_KEYS') verify_ssl = app.config.get('VERIFY_SSL') _kj = init_key_jar(**oidc_keys_conf) _kj.verify_ssl = verify_ssl _path = oidc_keys_conf['public_path'] if _path.startswith('./'): _path = _path[2:] elif _path.startswith('/'): _path = _path[1:] rph = RPHandler(base_url=app.config.get('BASEURL'), hash_seed="BabyHoldOn", keyjar=_kj, jwks_path=_path, client_configs=app.config.get('CLIENTS'), services=app.config.get('SERVICES'), verify_ssl=verify_ssl) return rph
class TestRPHandlerWithMockOP(object): @pytest.fixture(autouse=True) def rphandler_setup(self): self.issuer = 'https://github.com/login/oauth/authorize' self.mock_op = MockOP(issuer=self.issuer) self.rph = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, http_lib=self.mock_op, keyjar=KeyJar()) def test_finalize(self): auth_query = self.rph.begin(issuer_id='github') # The authorization query is sent and after successful authentication client = self.rph.get_client_from_session_key( state=auth_query['state']) # register a response p = urlparse( CLIENT_CONFIG['github']['provider_info']['authorization_endpoint']) self.mock_op.register_get_response(p.path, 'Redirect', 302) _ = client.http(auth_query['url']) # the user is redirected back to the RP with a positive response auth_response = AuthorizationResponse(code='access_code', state=auth_query['state']) # need session information and the client instance _session = self.rph.get_session_information(auth_response['state']) client = self.rph.get_client_from_session_key( state=auth_response['state']) # Faking resp = construct_access_token_response( _session['auth_request']['nonce'], issuer=self.issuer, client_id=CLIENT_CONFIG['github']['client_id'], key_jar=GITHUB_KEY) p = urlparse( CLIENT_CONFIG['github']['provider_info']['token_endpoint']) self.mock_op.register_post_response( p.path, resp.to_json(), 200, {'content-type': "application/json"}) _info = OpenIDSchema(sub='EndUserSubject', given_name='Diana', family_name='Krall', occupation='Jazz pianist') p = urlparse( CLIENT_CONFIG['github']['provider_info']['userinfo_endpoint']) self.mock_op.register_get_response( p.path, _info.to_json(), 200, {'content-type': "application/json"}) _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) # do the rest (= get access token and user info) # assume code flow resp = self.rph.finalize(_session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'userinfo', 'state', 'token', 'id_token'} def test_dynamic_setup(self): user_id = 'acct:[email protected]' _link = Link(rel="http://openid.net/specs/connect/1.0/issuer", href="https://server.example.com") webfinger_response = JRD(subject=user_id, links=[_link]) self.mock_op.register_get_response( '/.well-known/webfinger', webfinger_response.to_json(), 200, {'content-type': "application/json"}) resp = { "authorization_endpoint": "https://server.example.com/connect/authorize", "issuer": "https://server.example.com", "subject_types_supported": ['public'], "token_endpoint": "https://server.example.com/connect/token", "token_endpoint_auth_methods_supported": ["client_secret_basic", "private_key_jwt"], "userinfo_endpoint": "https://server.example.com/connect/user", "check_id_endpoint": "https://server.example.com/connect/check_id", "refresh_session_endpoint": "https://server.example.com/connect/refresh_session", "end_session_endpoint": "https://server.example.com/connect/end_session", "jwks_uri": "https://server.example.com/jwk.json", "registration_endpoint": "https://server.example.com/connect/register", "scopes_supported": ["openid", "profile", "email", "address", "phone"], "response_types_supported": ["code", "code id_token", "token id_token"], "acrs_supported": ["1", "2", "http://id.incommon.org/assurance/bronze"], "user_id_types_supported": ["public", "pairwise"], "userinfo_algs_supported": ["HS256", "RS256", "A128CBC", "A128KW", "RSA1_5"], "id_token_signing_alg_values_supported": ["HS256", "RS256", "A128CBC", "A128KW", "RSA1_5"], "request_object_algs_supported": ["HS256", "RS256", "A128CBC", "A128KW", "RSA1_5"] } pcr = ProviderConfigurationResponse(**resp) self.mock_op.register_get_response( '/.well-known/openid-configuration', pcr.to_json(), 200, {'content-type': "application/json"}) self.mock_op.register_post_response( '/connect/register', registration_callback, 200, {'content-type': "application/json"}) auth_query = self.rph.begin(user_id=user_id) assert auth_query
class TestRPHandlerTier2(object): @pytest.fixture(autouse=True) def rphandler_setup(self): self.rph = RPHandler(BASE_URL, CLIENT_CONFIG, keyjar=CLI_KEY) res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=GITHUB_KEY.get_signing_key( 'rsa', issuer_id=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600, 'refresh_token': 'refreshing' } at = AccessTokenResponse(**_info) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("POST", _url, body=at.to_json(), adding_headers={"Content-Type": "application/json"}, status=200) client.service['accesstoken'].endpoint = _url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) token_resp = self.rph.get_access_and_id_token(auth_response, client=client) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("GET", _url, body='{"sub":"EndUserSubject"}', adding_headers={"Content-Type": "application/json"}, status=200) client.service['userinfo'].endpoint = _url self.rph.get_user_info(res['state'], client, token_resp['access_token']) self.state = res['state'] def test_init_authorization(self): _session = self.rph.get_session_information(self.state) client = self.rph.issuer2rp[_session['iss']] res = self.rph.init_authorization( client, req_args={'scope': ['openid', 'email']}) part = urlsplit(res['url']) _qp = parse_qs(part.query) assert _qp['scope'] == ['openid email'] def test_refresh_access_token(self): _session = self.rph.get_session_information(self.state) client = self.rph.issuer2rp[_session['iss']] _info = { "access_token": "2nd_accessTok", "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("POST", _url, body=at.to_json(), adding_headers={"Content-Type": "application/json"}, status=200) client.service['refresh_token'].endpoint = _url res = self.rph.refresh_access_token(self.state, client, 'openid email') assert res['access_token'] == '2nd_accessTok' def test_get_user_info(self): _session = self.rph.get_session_information(self.state) client = self.rph.issuer2rp[_session['iss']] _url = "https://github.com/userinfo" with responses.RequestsMock() as rsps: rsps.add("GET", _url, body='{"sub":"EndUserSubject", "mail":"*****@*****.**"}', adding_headers={"Content-Type": "application/json"}, status=200) client.service['userinfo'].endpoint = _url resp = self.rph.get_user_info(self.state, client) assert set(resp.keys()) == {'sub', 'mail'} assert resp['mail'] == '*****@*****.**' def test_has_active_authentication(self): assert self.rph.has_active_authentication(self.state) def test_get_valid_access_token(self): (token, expires_at) = self.rph.get_valid_access_token(self.state) assert token == 'accessTok' assert expires_at > 0
def rphandler_setup(self): self.rph = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc'])
class TestRPHandler(object): @pytest.fixture(autouse=True) def rphandler_setup(self): self.rph = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) def test_pick_config(self): cnf = self.rph.pick_config('facebook') assert cnf['issuer'] == "https://www.facebook.com/v2.11/dialog/oauth" cnf = self.rph.pick_config('linkedin') assert cnf['issuer'] == "https://www.linkedin.com/oauth/v2/" cnf = self.rph.pick_config('github') assert cnf['issuer'] == "https://github.com/login/oauth/authorize" cnf = self.rph.pick_config('') assert 'issuer' not in cnf def test_init_client(self): client = self.rph.init_client('github') assert set(client.service.keys()) == { 'authorization', 'accesstoken', 'userinfo', 'refresh_token' } _context = client.service_context assert _context.get('client_id') == 'eeeeeeeee' assert _context.get('client_secret') == 'aaaaaaaaaaaaaaaaaaaa' assert _context.get( 'issuer') == "https://github.com/login/oauth/authorize" assert _context.get('provider_info') is not None assert set(_context.get('provider_info').keys()) == { 'authorization_endpoint', 'token_endpoint', 'userinfo_endpoint' } assert _context.get('behaviour') == { "response_types": ["code"], "scope": ["user", "public_repo"], "token_endpoint_auth_method": '', 'verify_args': { 'allow_sign_alg_none': True } } _github_id = iss_id('github') _context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) # The key jar should only contain a symmetric key that is the clients # secret. 2 because one is marked for encryption and the other signing # usage. assert list(_context.keyjar.owners()) == ['', _github_id] keys = _context.keyjar.get_issuer_keys('') assert len(keys) == 2 assert _context.base_url == BASE_URL def test_do_provider_info(self): client = self.rph.init_client('github') issuer = self.rph.do_provider_info(client) assert issuer == iss_id('github') # Make sure the service endpoints are set for service_type in ['authorization', 'accesstoken', 'userinfo']: _srv = client.service[service_type] _endp = client.service_context.get('provider_info')[ _srv.endpoint_name] assert _srv.endpoint == _endp def test_do_client_registration(self): client = self.rph.init_client('github') issuer = self.rph.do_provider_info(client) self.rph.do_client_registration(client, 'github') # only 2 things should have happened assert self.rph.hash2issuer['github'] == issuer assert client.service_context.get('post_logout_redirect_uris') is None def test_do_client_setup(self): client = self.rph.client_setup('github') _github_id = iss_id('github') _context = client.service_context assert _context.get('client_id') == 'eeeeeeeee' assert _context.get('client_secret') == 'aaaaaaaaaaaaaaaaaaaa' assert _context.get('issuer') == _github_id _context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) assert list(_context.keyjar.owners()) == ['', _github_id] keys = _context.keyjar.get_issuer_keys('') assert len(keys) == 2 for service_type in ['authorization', 'accesstoken', 'userinfo']: _srv = client.service[service_type] _endp = client.service_context.get('provider_info')[ _srv.endpoint_name] assert _srv.endpoint == _endp assert self.rph.hash2issuer['github'] == _context.get('issuer') def test_create_callbacks(self): cb = self.rph.create_callbacks('https://op.example.com/') assert set(cb.keys()) == {'code', 'implicit', 'form_post', '__hex'} _hash = cb['__hex'] assert cb['code'] == 'https://example.com/rp/authz_cb/{}'.format(_hash) assert cb[ 'implicit'] == 'https://example.com/rp/authz_im_cb/{}'.format( _hash) assert cb[ 'form_post'] == 'https://example.com/rp/authz_fp_cb/{}'.format( _hash) assert list(self.rph.hash2issuer.keys()) == [_hash] assert self.rph.hash2issuer[_hash] == 'https://op.example.com/' def test_begin(self): res = self.rph.begin(issuer_id='github') assert set(res.keys()) == {'url', 'state'} _github_id = iss_id('github') client = self.rph.issuer2rp[_github_id] assert client.service_context.get('issuer') == _github_id part = urlsplit(res['url']) assert part.scheme == 'https' assert part.netloc == 'github.com' assert part.path == '/login/oauth/authorize' query = parse_qs(part.query) assert set(query.keys()) == { 'nonce', 'state', 'client_id', 'redirect_uri', 'response_type', 'scope' } # nonce and state are created on the fly so can't check for those assert query['client_id'] == ['eeeeeeeee'] assert query['redirect_uri'] == [ 'https://example.com/rp/authz_cb/github' ] assert query['response_type'] == ['code'] assert query['scope'] == ['user public_repo openid'] def test_get_session_information(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) assert self.rph.client_configs['github']['issuer'] == _session['iss'] def test_get_client_from_session_key(self): res = self.rph.begin(issuer_id='linkedin') cli1 = self.rph.get_client_from_session_key(state=res['state']) _session = self.rph.get_session_information(res['state']) cli2 = self.rph.issuer2rp[_session['iss']] assert cli1 == cli2 # redo self.rph.do_provider_info(state=res['state']) # get new redirect_uris cli2.service_context.redirect_uris = [] self.rph.do_client_registration(state=res['state']) def test_finalize_auth(self): res = self.rph.begin(issuer_id='linkedin') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = self.rph.finalize_auth(client, _session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'state', 'code'} aresp = client.service['authorization'].get_item( AuthorizationResponse, 'auth_response', res['state']) assert set(aresp.keys()) == {'state', 'code'} def test_get_client_authn_method(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] authn_method = self.rph.get_client_authn_method( client, 'token_endpoint') assert authn_method == '' res = self.rph.begin(issuer_id='linkedin') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] authn_method = self.rph.get_client_authn_method( client, 'token_endpoint') assert authn_method == 'client_secret_post' def test_get_access_token(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=GITHUB_KEY.get_signing_key(issuer_id=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("POST", _url, body=at.to_json(), adding_headers={"Content-Type": "application/json"}, status=200) client.service['accesstoken'].endpoint = _url auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = self.rph.finalize_auth(client, _session['iss'], auth_response.to_dict()) resp = self.rph.get_access_token(res['state'], client) assert set(resp.keys()) == { 'access_token', 'expires_in', 'id_token', 'token_type', '__verified_id_token', '__expires_at' } atresp = client.service['accesstoken'].get_item( AccessTokenResponse, 'token_response', res['state']) assert set(atresp.keys()) == { 'access_token', 'expires_in', 'id_token', 'token_type', '__verified_id_token', '__expires_at' } def test_access_and_id_token(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=GITHUB_KEY.get_signing_key( 'rsa', issuer_id=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("POST", _url, body=at.to_json(), adding_headers={"Content-Type": "application/json"}, status=200) client.service['accesstoken'].endpoint = _url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) resp = self.rph.get_access_and_id_token(auth_response, client=client) assert resp['access_token'] == 'accessTok' assert isinstance(resp['id_token'], IdToken) def test_access_and_id_token_by_reference(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=GITHUB_KEY.get_signing_key( 'rsa', issuer_id=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("POST", _url, body=at.to_json(), adding_headers={"Content-Type": "application/json"}, status=200) client.service['accesstoken'].endpoint = _url _response = AuthorizationResponse(code='access_code', state=res['state']) _ = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) resp = self.rph.get_access_and_id_token(state=res['state']) assert resp['access_token'] == 'accessTok' assert isinstance(resp['id_token'], IdToken) def test_get_user_info(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=GITHUB_KEY.get_signing_key( 'rsa', issuer_id=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("POST", _url, body=at.to_json(), adding_headers={"Content-Type": "application/json"}, status=200) client.service['accesstoken'].endpoint = _url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) token_resp = self.rph.get_access_and_id_token(auth_response, client=client) _url = "https://github.com/user_info" with responses.RequestsMock() as rsps: rsps.add("GET", _url, body='{"sub":"EndUserSubject"}', adding_headers={"Content-Type": "application/json"}, status=200) client.service['userinfo'].endpoint = _url userinfo_resp = self.rph.get_user_info(res['state'], client, token_resp['access_token']) assert userinfo_resp def test_userinfo_in_id_token(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud, 'given_name': 'Diana', 'family_name': 'Krall', 'occupation': 'Jazz pianist' } idts = IdToken(**idval) userinfo = self.rph.userinfo_in_id_token(idts) assert set(userinfo.keys()) == { 'sub', 'family_name', 'given_name', 'occupation' }
if args.insecure: verify_ssl = False else: verify_ssl = True federation_entity = make_federation_entity( config.CLIENTS['']['federation'], httpcli=requests.request, verify_ssl=verify_ssl) rph = RPHandler(base_url=_base_url, hash_seed="BabyDriver", keyjar=_kj, jwks_path=config.RP_CONFIG['jwks_url_path'], client_configs=config.CLIENTS, service_factory=factory, client_cls=oidc.RP, verify_ssl=verify_ssl, federation_entity=federation_entity) cherrypy.tree.mount(cprp.Consumer(rph, 'html'), '/', provider_config) # If HTTPS if args.tls: cherrypy.server.ssl_certificate = config.SERVER_CERT cherrypy.server.ssl_private_key = config.SERVER_KEY if config.CA_BUNDLE: cherrypy.server.ssl_certificate_chain = config.CA_BUNDLE cherrypy.engine.start()
class TestRPHandler(object): @pytest.fixture(autouse=True) def rphandler_setup(self): self.rph = RPHandler(base_url=BASEURL, client_configs=CLIENT_CONFIG) def test_support_webfinger(self): assert self.rph.supports_webfinger() def test_pick_config(self): cnf = self.rph.pick_config('facebook') assert cnf['issuer'] == "https://www.facebook.com/v2.11/dialog/oauth" cnf = self.rph.pick_config('linkedin') assert cnf['issuer'] == "https://www.linkedin.com/oauth/v2/" cnf = self.rph.pick_config('github') assert cnf['issuer'] == "https://github.com/login/oauth/authorize" cnf = self.rph.pick_config('') assert 'issuer' not in cnf def test_init_client(self): client = self.rph.init_client('github') assert set(client.service.keys()) == {'authorization', 'accesstoken', 'userinfo', 'refresh_token'} _context = client.service_context assert _context.client_id == 'eeeeeeeee' assert _context.client_secret == 'aaaaaaaaaaaaa' assert _context.issuer == "https://github.com/login/oauth/authorize" assert _context.provider_info assert set(_context.provider_info.keys()) == { 'authorization_endpoint', 'token_endpoint', 'userinfo_endpoint' } assert _context.behaviour == { "response_types": ["code"], "scope": ["user", "public_repo"], "token_endpoint_auth_method": '' } # The key jar should only contain a symmetric key that is the clients # secret. 2 because one is marked for encryption and the other signing # usage. assert list(_context.keyjar.owners()) == [''] keys = _context.keyjar.get_issuer_keys('') assert len(keys) == 2 for key in keys: assert key.kty == 'oct' assert key.key == b'aaaaaaaaaaaaa' assert _context.base_url == BASEURL def test_do_provider_info(self): client = self.rph.init_client('github') issuer = self.rph.do_provider_info(client) assert issuer == "https://github.com/login/oauth/authorize" # Make sure the service endpoints are set for service_type in ['authorization', 'accesstoken', 'userinfo']: _srv = client.service[service_type] _endp = client.service_context.provider_info[_srv.endpoint_name] assert _srv.endpoint == _endp def test_do_client_registration(self): client = self.rph.init_client('github') issuer = self.rph.do_provider_info(client) self.rph.do_client_registration(client, 'github') # only 2 things should have happened assert self.rph.hash2issuer['github'] == issuer assert client.service_context.post_logout_redirect_uris == [BASEURL] def test_do_client_setup(self): client = self.rph.client_setup('github') _context = client.service_context assert _context.client_id == 'eeeeeeeee' assert _context.client_secret == 'aaaaaaaaaaaaa' assert _context.issuer == "https://github.com/login/oauth/authorize" assert list(_context.keyjar.owners()) == [''] keys = _context.keyjar.get_issuer_keys('') assert len(keys) == 2 for key in keys: assert key.kty == 'oct' assert key.key == b'aaaaaaaaaaaaa' for service_type in ['authorization', 'accesstoken', 'userinfo']: _srv = client.service[service_type] _endp = client.service_context.provider_info[_srv.endpoint_name] assert _srv.endpoint == _endp assert self.rph.hash2issuer['github'] == _context.issuer def test_create_callbacks(self): cb = self.rph.create_callbacks('https://op.example.com/') assert set(cb.keys()) == {'code', 'implicit', 'form_post', '__hex'} assert cb == { 'code': 'https://example.com/rp/authz_cb' '/7f729285244adafbf5412e06b097e0e1f92049bfc432fed0a13cbcb5661b137d', 'implicit': 'https://example.com/rp/authz_im_cb' '/7f729285244adafbf5412e06b097e0e1f92049bfc432fed0a13cbcb5661b137d', 'form_post': 'https://example.com/rp/authz_fp_cb' '/7f729285244adafbf5412e06b097e0e1f92049bfc432fed0a13cbcb5661b137d', '__hex': '7f729285244adafbf5412e06b097e0e1f92049bfc432fed0a13cbcb5661b137d' } assert list(self.rph.hash2issuer.keys()) == [ '7f729285244adafbf5412e06b097e0e1f92049bfc432fed0a13cbcb5661b137d'] assert self.rph.hash2issuer[ '7f729285244adafbf5412e06b097e0e1f92049bfc432fed0a13cbcb5661b137d' ] == 'https://op.example.com/' def test_begin(self): res = self.rph.begin(issuer_id='github') assert set(res.keys()) == {'url', 'state'} _session = self.rph.session_interface.get_state(res['state']) client = self.rph.issuer2rp[_session['iss']] assert client.service_context.issuer == \ "https://github.com/login/oauth/authorize" part = urlsplit(res['url']) assert part.scheme == 'https' assert part.netloc == 'github.com' assert part.path == '/login/oauth/authorize' query = parse_qs(part.query) assert set(query.keys()) == {'nonce', 'state', 'client_id', 'redirect_uri', 'response_type', 'scope'} # nonce and state are created on the fly so can't check for those assert query['client_id'] == ['eeeeeeeee'] assert query['redirect_uri'] == [ 'https://example.com/rp/authz_cb/github'] assert query['response_type'] == ['code'] assert query['scope'] == ['user public_repo openid'] def test_get_session_information(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) assert self.rph.client_configs['github']['issuer'] == _session['iss'] def test_finalize_auth(self): res = self.rph.begin(issuer_id='linkedin') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = self.rph.finalize_auth(client, _session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'state', 'code'} aresp = client.service['authorization'].get_item(AuthorizationResponse, 'auth_response', res['state']) assert set(aresp.keys()) == {'state', 'code'} def test_get_client_authn_method(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] authn_method = self.rph.get_client_authn_method(client, 'token_endpoint') assert authn_method == '' res = self.rph.begin(issuer_id='linkedin') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] authn_method = self.rph.get_client_authn_method(client, 'token_endpoint') assert authn_method == 'client_secret_post' def test_get_access_token(self, httpserver): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = {'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud} idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=client.service_context.keyjar.get_signing_key('oct'), algorithm="HS256", lifetime=300) _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json()) client.service['accesstoken'].endpoint = httpserver.url auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = self.rph.finalize_auth(client, _session['iss'], auth_response.to_dict()) resp = self.rph.get_access_token(res['state'], client) assert set(resp.keys()) == {'access_token', 'expires_in', 'id_token', 'token_type', '__verified_id_token', '__expires_at'} atresp = client.service['accesstoken'].get_item(AccessTokenResponse, 'token_response', res['state']) assert set(atresp.keys()) == {'access_token', 'expires_in', 'id_token', 'token_type', '__verified_id_token', '__expires_at'} def test_access_and_id_token(self, httpserver): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = {'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud} idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=client.service_context.keyjar.get_signing_key('oct'), algorithm="HS256", lifetime=300) _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json()) client.service['accesstoken'].endpoint = httpserver.url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) resp = self.rph.get_access_and_id_token(auth_response, client=client) assert resp['access_token'] == 'accessTok' assert isinstance(resp['id_token'], IdToken) def test_access_and_id_token_by_reference(self, httpserver): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = {'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud} idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=client.service_context.keyjar.get_signing_key('oct'), algorithm="HS256", lifetime=300) _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json()) client.service['accesstoken'].endpoint = httpserver.url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) resp = self.rph.get_access_and_id_token(state=res['state']) assert resp['access_token'] == 'accessTok' assert isinstance(resp['id_token'], IdToken) def test_get_user_info(self, httpserver): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = {'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud} idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=client.service_context.keyjar.get_signing_key('oct'), algorithm="HS256", lifetime=300) _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json()) client.service['accesstoken'].endpoint = httpserver.url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) token_resp = self.rph.get_access_and_id_token(auth_response, client=client) httpserver.serve_content('{"sub":"EndUserSubject"}') client.service['userinfo'].endpoint = httpserver.url userinfo_resp = self.rph.get_user_info(res['state'], client, token_resp['access_token']) assert userinfo_resp def test_userinfo_in_id_token(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = {'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud, 'given_name': 'Diana', 'family_name': 'Krall', 'occupation': 'Jazz pianist'} idts = IdToken(**idval) userinfo = self.rph.userinfo_in_id_token(idts) assert set(userinfo.keys()) == {'sub', 'family_name', 'given_name', 'occupation'}
def rphandler_setup(self): self.rph = RPHandler(BASE_URL)
class TestRPHandler(object): @pytest.fixture(autouse=True) def rphandler_setup(self): self.rph = RPHandler(BASE_URL) def test_pick_config(self): cnf = self.rph.pick_config('') assert cnf def test_init_client(self): client = self.rph.init_client('') assert set(client.service.keys()) == { 'registration', 'provider_info', 'webfinger', 'authorization', 'accesstoken', 'userinfo', 'refresh_token' } _context = client.service_context assert _context.config['client_preferences'] == { 'application_type': 'web', 'application_name': 'rphandler', 'response_types': [ 'code', 'id_token', 'id_token token', 'code id_token', 'code id_token token', 'code token' ], 'scope': ['openid'], 'token_endpoint_auth_method': 'client_secret_basic' } assert list(_context.keyjar.owners()) == ['', BASE_URL] keys = _context.keyjar.get_issuer_keys('') assert len(keys) == 2 assert _context.base_url == BASE_URL def test_begin(self): ISS_ID = "https://op.example.org" OP_KEYS = build_keyjar(DEFAULT_KEY_DEFS) # The 4 steps of client_setup client = self.rph.init_client(ISS_ID) with responses.RequestsMock() as rsps: request_uri = '{}/.well-known/openid-configuration'.format(ISS_ID) _jws = ProviderConfigurationResponse( issuer=ISS_ID, authorization_endpoint='{}/authorization'.format(ISS_ID), jwks_uri='{}/jwks.json'.format(ISS_ID), response_types_supported=[ 'code', 'id_token', 'id_token token' ], subject_types_supported=['public'], id_token_signing_alg_values_supported=["RS256", "ES256"], token_endpoint='{}/token'.format(ISS_ID), registration_endpoint='{}/register'.format(ISS_ID)).to_json() rsps.add("GET", request_uri, body=_jws, status=200) rsps.add("GET", '{}/jwks.json'.format(ISS_ID), body=OP_KEYS.export_jwks_as_json(), status=200) issuer = self.rph.do_provider_info(client) # Calculating request so I can build a reasonable response self.rph.add_callbacks(client.service_context) _req = client.service['registration'].construct_request() with responses.RequestsMock() as rsps: request_uri = client.service_context.get( 'provider_info')["registration_endpoint"] _jws = RegistrationResponse( client_id="client uno", client_secret="VerySecretAndLongEnough", **_req.to_dict()).to_json() rsps.add("POST", request_uri, body=_jws, status=200) self.rph.do_client_registration(client, ISS_ID) self.rph.issuer2rp[issuer] = client assert set(client.service_context.get('behaviour').keys()) == { 'token_endpoint_auth_method', 'response_types', 'scope', 'application_type', 'application_name' } assert client.service_context.get('client_id') == "client uno" assert client.service_context.get( 'client_secret') == "VerySecretAndLongEnough" assert client.service_context.get('issuer') == ISS_ID res = self.rph.init_authorization(client) assert set(res.keys()) == {'url', 'state'} p = urlparse(res["url"]) assert p.hostname == 'op.example.org' assert p.path == "/authorization" qs = parse_qs(p.query) assert qs['state'] == [res['state']] # PKCE stuff assert 'code_challenge' in qs assert qs["code_challenge_method"] == ["S256"] def test_begin_2(self): ISS_ID = "https://op.example.org" OP_KEYS = build_keyjar(DEFAULT_KEY_DEFS) # The 4 steps of client_setup client = self.rph.init_client(ISS_ID) with responses.RequestsMock() as rsps: request_uri = '{}/.well-known/openid-configuration'.format(ISS_ID) _jws = ProviderConfigurationResponse( issuer=ISS_ID, authorization_endpoint='{}/authorization'.format(ISS_ID), jwks_uri='{}/jwks.json'.format(ISS_ID), response_types_supported=[ 'code', 'id_token', 'id_token token' ], subject_types_supported=['public'], id_token_signing_alg_values_supported=["RS256", "ES256"], token_endpoint='{}/token'.format(ISS_ID), registration_endpoint='{}/register'.format(ISS_ID)).to_json() rsps.add("GET", request_uri, body=_jws, status=200) rsps.add("GET", '{}/jwks.json'.format(ISS_ID), body=OP_KEYS.export_jwks_as_json(), status=200) issuer = self.rph.do_provider_info(client) # Calculating request so I can build a reasonable response self.rph.add_callbacks(client.service_context) # Publishing a JWKS instead of a JWKS_URI client.service_context.jwks_uri = '' client.service_context.jwks = client.service_context.keyjar.export_jwks( ) _req = client.service['registration'].construct_request() with responses.RequestsMock() as rsps: request_uri = client.service_context.get( 'provider_info')["registration_endpoint"] _jws = RegistrationResponse( client_id="client uno", client_secret="VerySecretAndLongEnough", **_req.to_dict()).to_json() rsps.add("POST", request_uri, body=_jws, status=200) self.rph.do_client_registration(client, ISS_ID) assert 'jwks' in client.service_context.get('registration_response')
def rphandler_setup(self): self.rph = RPHandler( base_url=BASE_URL, client_configs=CLIENT_CONFIG, http_lib=MockOP(issuer='https://github.com/login/oauth/authorize'))
_base_url = config.BASEURL _kj = get_jwks(config.PRIVATE_JWKS_PATH, config.KEYDEFS, config.PUBLIC_JWKS_PATH) if args.insecure: _kj.verify_ssl = False _verify_ssl = False else: _verify_ssl = True rph = RPHandler(base_url=_base_url, hash_seed="BabyHoldOn", keyjar=_kj, jwks_path=config.PUBLIC_JWKS_PATH, client_configs=config.CLIENTS, services=config.SERVICES, verify_ssl=_verify_ssl) cherrypy.tree.mount(cprp.Consumer(rph, 'html'), '/', provider_config) # If HTTPS if args.tls: cherrypy.server.ssl_certificate = config.SERVER_CERT cherrypy.server.ssl_private_key = config.SERVER_KEY if config.CA_BUNDLE: cherrypy.server.ssl_certificate_chain = config.CA_BUNDLE cherrypy.engine.start() cherrypy.engine.block()
def rphandler_setup(self): self.rph = RPHandler(base_url=BASEURL, client_configs=CLIENT_CONFIG)
_base_url = "{}:{}".format(config.BASEURL, args.port) else: _base_url = config.BASEURL _kj = init_key_jar(private_path=config.PRIVATE_JWKS_PATH, key_defs=config.KEYDEFS, public_path=config.PUBLIC_JWKS_PATH) if args.insecure: verify_ssl = False else: verify_ssl = True rph = RPHandler(base_url=_base_url, hash_seed="BabyDriver", keyjar=_kj, jwks_path=config.PUBLIC_JWKS_PATH, client_configs=config.CLIENTS, service_factory=factory, services=config.SERVICES, client_cls=oidc.RP, verify_ssl=verify_ssl) cherrypy.tree.mount(cprp.Consumer(rph, 'html'), '/', provider_config) # If HTTPS if args.tls: cherrypy.server.ssl_certificate = config.SERVER_CERT cherrypy.server.ssl_private_key = config.SERVER_KEY if config.CA_BUNDLE: cherrypy.server.ssl_certificate_chain = config.CA_BUNDLE cherrypy.engine.start() cherrypy.engine.block()
class TestRPHandlerTier2(object): @pytest.fixture(autouse=True) def rphandler_setup(self, httpserver): self.rph = RPHandler(base_url=BASEURL, client_configs=CLIENT_CONFIG) res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = {'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud} idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=client.service_context.keyjar.get_signing_key('oct'), algorithm="HS256", lifetime=300) _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600, 'refresh_token': 'refreshing'} at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json()) client.service['accesstoken'].endpoint = httpserver.url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) token_resp = self.rph.get_access_and_id_token(auth_response, client=client) httpserver.serve_content('{"sub":"EndUserSubject"}') client.service['userinfo'].endpoint = httpserver.url self.rph.get_user_info(res['state'], client, token_resp['access_token']) self.state = res['state'] def test_init_authorization(self): _session = self.rph.get_session_information(self.state) client = self.rph.issuer2rp[_session['iss']] res = self.rph.init_authorization( client, req_args={'scope': ['openid', 'email']}) part = urlsplit(res['url']) _qp = parse_qs(part.query) assert _qp['scope'] == ['openid email'] def test_refresh_access_token(self, httpserver): _session = self.rph.get_session_information(self.state) client = self.rph.issuer2rp[_session['iss']] _info = {"access_token": "2nd_accessTok", "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json()) client.service['refresh_token'].endpoint = httpserver.url res = self.rph.refresh_access_token(self.state, client, 'openid email') assert res['access_token'] == '2nd_accessTok' def test_get_user_info(self, httpserver): _session = self.rph.get_session_information(self.state) client = self.rph.issuer2rp[_session['iss']] httpserver.serve_content( '{"sub":"EndUserSubject", "mail":"*****@*****.**"}') client.service['userinfo'].endpoint = httpserver.url resp = self.rph.get_user_info(self.state, client) assert set(resp.keys()) == {'sub', 'mail'} assert resp['mail'] == '*****@*****.**' def test_has_active_authentication(self): assert self.rph.has_active_authentication(self.state) def test_get_valid_access_token(self): (token, expires_at) = self.rph.get_valid_access_token(self.state) assert token == 'accessTok' assert expires_at > 0