def test_userinfo_in_id_token(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='github') _session = rph_1.get_session_information(res['state']) client = rph_1.issuer2rp[_session['iss']] #_context = client.client_get("service_context") _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.get_client_id() idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud, 'given_name': 'Diana', 'family_name': 'Krall', 'occupation': 'Jazz pianist' } idts = IdToken(**idval) userinfo = rph_1.userinfo_in_id_token(idts) assert set(userinfo.keys()) == { 'sub', 'family_name', 'given_name', 'occupation' }
def test_begin(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='github') assert set(res.keys()) == {'url', 'state'} _github_id = iss_id('github') client = rph_1.issuer2rp[_github_id] assert client.client_get("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_do_client_setup(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) client = rph_1.client_setup('github') _github_id = iss_id('github') _context = client.client_get("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.client_get("service", service_type) _endp = client.client_get("service_context").get('provider_info')[ _srv.endpoint_name] assert _srv.endpoint == _endp assert rph_1.hash2issuer['github'] == _context.get('issuer')
def test_get_session_information(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='github') _session = rph_1.get_session_information(res['state']) assert rph_1.client_configs['github']['issuer'] == _session['iss']
def test_do_provider_info(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) client_1 = rph_1.init_client('github') issuer = rph_1.do_provider_info(client_1) assert issuer == iss_id('github') # Make sure the service endpoints are set rph_2 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) client_2 = rph_2.init_client('github') _context_dump = client_1.client_get("service_context").dump() client_2.client_get("service_context").load(_context_dump) _service_dump = client_1.client_get("services").dump() client_2.client_get("services").load( _service_dump, init_args={"client_get": client_2.client_get}) for service_type in ['authorization', 'accesstoken', 'userinfo']: _srv = client_2.client_get("service", service_type) _endp = client_2.client_get("service_context").provider_info[ _srv.endpoint_name] assert _srv.endpoint == _endp
def test_get_client_from_session_key(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='linkedin') cli1 = rph_1.get_client_from_session_key(state=res['state']) _session = rph_1.get_session_information(res['state']) cli2 = rph_1.issuer2rp[_session['iss']] assert cli1 == cli2 # redo rph_1.do_provider_info(state=res['state']) # get new redirect_uris cli2.client_get("service_context").redirect_uris = [] rph_1.do_client_registration(state=res['state'])
def test_finalize_auth(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='linkedin') _session = rph_1.get_session_information(res['state']) client = rph_1.issuer2rp[_session['iss']] auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = rph_1.finalize_auth(client, _session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'state', 'code'} aresp = client.client_get( "service", 'authorization').client_get("service_context").state.get_item( AuthorizationResponse, 'auth_response', res['state']) assert set(aresp.keys()) == {'state', 'code'}
def test_get_client_authn_method(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='github') _session = rph_1.get_session_information(res['state']) client = rph_1.issuer2rp[_session['iss']] authn_method = rph_1.get_client_authn_method(client, 'token_endpoint') assert authn_method == '' res = rph_1.begin(issuer_id='linkedin') _session = rph_1.get_session_information(res['state']) client = rph_1.issuer2rp[_session['iss']] authn_method = rph_1.get_client_authn_method(client, 'token_endpoint') assert authn_method == 'client_secret_post'
def test_access_and_id_token_by_reference(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='github') _session = rph_1.get_session_information(res['state']) client = rph_1.issuer2rp[_session['iss']] _context = client.client_get("service_context") _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = _context.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } _github_id = iss_id('github') _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.client_get("service", 'accesstoken').endpoint = _url _response = AuthorizationResponse(code='access_code', state=res['state']) _ = rph_1.finalize_auth(client, _session['iss'], _response.to_dict()) resp = rph_1.get_access_and_id_token(state=res['state']) assert resp['access_token'] == 'accessTok' assert isinstance(resp['id_token'], IdToken)
def test_do_client_registration(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) client = rph_1.init_client('github') issuer = rph_1.do_provider_info(client) rph_1.do_client_registration(client, 'github') # only 2 things should have happened assert rph_1.hash2issuer['github'] == issuer assert not client.client_get("service_context").callback.get( 'post_logout_redirect_uris')
def test_pick_config(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) cnf = rph_1.pick_config('facebook') assert cnf['issuer'] == "https://www.facebook.com/v2.11/dialog/oauth" cnf = rph_1.pick_config('linkedin') assert cnf['issuer'] == "https://www.linkedin.com/oauth/v2/" cnf = rph_1.pick_config('github') assert cnf['issuer'] == "https://github.com/login/oauth/authorize" cnf = rph_1.pick_config('') assert 'issuer' not in cnf
def init_oidc_rp_handler(app): _rp_conf = app.rp_config if _rp_conf.key_conf: _kj = init_key_jar(**_rp_conf.key_conf) _path = _rp_conf.key_conf['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 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.client_get("services").keys()) == { 'registration', 'provider_info', 'webfinger', 'authorization', 'accesstoken', 'userinfo', 'refresh_token' } _context = client.client_get("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) _context = client.client_get("service_context") # Calculating request so I can build a reasonable response _req = client.client_get("service", 'registration').construct_request() with responses.RequestsMock() as rsps: request_uri = _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(_context.get('behaviour').keys()) == { 'token_endpoint_auth_method', 'response_types', 'scope', 'application_type', 'application_name' } assert _context.get('client_id') == "client uno" assert _context.get('client_secret') == "VerySecretAndLongEnough" assert _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) _context = client.client_get("service_context") # Calculating request so I can build a reasonable response # Publishing a JWKS instead of a JWKS_URI _context.jwks_uri = '' _context.jwks = _context.keyjar.export_jwks() _req = client.client_get("service", 'registration').construct_request() with responses.RequestsMock() as rsps: request_uri = _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 _context.get('registration_response')