def test_server_authenticated_1(): server = provider_init _session_db = {} cons = Consumer( _session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA state, location = cons.begin("openid", "code", path="http://localhost:8087") resp = server.authorization_endpoint(request=location.split("?")[1]) print resp aresp = cons.parse_response(AuthorizationResponse, location, sformat="urlencoded") print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), [ 'request', 'state', 'redirect_uri', 'claims', 'response_type', 'client_id', 'scope' ])
def test_userinfo_endpoint(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO) cons.debug = True cons.client_secret = "drickyoughurt" cons.config["response_type"] = ["token"] cons.config["request_method"] = "parameter" cons.keyjar[""] = KC_RSA location = cons.begin("openid", "token", path="http://localhost:8087") resp = server.authorization_endpoint(request=location.split("?")[1]) line = resp.message path, query = line.split("?") # redirect atr = AuthorizationResponse().deserialize(query, "urlencoded") uir = UserInfoRequest(access_token=atr["access_token"], schema="openid") resp3 = server.userinfo_endpoint(request=uir.to_urlencoded()) ident = OpenIDSchema().deserialize(resp3.message, "json") print ident.keys() assert _eq(ident.keys(), ['nickname', 'sub', 'name', 'email']) assert ident["sub"] == USERDB["username"]["sub"]
def test_server_authenticated_token(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA cons.config["response_type"] = ["token"] environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] resp = server.authorization_endpoint(environ, start_response) sid = resp[0][len("<form>"):-len("</form>")] environ2 = create_return_form_env("user", "password", sid) resp2 = server.authenticated(environ2, start_response) assert len(resp2) == 1 txt = resp2[0] assert "access_token=" in txt assert "token_type=Bearer" in txt
def test_server_authenticated_none(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA cons.response_type = "none" environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] resp = server.authorization_endpoint(environ, start_response) sid = resp[0][len("<form>"):-len("</form>")] environ2 = create_return_form_env("user", "password", sid) resp2 = server.authenticated(environ2, start_response) assert len(resp2) == 1 txt = resp2[0] pos0 = txt.index("<title>") + len("<title>Redirecting to ") pos1 = txt.index("</title>") location = txt[pos0:pos1] print location assert location.startswith("http://localhost:8087/authz") query = location.split("?")[1] print query assert "token_type=Bearer" in query
def test_userinfo_endpoint(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO) cons.debug = True cons.client_secret = "drickyoughurt" cons.config["response_type"] = ["token"] cons.config["request_method"] = "parameter" cons.keyjar[""] = KC_RSA state, location = cons.begin("openid", "token", path="http://localhost:8087") resp = server.authorization_endpoint(request=location.split("?")[1]) line = resp.message path, query = line.split("#") # redirect atr = AuthorizationResponse().deserialize(query, "urlencoded") uir = UserInfoRequest(access_token=atr["access_token"], schema="openid") resp3 = server.userinfo_endpoint(request=uir.to_urlencoded()) ident = OpenIDSchema().deserialize(resp3.message, "json") print ident.keys() assert _eq(ident.keys(), ['nickname', 'sub', 'name', 'email'])
def setup_userinfo_endpoint(self): cons = Consumer({}, CONSUMER_CONFIG, {"client_id": CLIENT_ID}, server_info=SERVER_INFO, ) cons.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]} cons.keyjar[""] = KC_RSA cons.client_secret = "drickyoughurt" state, location = cons.begin("openid", "token", path=TestConfiguration.get_instance().rp_base) resp = self.provider.authorization_endpoint( request=urlparse(location).query) # redirect atr = AuthorizationResponse().deserialize( urlparse(resp.message).fragment, "urlencoded") uir = UserInfoRequest(access_token=atr["access_token"], schema="openid") resp = self.provider.userinfo_endpoint(request=uir.to_urlencoded()) responses.add( responses.POST, self.op_base + "userinfo", body=resp.message, status=200, content_type='application/json')
def setup_userinfo_endpoint(self): cons = Consumer( {}, CONSUMER_CONFIG, {"client_id": CLIENT_ID}, server_info=SERVER_INFO, ) cons.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } cons.keyjar[""] = KC_RSA cons.client_secret = "drickyoughurt" state, location = cons.begin( "openid", "token", path=TestConfiguration.get_instance().rp_base) resp = self.provider.authorization_endpoint( request=urlparse(location).query) # redirect atr = AuthorizationResponse().deserialize( urlparse(resp.message).fragment, "urlencoded") uir = UserInfoRequest(access_token=atr["access_token"], schema="openid") resp = self.provider.userinfo_endpoint(request=uir.to_urlencoded()) responses.add(responses.POST, self.op_base + "userinfo", body=resp.message, status=200, content_type='application/json')
def test_server_authenticated(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] resp = server.authorization_endpoint(environ, start_response) sid = resp[0][len("<form>"):-len("</form>")] environ2 = create_return_form_env("user", "password", sid) resp2 = server.authenticated(environ2, start_response) print resp2[0] assert len(resp2) == 1 txt = resp2[0] pos0 = txt.index("<title>") + len("<title>Redirecting to ") pos1 = txt.index("</title>") location = txt[pos0:pos1] print location assert location.startswith("http://localhost:8087/authz") environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location part = cons.parse_authz(environ, start_response) aresp = part[0] assert part[1] is None assert part[2] is None #aresp = client.parse_response(AuthorizationResponse, location, # format="urlencoded", # state="id-6da9ca0cc23959f5f33e8becd9b08cae") print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), ['code', 'state', 'scope']) print cons.grant[cons.state].keys() assert _eq(cons.grant[cons.state].keys(), ['code', 'id_token', 'tokens', 'exp_in', 'grant_expiration_time', 'seed'])
def register(environ, start_response, logger, kaka=None): """ Initialize the OAuth2 flow """ _session_db = environ["oic.session_db"] _cc = environ["oic.client_config"] _conc = environ["oic.consumer.config"] _server_info = environ["oic.server.info"] # get the redirect to the authorization server endpoint _oac = Consumer(_session_db, _conc, _cc, _server_info) location = _oac.begin(environ, start_response, logger) logger.info("[1] %s" % (_oac.__dict__,)) resp = http_util.Redirect(location) return resp(environ, start_response)
def test_server_authenticated_2(): server = provider_init server.baseurl = server.name _session_db = {} cons = Consumer( _session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA _state, location = cons.begin(scope="openid email claims_in_id_token", response_type="code id_token", path="http://localhost:8087") print location resp = server.authorization_endpoint(request=location.split("?")[1]) print resp.message part = cons.parse_authz(resp.message) print part aresp = part[0] assert part[1] is None assert part[2] is not None #aresp = cons.parse_response(AuthorizationResponse, location, # sformat="urlencoded") print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), ['scope', 'state', 'code', 'id_token']) print cons.grant[_state].keys() assert _eq(cons.grant[_state].keys(), [ 'code', 'id_token', 'tokens', 'exp_in', 'grant_expiration_time', 'seed' ]) id_token = part[2] assert isinstance(id_token, IdToken) print id_token.keys() assert _eq(id_token.keys(), [ 'nonce', 'c_hash', 'sub', 'iss', 'acr', 'exp', 'auth_time', 'iat', 'aud' ])
def test_sign_userinfo(): consumer = Consumer(SessionDB(SERVER_INFO["issuer"]), CONFIG, CLIENT_CONFIG, SERVER_INFO) consumer.keyjar = CLIKEYS mfos = MyFakeOICServer("http://localhost:8088") mfos.keyjar = SRVKEYS mfos.userinfo_signed_response_alg = "RS256" consumer.http_request = mfos.http_request consumer.redirect_uris = ["http://example.com/authz"] _state = "state0" consumer.nonce = rndstr() consumer.secret_type = "basic" consumer.set_client_secret("hemligt") consumer.keyjar = CLIKEYS consumer.client_prefs = {"userinfo_signed_response_alg": "RS256"} consumer.provider_info = { "userinfo_endpoint": "http://localhost:8088/userinfo", "issuer": "http://localhost:8088/" } del consumer.config["request_method"] args = { "client_id": consumer.client_id, "response_type": "code", "scope": ["openid"], } sid, location = consumer.begin("openid", "code") print location result = consumer.do_authorization_request(state=_state, request_args=args) assert result.status_code == 302 assert result.headers["location"].startswith(consumer.redirect_uris[0]) _, query = result.headers["location"].split("?") consumer.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") consumer.complete(_state) result = consumer.get_user_info(_state) print result assert result.type() == "OpenIDSchema" assert _eq(result.keys(), ['name', 'email', 'verified', 'nickname', 'sub'])
def test_sign_userinfo(): consumer = Consumer(SessionDB(SERVER_INFO["issuer"]), CONFIG, CLIENT_CONFIG, SERVER_INFO) consumer.keyjar = CLIKEYS mfos = MyFakeOICServer("http://localhost:8088") mfos.keyjar = SRVKEYS mfos.userinfo_signed_response_alg = "RS256" consumer.http_request = mfos.http_request consumer.redirect_uris = ["http://example.com/authz"] _state = "state0" consumer.nonce = rndstr() consumer.secret_type = "basic" consumer.set_client_secret("hemligt") consumer.keyjar = CLIKEYS consumer.client_prefs = {"userinfo_signed_response_alg": "RS256"} consumer.provider_info = { "userinfo_endpoint": "http://localhost:8088/userinfo", "issuer": "http://localhost:8088/"} del consumer.config["request_method"] args = { "client_id": consumer.client_id, "response_type": "code", "scope": ["openid"], } sid, location = consumer.begin("openid", "code") print location result = consumer.do_authorization_request(state=_state, request_args=args) assert result.status_code == 302 assert result.headers["location"].startswith(consumer.redirect_uris[0]) _, query = result.headers["location"].split("?") consumer.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") consumer.complete(_state) result = consumer.get_user_info(_state) print result assert result.type() == "OpenIDSchema" assert _eq(result.keys(), ['name', 'email', 'verified', 'nickname', 'sub'])
def test_server_authenticated_none(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA location = cons.begin("openid", response_type="none", path="http://localhost:8087") resp = server.authorization_endpoint(request=location.split("?")[1]) assert resp.message.startswith("http://localhost:8087/authz") query_part = resp.message.split("?")[1] print query_part assert "state" in query_part
def test_server_authenticated_token(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA location = cons.begin("openid", response_type="token", path="http://localhost:8087") resp = server.authorization_endpoint(request=location.split("?")[1]) txt = resp.message assert "access_token=" in txt assert "token_type=Bearer" in txt
def test_server_authenticated(): server = provider_init _session_db = {} cons = Consumer( _session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA _state, location = cons.begin("openid", "code", path="http://localhost:8087") QUERY_STRING = location.split("?")[1] print QUERY_STRING resp = server.authorization_endpoint(request=QUERY_STRING) print resp.message assert resp.message.startswith("http://localhost:8087/authz") part = cons.parse_authz(query=location) aresp = part[0] assert part[1] is None assert part[2] is None #aresp = client.parse_response(AuthorizationResponse, location, # format="urlencoded", # state="id-6da9ca0cc23959f5f33e8becd9b08cae") print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), [ 'request', 'state', 'redirect_uri', 'response_type', 'client_id', 'claims', 'scope' ]) print cons.grant[_state].keys() assert _eq(cons.grant[_state].keys(), [ 'code', 'tokens', 'id_token', 'exp_in', 'seed', 'grant_expiration_time' ])
def test_userinfo_endpoint(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO) cons.debug = True cons.client_secret = "drickyoughurt" cons.config["response_type"] = ["token"] cons.config["request_method"] = "parameter" cons.keyjar[""] = KC_RSA environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] resp = server.authorization_endpoint(environ, start_response) sid = resp[0][len("<form>"):-len("</form>")] environ2 = create_return_form_env("user", "password", sid) resp2 = server.authenticated(environ2, start_response) line = resp2[0] start = line.index("<title>") start += len("<title>Redirecting to ") stop = line.index("</title>") path, query = line[start:stop].split("?") # redirect atr = AuthorizationResponse().deserialize(query, "urlencoded") uir = UserInfoRequest(access_token=atr["access_token"], schema="openid") environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = uir.to_urlencoded() resp3 = server.userinfo_endpoint(environ, start_response) ident = OpenIDSchema().deserialize(resp3[0], "json") print ident.keys() assert _eq(ident.keys(), ['nickname', 'sub', 'name', 'email']) assert ident["sub"] == USERDB["user"]["sub"]
def test_server_authenticated_1(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA location = cons.begin("openid", "code", path="http://localhost:8087") resp = server.authorization_endpoint(request=location.split("?")[1]) print resp aresp = cons.parse_response(AuthorizationResponse, location, sformat="urlencoded") print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), ['request', 'state', 'redirect_uri', 'claims', 'response_type', 'client_id', 'scope'])
def test_server_authenticated_2(): server = provider_init server.baseurl = server.name _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA location = cons.begin(scope="openid email claims_in_id_token", response_type="code id_token", path="http://localhost:8087") print location resp = server.authorization_endpoint(request=location.split("?")[1]) print resp.message part = cons.parse_authz(resp.message) print part aresp = part[0] assert part[1] is None assert part[2] is not None #aresp = cons.parse_response(AuthorizationResponse, location, # sformat="urlencoded") print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), ['scope', 'state', 'code', 'id_token']) print cons.grant[cons.state].keys() assert _eq(cons.grant[cons.state].keys(), ['code', 'id_token', 'tokens', 'exp_in', 'grant_expiration_time', 'seed']) id_token = part[2] assert isinstance(id_token, IdToken) print id_token.keys() assert _eq(id_token.keys(), ['c_hash', 'sub', 'iss', 'acr', 'exp', 'iat', 'aud', 'nonce'])
def test_server_authenticated_token(): server = provider_init _session_db = {} cons = Consumer( _session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA _state, location = cons.begin("openid", response_type="token", path="http://localhost:8087") resp = server.authorization_endpoint(request=location.split("?")[1]) txt = resp.message assert "access_token=" in txt assert "token_type=Bearer" in txt
def test_server_authenticated_none(): server = provider_init _session_db = {} cons = Consumer( _session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA _state, location = cons.begin("openid", response_type="none", path="http://localhost:8087") resp = server.authorization_endpoint(request=location.split("?")[1]) assert resp.message.startswith("http://localhost:8087/authz") query_part = resp.message.split("?")[1] print query_part assert "state" in query_part
def test_server_authenticated_1(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] _ = server.authorization_endpoint(environ, start_response) #sid = resp[0][len("FORM with "):] environ2 = create_return_form_env("user", "password", "abcd") resp2 = server.authenticated(environ2, start_response) print resp2 assert resp2 == ['<html>Could not find session</html>']
def test_server_authenticated(): server = provider_init _session_db = {} cons = Consumer(_session_db, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) cons.debug = True cons.keyjar[""] = KC_RSA location = cons.begin("openid", "code", path="http://localhost:8087") QUERY_STRING = location.split("?")[1] print QUERY_STRING resp = server.authorization_endpoint(request=QUERY_STRING) print resp.message assert resp.message.startswith("http://localhost:8087/authz") part = cons.parse_authz(query=location) aresp = part[0] assert part[1] is None assert part[2] is None #aresp = client.parse_response(AuthorizationResponse, location, # format="urlencoded", # state="id-6da9ca0cc23959f5f33e8becd9b08cae") print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), ['request', 'state', 'redirect_uri', 'response_type', 'client_id', 'claims', 'scope']) print cons.grant[cons.state].keys() assert _eq(cons.grant[cons.state].keys(), ['tokens', 'id_token', 'exp_in', 'seed', 'grant_expiration_time'])
def register(environ, start_response): _oac = Consumer(SESSION_DB, CONSUMER_CONFIG, CLIENT_CONFIG, SERVER_INFO) location = _oac.begin(environ, start_response, DEVNULL()) resp = http_util.Redirect(location) return resp(environ, start_response)
class TestProvider(object): CDB = { "number5": { "password": "******", "client_secret": "drickyoughurt", "redirect_uris": [("http://localhost:8087/authz", None)], "post_logout_redirect_uris": [("https://example.com/post_logout", None)], "client_salt": "salted", "response_types": [ "code", "token", "code id_token", "none", "code token", "id_token", ], }, "a1b2c3": { "redirect_uris": [("http://localhost:8088/authz", None)], "client_salt": "salted", "client_secret": "very_secret", "response_types": ["code", "token", "code id_token"], }, "client0": { "redirect_uris": [("http://www.example.org/authz", None)], "client_secret": "very_secret", "post_logout_redirect_uris": [("https://www.example.org/post_logout", None)], "client_salt": "salted", "response_types": ["code", "token", "code id_token"], }, CLIENT_ID: { "client_secret": CLIENT_SECRET, "redirect_uris": [("http://localhost:8087/authz", None)], "client_salt": "salted", "token_endpoint_auth_method": "client_secret_post", "response_types": ["code", "token", "code id_token"], }, } # type: Dict[str, Dict[str, Any]] @pytest.fixture(autouse=True) def create_provider(self, session_db_factory): self.provider = Provider( SERVER_INFO["issuer"], session_db_factory(SERVER_INFO["issuer"]), self.CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR, ) self.provider.baseurl = self.provider.name self.provider.logout_verify_url = "https://127.0.0.1/logout_verify.html" self.cons = Consumer( DictSessionBackend(), CONSUMER_CONFIG.copy(), CLIENT_CONFIG, server_info=SERVER_INFO, ) self.cons.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.cons.keyjar[""] = KC_RSA self.cons.keyjar.import_jwks(self.provider.keyjar.export_jwks(), self.cons.issuer) self.cons2 = Consumer({}, CONSUMER_CONFIG.copy(), CLIENT_CONFIG_2, server_info=SERVER_INFO) self.cons2.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.cons2.keyjar[""] = KC_RSA def _code_auth(self): state, location = self.cons.begin("openid", "code", path="http://localhost:8087") return self.provider.authorization_endpoint( request=location.split("?")[1]) def _code_auth2(self): state, location = self.cons2.begin("openid", "code", path="http://www.example.org") return self.provider.authorization_endpoint( request=location.split("?")[1]) def _auth_with_id_token(self): state, location = self.cons.begin("openid", "id_token", path="http://localhost:8087") resp = self.provider.authorization_endpoint( request=location.split("?")[1]) aresp = self.cons.parse_response(AuthorizationResponse, resp.message, sformat="urlencoded") return aresp["id_token"] def _create_cookie(self, user, client_id, c_type="sso"): cd = CookieDealer(self.provider) set_cookie = cd.create_cookie("{}][{}".format(user, client_id), c_type, self.provider.sso_cookie_name) cookies_string = set_cookie[1] all_cookies = SimpleCookie() # type: SimpleCookie try: cookies_string = cookies_string.decode() except (AttributeError, UnicodeDecodeError): pass all_cookies.load(cookies_string) return all_cookies def test_missing_post_logout_redirect_uri(self): esr = EndSessionRequest(state="foo") assert self.provider.verify_post_logout_redirect_uri(esr, CLIENT_ID) is None def test_wrong_post_logout_redirect_uri(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://example.com/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru") assert self.provider.verify_post_logout_redirect_uri(esr, CLIENT_ID) is None def test_no_post_logout_redirect_uri(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://example.com/plru", "https://example.com/plru2", ] esr = EndSessionRequest(state="foo") assert self.provider.verify_post_logout_redirect_uri(esr, CLIENT_ID) is None def test_let_user_verify_logout(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://localhost:8087/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru") res = self.provider.let_user_verify_logout("user", esr, None, None) assert isinstance(res, Response) assert res.headers == [("Content-type", "text/html")] assert res.status_code == 200 def test_let_user_verify_logout_with_cookie(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://localhost:8087/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru") res = self.provider.let_user_verify_logout("user", esr, [("Set-Cookie", "kaka")], None) assert isinstance(res, Response) assert set(res.headers) == { ("Content-type", "text/html"), ("Set-Cookie", "kaka"), } assert res.status_code == 200 def test_let_user_verify_logout_with_redirect(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://localhost:8087/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru") res = self.provider.let_user_verify_logout( "user", esr, None, "https://example.com/redirect") assert isinstance(res, Response) assert set(res.headers) == {("Content-type", "text/html")} assert res.status_code == 200 # make sure the redirect was propagated txt = '<input type="hidden" name="{}" value="{}"/>'.format( "post_logout_redirect_uri", "https://localhost:8087/plru") assert txt in res.message def test_let_user_verify_logout_with_id_token_hint(self): self.provider.cdb[CLIENT_ID]["post_logout_redirect_uris"] = [ "https://localhost:8087/plru" ] esr = EndSessionRequest( state="foo", post_logout_redirect_uri="https://localhost:8087/plru", id_token_hint="J.W.S", ) res = self.provider.let_user_verify_logout("user", esr, None, None) assert isinstance(res, Response) assert set(res.headers) == {("Content-type", "text/html")} assert res.status_code == 200 # make sure the id_token_hint was propagated txt = '<input type="hidden" name="{}" value="{}"/>'.format( "id_token_hint", "J.W.S") assert txt in res.message def test_end_session_endpoint_with_cookie(self): self.provider.events = DummyEventStore() self._code_auth() cookie = self._create_cookie("username", "number5") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["state"] == "abcde" assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://example.com/post_logout" def test_end_session_endpoint_with_wrong_cookie(self): # Need cookie and ID Token to figure this out id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) # verify we got valid session id_token_hint = id_token.to_jwt(algorithm="none") cookie = self._create_cookie("diggins", "number5") resp = self.provider.end_session_endpoint(urlencode( {"id_token_hint": id_token_hint}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" assert _err["error_description"] == "Wrong user" def test_end_session_endpoint_with_cookie_wrong_user(self): # Need cookie and ID Token to figure this out id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) id_token_hint = id_token.to_jwt(algorithm="none") cookie = self._create_cookie("diggins", "number5") resp = self.provider.end_session_endpoint(urlencode( {"id_token_hint": id_token_hint}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" assert _err["error_description"] == "Wrong user" def test_end_session_endpoint_with_cookie_wrong_client(self): # Need cookie and ID Token to figure this out id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) id_token_hint = id_token.to_jwt(algorithm="none") # Wrong client_id cookie = self._create_cookie("username", "a1b2c3") resp = self.provider.end_session_endpoint(urlencode( {"id_token_hint": id_token_hint}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" def test_end_session_endpoint_with_cookie_dual_login(self): self._code_auth() self._code_auth2() cookie = self._create_cookie("username", "client0") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["state"] == "abcde" assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "client0" assert jwt_info[ "redirect_uri"] == "https://www.example.org/post_logout" def test_end_session_endpoint_with_cookie_dual_login_wrong_client(self): self._code_auth() self._code_auth2() # The cookie states that a user has a session at a client and this # statement is false. cookie = self._create_cookie("username", "a1b2c3") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" def test_end_session_endpoint_with_id_token_hint_only(self): id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) id_token_hint = id_token.to_jwt(algorithm="none") resp = self.provider.end_session_endpoint( urlencode({"id_token_hint": id_token_hint})) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://example.com/post_logout" def test_end_session_endpoint_with_id_token_hint_and_cookie(self): id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) id_token_hint = id_token.to_jwt(algorithm="none") cookie = self._create_cookie("username", "number5") resp = self.provider.end_session_endpoint(urlencode( {"id_token_hint": id_token_hint}), cookie=cookie) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://example.com/post_logout" def test_end_session_endpoint_with_post_logout_redirect_uri(self): self._code_auth() cookie = self._create_cookie("username", "number5") post_logout_redirect_uri = self.CDB[str( CLIENT_CONFIG["client_id"])]["post_logout_redirect_uris"][0][0] resp = self.provider.end_session_endpoint( urlencode({ "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde" }), cookie=cookie, ) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["state"] == "abcde" assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://example.com/post_logout" def test_end_session_endpoint_without_post_logout_redirect_uri(self): # default post logout page registered self.provider.post_logout_page = "https://foo.example.com/def_post" # No post_logout_redirect_uris registered _plru = self.provider.cdb["number5"]["post_logout_redirect_uris"] del self.provider.cdb["number5"]["post_logout_redirect_uris"] self._code_auth() cookie = self._create_cookie("username", "number5") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) jwt_info = self.provider.unpack_signed_jwt(qs["sjwt"][0]) assert jwt_info["state"] == "abcde" assert jwt_info["uid"] == "username" assert jwt_info["client_id"] == "number5" assert jwt_info["redirect_uri"] == "https://foo.example.com/def_post" # restore self.provider.cdb["number5"]["post_logout_redirect_uris"] = _plru def test_end_session_endpoint_without_post_logout_redirect_uri_no_default( self): # No post_logout_redirect_uris registered _plru = self.provider.cdb["number5"]["post_logout_redirect_uris"] del self.provider.cdb["number5"]["post_logout_redirect_uris"] self._code_auth() cookie = self._create_cookie("username", "number5") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "server_error" # restore self.provider.cdb["number5"]["post_logout_redirect_uris"] = _plru def test_end_session_endpoint_bogus_sjwt(self): self._code_auth() cookie = self._create_cookie("username", "number5") post_logout_redirect_uri = self.CDB[str( CLIENT_CONFIG["client_id"])]["post_logout_redirect_uris"][0][0] resp = self.provider.end_session_endpoint( urlencode({ "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde" }), cookie=cookie, ) # returns a SeeOther instance p = urlparse(resp.message) qs = parse_qs(p.query) _sjwt = qs["sjwt"][0] _sjwt = ".".join(_sjwt.split(".")[:2]) + "." # Not signed with pytest.raises(ValueError): self.provider.unpack_signed_jwt(_sjwt) def test_end_session_endpoint_with_wrong_post_logout_redirect_uri(self): self._code_auth() cookie = self._create_cookie("username", "number5") post_logout_redirect_uri = "https://www.example.com/logout" resp = self.provider.end_session_endpoint( urlencode({ "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde" }), cookie=cookie, ) assert isinstance(resp, Response) _err = ErrorResponse().from_json(resp.message) assert _err["error"] == "invalid_request" def test_end_session_endpoint_with_registered_post_logout_redirect_uri_with_query_part( self, ): self._code_auth() cookie = self._create_cookie("username", "number5") self.provider.cdb["number5"]["post_logout_redirect_uris"] = [ ("https://www.example.com/logout", { "foo": ["bar"] }) ] # No post_logout_redirect_uri in request resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(resp, Response) _qp = parse_qs(resp.message.split("?")[1]) _jwt = self.provider.unpack_signed_jwt(_qp["sjwt"][0]) assert _jwt["redirect_uri"] == "https://www.example.com/logout?foo=bar" def test_back_channel_logout_no_uri(self): self._code_auth() res = self.provider.do_back_channel_logout( self.provider.cdb[CLIENT_ID], "username", "sid") assert res is None def test_back_channel_logout(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb["backchannel_logout_uri"] = "https://example.com/bc_logout" _cdb["client_id"] = CLIENT_ID res = self.provider.do_back_channel_logout(_cdb, "username", "_sid_") assert isinstance(res, tuple) assert res[0] == "https://example.com/bc_logout" _jwt = self.provider.unpack_signed_jwt(res[1]) assert _jwt assert _jwt["iss"] == SERVER_INFO["issuer"] assert _jwt["aud"] == [CLIENT_ID] assert _jwt["sub"] == "username" assert _jwt["sid"] == "_sid_" def test_front_channel_logout(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb["frontchannel_logout_uri"] = "https://example.com/fc_logout" _cdb["client_id"] = CLIENT_ID res = self.provider.do_front_channel_logout_iframe( _cdb, str(SERVER_INFO["issuer"]), "_sid_") assert res == '<iframe src="https://example.com/fc_logout">' def test_front_channel_logout_session_required(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb["frontchannel_logout_uri"] = "https://example.com/fc_logout" _cdb["frontchannel_logout_session_required"] = True _cdb["client_id"] = CLIENT_ID res = self.provider.do_front_channel_logout_iframe( _cdb, str(SERVER_INFO["issuer"]), "_sid_") m = re.match(r'<iframe src="([^"]+)">', str(res)) assert m _q = parse_qs(str(m.group(1)).split("?")[1]) assert set(_q.keys()) == {"iss", "sid"} def test_front_channel_logout_session_required_uri_query(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb[ "frontchannel_logout_uri"] = "https://example.com/fc_logout?foo=bar" _cdb["frontchannel_logout_session_required"] = True _cdb["client_id"] = CLIENT_ID res = self.provider.do_front_channel_logout_iframe( _cdb, str(SERVER_INFO["issuer"]), "_sid_") m = re.match(r'<iframe src="([^"]+)">', str(res)) assert m _q = parse_qs(str(m.group(1)).split("?")[1]) assert set(_q.keys()) == {"foo", "iss", "sid"} def test_front_channel_logout_missing_url(self): self._code_auth() _cdb = copy.copy(self.provider.cdb[CLIENT_ID]) _cdb["client_id"] = CLIENT_ID res = self.provider.do_front_channel_logout_iframe( _cdb, str(SERVER_INFO["issuer"]), "_sid_") assert res is None def test_logout_from_client_bc(self): self._code_auth() self.provider.cdb[CLIENT_ID][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb[CLIENT_ID]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] res = self.provider.logout_info_for_one_client(_sid, CLIENT_ID) assert set(res.keys()) == {"back_channel", "front_channel"} assert res["back_channel"] != {} assert res["front_channel"] == {} assert set(res["back_channel"].keys()) == {CLIENT_ID} _spec = res["back_channel"][CLIENT_ID] assert _spec[0] == "https://example.com/bc_logout" _jwt = self.provider.unpack_signed_jwt(_spec[1]) assert _jwt assert _jwt["iss"] == SERVER_INFO["issuer"] assert _jwt["aud"] == [CLIENT_ID] assert _jwt["sid"] == _sid def test_logout_from_client_fc(self): self._code_auth() del self.provider.cdb[CLIENT_ID]["backchannel_logout_uri"] self.provider.cdb[CLIENT_ID][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb[CLIENT_ID]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] res = self.provider.logout_info_for_one_client(_sid, CLIENT_ID) assert set(res.keys()) == {"front_channel", "back_channel"} assert res["back_channel"] == {} assert set(res["front_channel"].keys()) == {CLIENT_ID} _spec = res["front_channel"][CLIENT_ID] assert _spec == '<iframe src="https://example.com/fc_logout">' def test_logout_from_client(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] res = self.provider.logout_info_for_all_clients(sid=_sid) assert res assert set(res.keys()) == {"back_channel", "front_channel"} assert set(res["front_channel"].keys()) == {"number5"} _spec = res["front_channel"]["number5"] assert _spec == '<iframe src="https://example.com/fc_logout">' assert set(res["back_channel"].keys()) == {"client0"} _spec = res["back_channel"]["client0"] assert _spec[0] == "https://example.com/bc_logout" _jwt = self.provider.unpack_signed_jwt(_spec[1]) assert _jwt assert _jwt["iss"] == SERVER_INFO["issuer"] assert _jwt["aud"] == ["client0"] def test_logout_spec_all(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] logout_spec_all = self.provider.logout_info_for_all_clients(sid=_sid) assert set(logout_spec_all.keys()) == {"back_channel", "front_channel"} assert set(logout_spec_all["back_channel"].keys()) == {"client0"} assert set(logout_spec_all["front_channel"].keys()) == {"number5"} def test_do_verified_logout_all(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] with responses.RequestsMock() as rsps: rsps.add(rsps.POST, "https://example.com/bc_logout", status=200) res = self.provider.do_verified_logout(_sid, CLIENT_ID, alla=True) assert set(res.keys()) == {"iframe", "cookie"} def test_do_verified_logout_just_the_one(self): self.provider.events = DummyEventStore() self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # There is no back channel logout, hence there should be no HTTP POST exception = requests.ConnectionError() with responses.RequestsMock( assert_all_requests_are_fired=False) as rsps: rsps.add(responses.POST, "https://example.com/bc_logout", body=exception) res = self.provider.do_verified_logout(_sid, CLIENT_ID, alla=False) assert set(res.keys()) == {"iframe", "cookie"} def test_do_verified_logout_the_other(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # This only does back channel logout with responses.RequestsMock() as rsps: rsps.add(rsps.POST, "https://example.com/bc_logout", status=200) res = self.provider.do_verified_logout(_sid, "client0", alla=False) assert set(res.keys()) == {"cookie"} def test_do_verified_logout_the_other_back_channel_failed(self): self._code_auth() self._code_auth2() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = CLIENT_ID # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # Does back channel logout and it will fail with responses.RequestsMock() as rsps: rsps.add(rsps.POST, "https://example.com/bc_logout", status=400) res = self.provider.do_verified_logout(_sid, "client0", alla=False) assert list(res.keys()) == [] def test_end_session_endpoint_no_post_logout_redirect_uri(self): self._code_auth() cookie = self._create_cookie("username", "number5") self.provider.cdb["number5"]["post_logout_redirect_uris"] = [ ("https://example.com/plru", ""), ("https://example.com/plru2", ""), ] res = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(res, Response) assert res.status_code == 400 def test_logout_info_for_all_clients_no_params(self): with pytest.raises(ParameterError): self.provider.logout_info_for_all_clients() def test_do_back_channel_logout_no_backchannel(self): self._code_auth() # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] _sub = self.provider.sdb[_sid]["sub"] # if "backchannel_logout_uri" in self.provider.cdb["number5"]: del self.provider.cdb["number5"]["backchannel_logout_uri"] res = self.provider.do_back_channel_logout( self.provider.cdb["number5"], _sub, _sid) assert res is None def test_id_token_hint_multiple_aud(self): id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) # verify we got valid session self.provider.cdb["number5"]["post_logout_redirect_uris"] = [ ("https://example.com/plru", "") ] # add another aud and an azp. id_token["azp"] = id_token["aud"][0] id_token["aud"].append("foobar") id_token_hint = id_token.to_jwt(algorithm="none") resp = self.provider.end_session_endpoint( urlencode({"id_token_hint": id_token_hint})) assert isinstance(resp, SeeOther) def test_id_token_hint_aud_does_not_match_client_id(self): id_token = self._auth_with_id_token() assert session_get(self.provider.sdb, "sub", id_token["sub"]) # verify we got valid session # add another aud and an azp. id_token_hint = id_token.to_jwt(algorithm="none") # Mess with the session DB _sid = list(self.provider.sdb._db.storage.keys())[0] self.provider.sdb[_sid]["client_id"] = "something else" resp = self.provider.end_session_endpoint( urlencode({"id_token_hint": id_token_hint})) assert isinstance(resp, Response) assert resp.status_code == 400 def test_no_back_or_front_channel_logout(self): self._code_auth() # Mess with client DB for c in ["backchannel_logout_uri", "frontchannel_logout_uri"]: if c in self.provider.cdb["number5"]: del self.provider.cdb["number5"][c] resp = self.provider.do_verified_logout(sid=list( self.provider.sdb._db.storage.keys())[0], client_id="number5") assert resp == {} def test_back_channel_logout_fails(self): self._code_auth() # client0 self.provider.cdb["client0"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["client0"]["client_id"] = "client0" # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # There is no back channel logout, hence there should be no HTTP POST with responses.RequestsMock(): res = self.provider.do_verified_logout(_sid, "client0", alla=False) assert res == {} def test_logout_info_for_one_client_no_logout_info(self): self._code_auth() # Mess with client DB for c in ["backchannel_logout_uri", "frontchannel_logout_uri"]: if c in self.provider.cdb["number5"]: del self.provider.cdb["number5"][c] # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] resp = self.provider.logout_info_for_one_client(_sid, "number5") assert resp == {"back_channel": {}, "front_channel": {}} def test_unknown_client(self): self._code_auth() cookie = self._create_cookie("username", "unknown") resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"}), cookie=cookie) assert isinstance(resp, Response) assert resp.status_code == 400 def test_no_cookie_no_id_token_hint(self): self._code_auth() resp = self.provider.end_session_endpoint(urlencode({"state": "abcde"})) assert isinstance(resp, Response) assert resp.status_code == 400 def test_back_channel_logout_failed_front_channel_logout_exists(self): self._code_auth() # client0 self.provider.cdb["number5"][ "backchannel_logout_uri"] = "https://example.com/bc_logout" self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = "number5" # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] # Does back channel logout and it will fail with responses.RequestsMock() as rsps: rsps.add(rsps.POST, "https://example.com/bc_logout", status=400) res = self.provider.do_verified_logout(_sid, "client0", alla=True) assert set(res.keys()) == {"cookie", "iframe"} def test_logout_from_clients_one_without_logout_info(self): self._code_auth() self._code_auth2() # Mess with client DB # neither back channel nor front channel for c in ["backchannel_logout_uri", "frontchannel_logout_uri"]: if c in self.provider.cdb["client0"]: del self.provider.cdb["client0"][c] self.provider.cdb["client0"]["client_id"] = "client0" # both back channel and front channel self.provider.cdb["number5"][ "frontchannel_logout_uri"] = "https://example.com/fc_logout" self.provider.cdb["number5"]["client_id"] = "number5" # Get a session ID, anyone will do. # I know the session backend DB is a DictSessionBackend so I can use that _sid = list(self.provider.sdb._db.storage.keys())[0] res = self.provider.logout_info_for_all_clients(sid=_sid) assert set(res.keys()) == {"back_channel", "front_channel"} assert set(res["back_channel"].keys()) == {"number5"} assert set(res["front_channel"].keys()) == {"number5"}
class TestOICConsumer: @pytest.fixture(autouse=True) def setup_consumer(self, session_db_factory): client_id = "client_1" client_config = { "client_id": client_id, "client_authn_method": CLIENT_AUTHN_METHOD, } self.consumer = Consumer(DictSessionBackend(), CONFIG, client_config, SERVER_INFO) self.consumer.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.consumer.keyjar = CLIKEYS self.consumer.redirect_uris = ["https://example.com/cb"] self.consumer.authorization_endpoint = "https://example.com/authorization" self.consumer.token_endpoint = "https://example.com/token" self.consumer.userinfo_endpoint = "https://example.com/userinfo" # type: ignore self.consumer.client_secret = "hemlig" self.consumer.secret_type = "basic" def test_backup_keys(self): keys = self.consumer.__dict__.keys() _dict = self.consumer.dictionary() dkeys = [key for key in keys if key not in _dict.keys()] assert _eq(dkeys, IGNORE) def test_backup_restore(self): authz_org_url = "http://example.org/authorization" _dict = sorted(list(self.consumer.__dict__.items())) self.consumer._backup("sid") self.consumer.restore("sid") assert sorted(_dict) == sorted(list(self.consumer.__dict__.items())) self.consumer.authorization_endpoint = authz_org_url assert _dict != sorted(list(self.consumer.__dict__.items())) self.consumer.restore("sid") assert _dict == sorted(list(self.consumer.__dict__.items())) def test_backup_restore_update(self): authz_org_url = "http://example.org/authorization" self.consumer._backup("sid") self.consumer.authorization_endpoint = authz_org_url self.consumer.token_endpoint = "https://example.org/token" self.consumer.userinfo_endpoint = "" # type: ignore assert self.consumer.authorization_endpoint == authz_org_url assert self.consumer.token_endpoint == "https://example.org/token" assert self.consumer.userinfo_endpoint == "" # type: ignore self.consumer.update("sid") assert self.consumer.authorization_endpoint == authz_org_url assert self.consumer.token_endpoint == "https://example.org/token" assert (self.consumer.userinfo_endpoint # type: ignore == "https://example.com/userinfo") def test_begin(self): srv = Server() srv.keyjar = SRVKEYS sid, location = self.consumer.begin("openid", "code") authreq = srv.parse_authorization_request(url=location) assert _eq( list(authreq.keys()), [ "state", "max_age", "claims", "response_type", "client_id", "scope", "redirect_uri", ], ) assert authreq["state"] == sid assert authreq["scope"] == self.consumer.consumer_config["scope"] assert authreq["client_id"] == self.consumer.client_id def test_begin_file(self, tmpdir): path = tmpdir.strpath external_path = "/exported" self.consumer.consumer_config["request_method"] = "file" self.consumer.consumer_config["temp_dir"] = path self.consumer.consumer_config["temp_path"] = external_path self.consumer.consumer_config["authz_page"] = "/authz" srv = Server() srv.keyjar = SRVKEYS sid, location = self.consumer.begin("openid", "code", path="http://*****:*****@example.com", "nickname": "Ilja", "verified": True, }, ) result = self.consumer.do_authorization_request(state=_state, request_args=args) parsed = urlparse(result.headers["location"]) self.consumer.parse_response(AuthorizationResponse, info=parsed.query, sformat="urlencoded") self.consumer.complete(_state) result = self.consumer.get_user_info(_state) assert isinstance(result, OpenIDSchema) assert _eq(result.keys(), ["name", "email", "verified", "nickname", "sub"]) def test_sign_userinfo(self): _state = "state0" self.consumer.client_prefs = {"userinfo_signed_response_alg": "RS256"} del self.consumer.consumer_config["request_method"] args = { "client_id": self.consumer.client_id, "response_type": "code", "scope": ["openid"], } location = "https://example.com/cb?code=code&state=state0" with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) rsps.add( responses.POST, "https://example.com/token", content_type="application/json", json={ "access_token": "some_token", "token_type": "bearer", "state": "state0", "scope": "openid", }, ) rsps.add( responses.POST, "https://example.com/userinfo", content_type="application/json", json={ "name": "Ilja", "sub": "some_sub", "email": "*****@*****.**", "nickname": "Ilja", "verified": True, }, ) self.consumer.begin("openid", "code") result = self.consumer.do_authorization_request(state=_state, request_args=args) parsed = urlparse(result.headers["location"]) self.consumer.parse_response(AuthorizationResponse, info=parsed.query, sformat="urlencoded") self.consumer.complete(_state) result = self.consumer.get_user_info(_state) assert isinstance(result, OpenIDSchema) assert _eq(result.keys(), ["name", "email", "verified", "nickname", "sub"]) def test_get_userinfo_claims(self): _state = "state0" args = { "client_id": self.consumer.client_id, "response_type": "code", "scope": ["openid"], } location = "https://example.com/cb?code=code&state=state0" with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) rsps.add( responses.POST, "https://example.com/token", content_type="application/json", json={ "access_token": "some_token", "token_type": "bearer", "state": "state0", "scope": "openid", }, ) rsps.add( responses.POST, "https://example.com/userinfo", content_type="application/json", json={ "name": "Ilja", "sub": "some_sub", "email": "*****@*****.**", "nickname": "Ilja", "verified": True, }, ) result = self.consumer.do_authorization_request(state=_state, request_args=args) parsed = urlparse(result.headers["location"]) self.consumer.parse_response(AuthorizationResponse, info=parsed.query, sformat="urlencoded") response = self.consumer.complete(_state) result = self.consumer.get_userinfo_claims( response["access_token"], self.consumer.userinfo_endpoint, # type: ignore ) assert isinstance(result, OpenIDSchema) assert _eq(result.keys(), ["name", "email", "verified", "nickname", "sub"]) def real_test_discover(self): c = Consumer(None, None) principal = "*****@*****.**" res = c.discover(principal) assert isinstance(res, ProviderConfigurationResponse) assert _eq( res.keys(), [ "registration_endpoint", "scopes_supported", "identifiers_supported", "token_endpoint", "flows_supported", "version", "userinfo_endpoint", "authorization_endpoint", "x509_url", "issuer", ], ) assert res.version == "3.0" # type: ignore assert _eq( res.flows_supported, # type: ignore [ "code", "token", "id_token", "code token", "code id_token", "id_token token", ], ) def test_discover(self): c = Consumer(None, None) webfinger = { "subject": "acct:[email protected]", "links": [{ "rel": "http://openid.net/specs/connect/1.0/issuer", "href": "https://*****:*****@example.com" with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/.well-known/webfinger" "?resource=acct%3Afoo%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer", json=webfinger, ) res = c.discover(principal) assert res == "https://localhost:8088/" def test_client_register(self): c = Consumer(None, None) c.redirect_uris = ["https://example.com/authz"] reg_resp = { "client_id": "some_client", "client_secret": "super_secret", "client_secret_expires_at": 123456789, "redirect_uris": ["https://example.com/authz"], } with responses.RequestsMock() as rsps: rsps.add(responses.POST, "https://example.com/register/", json=reg_resp) c.register("https://example.com/register/") assert json.loads(rsps.calls[0].request.body) == { "application_type": "web", "response_types": ["code"], "redirect_uris": ["https://example.com/authz"], "grant_types": ["authorization_code"], } assert c.client_id == "some_client" assert c.client_secret == "super_secret" assert c.registration_expires == 123456789 def test_client_register_token(self): c = Consumer(None, None) c.redirect_uris = ["https://example.com/authz"] client_info = { "client_id": "clientid", "redirect_uris": ["https://example.com/authz"], } with responses.RequestsMock() as rsps: rsps.add( rsps.POST, "https://provider.example.com/registration/", json=client_info, ) c.register( "https://provider.example.com/registration/", registration_token="initial_registration_token", ) header = rsps.calls[0].request.headers["Authorization"] assert header == "Bearer aW5pdGlhbF9yZWdpc3RyYXRpb25fdG9rZW4=" def test_client_register_token_b64(self): c = Consumer(None, None) c.redirect_uris = ["https://example.com/authz"] client_info = { "client_id": "clientid", "redirect_uris": ["https://example.com/authz"], } registration_token = ( "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6IC" "JlYjc1N2M3Yy00MWRlLTRmZDYtOTkwNy1hNGFiMDY1ZjEzMmEifQ.eyJqdGkiOiI2ZWY0MDZi" "MC02YzA3LTQ0NzctOWU1YS1hY2FiZjNiMWNiMjgiLCJleHAiOjAsIm5iZiI6MCwiaWF0Ijox" "NTczNzMxNjg5LCJpc3MiOiJodHRwczovL29wZW5pZC1wcm92aWRlci5leGFtcGxlLmNvbS9h" "dXRoL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJodHRwczovL29wZW5pZC1wcm92aWRlci5leGFt" "cGxlLmNvbS9hdXRoL3JlYWxtcy9tYXN0ZXIiLCJ0eXAiOiJJbml0aWFsQWNjZXNzVG9rZW4i" "fQ.0XTlit_JcxPZeIy8A4BzrHn1NvegVP7ws8KI0ySFex8") with responses.RequestsMock() as rsps: rsps.add( rsps.POST, "https://provider.example.com/registration/", json=client_info, ) c.register( "https://provider.example.com/registration/", registration_token=registration_token, ) header = rsps.calls[0].request.headers["Authorization"] assert header == "Bearer " + registration_token def _faulty_id_token(self): idval = { "nonce": "KUEYfRM2VzKDaaKD", "sub": "EndUserSubject", "iss": "https://alpha.cloud.nds.rub.de", "exp": 1420823073, "iat": 1420822473, "aud": "TestClient", } idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=[SYMKey(key="TestPassword")], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" return ".".join(p) def test_faulty_id_token(self): _faulty_signed_jwt = self._faulty_id_token() with pytest.raises(BadSignature): IdToken().from_jwt(_faulty_signed_jwt, key=[SYMKey(key="TestPassword")]) # What if no verification key is given ? # Should also result in an exception with pytest.raises(MissingSigningKey): IdToken().from_jwt(_faulty_signed_jwt) def test_faulty_id_token_in_access_token_response(self): c = Consumer(None, None) c.keyjar.add_symmetric("", "TestPassword", ["sig"]) _info = { "access_token": "accessTok", "id_token": self._faulty_id_token(), "token_type": "Bearer", } _json = json.dumps(_info) with pytest.raises(ValueError): c.parse_response(AccessTokenResponse, _json, sformat="json") def test_faulty_idtoken_from_accesstoken_endpoint(self): _state = "state0" self.consumer.consumer_config["response_type"] = ["id_token"] args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], } location = ( "https://example.com/cb?state=state0&id_token=eyJhbGciOiJSUzI1NiJ9" ".eyJpc3MiOiAiaHR0cDovL2xvY2FsaG9zdDo4MDg4IiwgInN1YiI6ICJhNWRkMjRiMmYwOGE2ODZmZDM4NmMyMmM" "zZmY4ZWUyODFlZjJmYmZmMWZkZTcwMDg2NjhjZGEzZGVjZmE0NjY5IiwgImF1ZCI6IFsiY2xpZW50XzEiXSwgImV" "4cCI6IDE1NzIwOTk5NjAsICJhY3IiOiAiMiIsICJpYXQiOiAxNTcyMDEzNTYwLCAibm9uY2UiOiAibmdFTGZVdmN" "PMWoyaXNWcXkwQWNwM0NOYlZnMGdFRDEifQ.aaa") with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) result = self.consumer.do_authorization_request(state=_state, request_args=args) self.consumer._backup("state0") assert result.status_code == 302 query = urlparse(result.headers["location"]).query with pytest.raises(BadSignature): self.consumer.parse_authz(query=query) def test_get_session_management_id(self): now = utc_time_sans_frac() smid = "session_management_id" idval = { "nonce": "KUEYfRM2VzKDaaKD", "sub": "EndUserSubject", "iss": "https://example.com", "exp": now + 3600, "iat": now, "aud": self.consumer.client_id, "sid": smid, } idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=KC_RSA.keys(), algorithm="RS256") _state = "state" self.consumer.sdb[_state] = { "redirect_uris": ["https://example.org/cb"] } resp = AuthorizationResponse(id_token=_signed_jwt, state=_state) self.consumer.consumer_config["response_type"] = ["id_token"] self.consumer.parse_authz(resp.to_urlencoded()) assert self.consumer.sso_db["state"]["smid"] == smid assert session_get(self.consumer.sso_db, "smid", smid) == [_state]
class TestOICConsumer(): @pytest.fixture(autouse=True) def setup_consumer(self): client_id = "client_1" client_config = { "client_id": client_id, "client_authn_method": CLIENT_AUTHN_METHOD, #'config': {} } self.consumer = Consumer(SessionDB(SERVER_INFO["issuer"]), CONFIG, client_config, SERVER_INFO) self.consumer.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]} self.consumer.client_secret = "abcdefghijklmnop" self.consumer.keyjar = CLIKEYS self.consumer.redirect_uris = ["https://example.com/cb"] self.consumer.authorization_endpoint = "http://example.com/authorization" self.consumer.token_endpoint = "http://example.com/token" self.consumer.userinfo_endpoint = "http://example.com/userinfo" self.consumer.client_secret = "hemlig" self.consumer.secret_type = "basic" mfos = MyFakeOICServer("http://*****:*****@connect-op.heroku.com" res = c.discover(principal) assert isinstance(res, ProviderConfigurationResponse) assert _eq(res.keys(), ['registration_endpoint', 'scopes_supported', 'identifiers_supported', 'token_endpoint', 'flows_supported', 'version', 'userinfo_endpoint', 'authorization_endpoint', 'x509_url', 'issuer']) assert res.version == "3.0" assert _eq(res.flows_supported, ['code', 'token', 'id_token', 'code token', 'code id_token', 'id_token token']) def test_discover(self): c = Consumer(None, None) mfos = MyFakeOICServer("http://*****:*****@example.com" res = c.discover(principal) assert res == "http://*****:*****@example.com" res = c.discover(principal) info = c.provider_config(res) assert isinstance(info, ProviderConfigurationResponse) assert _eq(info.keys(), ['registration_endpoint', 'jwks_uri', 'check_session_endpoint', 'refresh_session_endpoint', 'register_endpoint', 'subject_types_supported', 'token_endpoint_auth_methods_supported', 'id_token_signing_alg_values_supported', 'grant_types_supported', 'user_info_endpoint', 'claims_parameter_supported', 'request_parameter_supported', 'discovery_endpoint', 'issuer', 'authorization_endpoint', 'scopes_supported', 'require_request_uri_registration', 'identifiers_supported', 'token_endpoint', 'request_uri_parameter_supported', 'version', 'response_types_supported', 'end_session_endpoint', 'flows_supported']) assert info["end_session_endpoint"] == "http://example.com/end_session" def test_client_register(self): c = Consumer(None, None) c.application_type = "web" c.application_name = "My super service" c.redirect_uris = ["http://example.com/authz"] c.contact = ["*****@*****.**"] mfos = MyFakeOICServer("http://example.com") mfos.keyjar = SRVKEYS c.http_request = mfos.http_request location = c.discover("*****@*****.**") info = c.provider_config(location) c.register(info["registration_endpoint"]) assert c.client_id is not None assert c.client_secret is not None assert c.registration_expires > utc_time_sans_frac() def _faulty_id_token(self): idval = {'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient'} idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=[SYMKey(key="TestPassword")], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" return ".".join(p) def test_faulty_id_token(self): _faulty_signed_jwt = self._faulty_id_token() with pytest.raises(BadSignature): IdToken().from_jwt(_faulty_signed_jwt, key=[SYMKey(key="TestPassword")]) # What if no verification key is given ? # Should also result in an exception with pytest.raises(MissingSigningKey): _ = IdToken().from_jwt(_faulty_signed_jwt) def test_faulty_id_token_in_access_token_response(self): c = Consumer(None, None) c.keyjar.add_symmetric("", "TestPassword", ["sig"]) _info = {"access_token": "accessTok", "id_token": self._faulty_id_token(), "token_type": "Bearer"} _json = json.dumps(_info) with pytest.raises(BadSignature): c.parse_response(AccessTokenResponse, _json, sformat="json") def test_faulty_idtoken_from_accesstoken_endpoint(self): mfos = MITMServer("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request _state = "state0" self.consumer.consumer_config["response_type"] = ["id_token"] args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], } result = self.consumer.do_authorization_request(state=_state, request_args=args) self.consumer._backup("state0") assert result.status_code == 302 query = urlparse(result.headers["location"]).query with pytest.raises(BadSignature): self.consumer.parse_authz(query=query)
class TestOICConsumer(): @pytest.fixture(autouse=True) def setup_consumer(self, fake_oic_server, session_db_factory): client_id = "client_1" client_config = { "client_id": client_id, "client_authn_method": CLIENT_AUTHN_METHOD, # 'config': {} } self.consumer = Consumer(session_db_factory(SERVER_INFO["issuer"]), CONFIG, client_config, SERVER_INFO) self.consumer.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.consumer.client_secret = "abcdefghijklmnop" self.consumer.keyjar = CLIKEYS self.consumer.redirect_uris = ["https://example.com/cb"] self.consumer.authorization_endpoint = \ "http://example.com/authorization" self.consumer.token_endpoint = "http://example.com/token" self.consumer.userinfo_endpoint = "http://example.com/userinfo" self.consumer.client_secret = "hemlig" self.consumer.secret_type = "basic" mfos = fake_oic_server("http://*****:*****@connect-op.heroku.com" res = c.discover(principal) assert isinstance(res, ProviderConfigurationResponse) assert _eq(res.keys(), [ 'registration_endpoint', 'scopes_supported', 'identifiers_supported', 'token_endpoint', 'flows_supported', 'version', 'userinfo_endpoint', 'authorization_endpoint', 'x509_url', 'issuer' ]) assert res.version == "3.0" assert _eq(res.flows_supported, [ 'code', 'token', 'id_token', 'code token', 'code id_token', 'id_token token' ]) def test_discover(self, fake_oic_server): c = Consumer(None, None) mfos = fake_oic_server("https://*****:*****@example.com" res = c.discover(principal) assert res == "https://*****:*****@example.com" res = c.discover(principal) info = c.provider_config(res) assert isinstance(info, ProviderConfigurationResponse) assert _eq(info.keys(), [ 'registration_endpoint', 'jwks_uri', 'check_session_endpoint', 'refresh_session_endpoint', 'register_endpoint', 'subject_types_supported', 'token_endpoint_auth_methods_supported', 'id_token_signing_alg_values_supported', 'grant_types_supported', 'user_info_endpoint', 'claims_parameter_supported', 'request_parameter_supported', 'discovery_endpoint', 'issuer', 'authorization_endpoint', 'scopes_supported', 'require_request_uri_registration', 'identifiers_supported', 'token_endpoint', 'request_uri_parameter_supported', 'version', 'response_types_supported', 'end_session_endpoint', 'flows_supported' ]) assert info[ "end_session_endpoint"] == "https://example.com/end_session" def test_client_register(self, fake_oic_server): c = Consumer(None, None) c.application_type = "web" c.application_name = "My super service" c.redirect_uris = ["https://example.com/authz"] c.contact = ["*****@*****.**"] mfos = fake_oic_server("https://example.com") mfos.keyjar = SRVKEYS c.http_request = mfos.http_request location = c.discover("*****@*****.**") info = c.provider_config(location) c.register(info["registration_endpoint"]) assert c.client_id is not None assert c.client_secret is not None assert c.registration_expires > utc_time_sans_frac() def _faulty_id_token(self): idval = { 'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'EndUserSubject', 'iss': 'https://alpha.cloud.nds.rub.de', 'exp': 1420823073, 'iat': 1420822473, 'aud': 'TestClient' } idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=[SYMKey(key="TestPassword")], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" return ".".join(p) def test_faulty_id_token(self): _faulty_signed_jwt = self._faulty_id_token() with pytest.raises(BadSignature): IdToken().from_jwt(_faulty_signed_jwt, key=[SYMKey(key="TestPassword")]) # What if no verification key is given ? # Should also result in an exception with pytest.raises(MissingSigningKey): IdToken().from_jwt(_faulty_signed_jwt) def test_faulty_id_token_in_access_token_response(self): c = Consumer(None, None) c.keyjar.add_symmetric("", "TestPassword", ["sig"]) _info = { "access_token": "accessTok", "id_token": self._faulty_id_token(), "token_type": "Bearer" } _json = json.dumps(_info) with pytest.raises(BadSignature): c.parse_response(AccessTokenResponse, _json, sformat="json") def test_faulty_idtoken_from_accesstoken_endpoint(self, mitm_server): mfos = mitm_server("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request _state = "state0" self.consumer.consumer_config["response_type"] = ["id_token"] args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], } result = self.consumer.do_authorization_request(state=_state, request_args=args) self.consumer._backup("state0") assert result.status_code == 302 query = urlparse(result.headers["location"]).query with pytest.raises(BadSignature): self.consumer.parse_authz(query=query)
class TestOICProvider(object): def setup_class(self): self.server = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR) self.cons = Consumer({}, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) self.cons.behaviour = {"request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]} self.cons.debug = True self.cons.keyjar[""] = KC_RSA def test_server_init(self): assert self.server assert self.server.authn_broker == AUTHN_BROKER print self.server.urlmap assert self.server.urlmap["client_1"] == ["https://example.com/authz"] def test_server_authorization_endpoint(self): bib = {"scope": ["openid"], "state": "id-6da9ca0cc23959f5f33e8becd9b08cae", "redirect_uri": "http://*****:*****@example.com"] req["response_types"] = ["code"] print req.to_dict() resp = self.server.registration_endpoint(request=req.to_json()) print resp.message regresp = RegistrationResponse().deserialize(resp.message, "json") print regresp.keys() assert _eq(regresp.keys(), ['redirect_uris', 'contacts', 'application_type', 'client_name', 'registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'client_secret', 'client_id_issued_at', 'response_types']) def test_provider_key_setup(self): provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), None, None, None, None, None, "") provider.baseurl = "http://www.example.com/" provider.key_setup("static", sig={"format": "jwk", "alg": "RSA"}) keys = provider.keyjar.get_signing_key("RSA") assert len(keys) == 1 assert provider.jwks_uri == "http://www.example.com/static/jwks" def _client_id(self, cdb): cid = None for k, item in cdb.items(): if item in cdb.keys(): cid = item break return cid def test_registered_redirect_uri_without_query_component(self): provider = Provider("FOO", {}, {}, None, None, None, None, "") rr = RegistrationRequest(operation="register", redirect_uris=["http://example.org/cb"], response_types=["code"]) registration_req = rr.to_json() provider.registration_endpoint(request=registration_req) correct = [ "http://example.org/cb", "http://example.org/cb/foo", ] faulty = [ "http://example.org/foo", "http://example.com/cb", "http://example.org/cb?got=you", "http://example.org/cb/foo?got=you" ] cid = self._client_id(provider.cdb) for ruri in faulty: areq = AuthorizationRequest(redirect_uri=ruri, client_id=cid, response_type="code", scope="openid") print areq try: provider._verify_redirect_uri(areq) assert False except RedirectURIError: pass for ruri in correct: areq = AuthorizationRequest(redirect_uri=ruri, client_id=cid, response_type="code", scope="openid") print areq try: provider._verify_redirect_uri(areq) except RedirectURIError, err: print err assert False
class TestOICConsumer: @pytest.fixture(autouse=True) def setup_consumer(self, session_db_factory): client_id = "client_1" client_config = { "client_id": client_id, "client_authn_method": CLIENT_AUTHN_METHOD, } self.consumer = Consumer( session_db_factory(SERVER_INFO["issuer"]), CONFIG, client_config, SERVER_INFO, ) self.consumer.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.consumer.keyjar = CLIKEYS self.consumer.redirect_uris = ["https://example.com/cb"] self.consumer.authorization_endpoint = "https://example.com/authorization" self.consumer.token_endpoint = "https://example.com/token" self.consumer.userinfo_endpoint = "https://example.com/userinfo" self.consumer.client_secret = "hemlig" self.consumer.secret_type = "basic" def test_backup_keys(self): keys = self.consumer.__dict__.keys() _dict = self.consumer.dictionary() dkeys = [key for key in keys if key not in _dict.keys()] assert _eq(dkeys, IGNORE) def test_backup_restore(self): authz_org_url = "http://example.org/authorization" _dict = sorted(list(self.consumer.__dict__.items())) self.consumer._backup("sid") self.consumer.restore("sid") assert sorted(_dict) == sorted(list(self.consumer.__dict__.items())) self.consumer.authorization_endpoint = authz_org_url assert _dict != sorted(list(self.consumer.__dict__.items())) self.consumer.restore("sid") assert _dict == sorted(list(self.consumer.__dict__.items())) def test_backup_restore_update(self): authz_org_url = "http://example.org/authorization" self.consumer._backup("sid") self.consumer.authorization_endpoint = authz_org_url self.consumer.token_endpoint = "https://example.org/token" self.consumer.userinfo_endpoint = "" assert self.consumer.authorization_endpoint == authz_org_url assert self.consumer.token_endpoint == "https://example.org/token" assert self.consumer.userinfo_endpoint == "" self.consumer.update("sid") assert self.consumer.authorization_endpoint == authz_org_url assert self.consumer.token_endpoint == "https://example.org/token" assert self.consumer.userinfo_endpoint == "https://example.com/userinfo" def test_begin(self): srv = Server() srv.keyjar = SRVKEYS sid, location = self.consumer.begin("openid", "code") authreq = srv.parse_authorization_request(url=location) assert _eq( list(authreq.keys()), [ "state", "max_age", "claims", "response_type", "client_id", "scope", "redirect_uri", ], ) assert authreq["state"] == sid assert authreq["scope"] == self.consumer.consumer_config["scope"] assert authreq["client_id"] == self.consumer.client_id def test_begin_file(self, tmpdir): path = tmpdir.strpath external_path = "/exported" self.consumer.consumer_config["request_method"] = "file" self.consumer.consumer_config["temp_dir"] = path self.consumer.consumer_config["temp_path"] = external_path self.consumer.consumer_config["authz_page"] = "/authz" srv = Server() srv.keyjar = SRVKEYS sid, location = self.consumer.begin( "openid", "code", path="http://*****:*****@example.com", "nickname": "Ilja", "verified": True, }, ) result = self.consumer.do_authorization_request( state=_state, request_args=args ) parsed = urlparse(result.headers["location"]) self.consumer.parse_response( AuthorizationResponse, info=parsed.query, sformat="urlencoded" ) self.consumer.complete(_state) result = self.consumer.get_user_info(_state) assert isinstance(result, OpenIDSchema) assert _eq(result.keys(), ["name", "email", "verified", "nickname", "sub"]) def test_sign_userinfo(self): _state = "state0" self.consumer.client_prefs = {"userinfo_signed_response_alg": "RS256"} del self.consumer.consumer_config["request_method"] args = { "client_id": self.consumer.client_id, "response_type": "code", "scope": ["openid"], } location = "https://example.com/cb?code=code&state=state0" with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) rsps.add( responses.POST, "https://example.com/token", content_type="application/json", json={ "access_token": "some_token", "token_type": "bearer", "state": "state0", "scope": "openid", }, ) rsps.add( responses.POST, "https://example.com/userinfo", content_type="application/json", json={ "name": "Ilja", "sub": "some_sub", "email": "*****@*****.**", "nickname": "Ilja", "verified": True, }, ) self.consumer.begin("openid", "code") result = self.consumer.do_authorization_request( state=_state, request_args=args ) parsed = urlparse(result.headers["location"]) self.consumer.parse_response( AuthorizationResponse, info=parsed.query, sformat="urlencoded" ) self.consumer.complete(_state) result = self.consumer.get_user_info(_state) assert isinstance(result, OpenIDSchema) assert _eq(result.keys(), ["name", "email", "verified", "nickname", "sub"]) def test_get_userinfo_claims(self): _state = "state0" args = { "client_id": self.consumer.client_id, "response_type": "code", "scope": ["openid"], } location = "https://example.com/cb?code=code&state=state0" with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) rsps.add( responses.POST, "https://example.com/token", content_type="application/json", json={ "access_token": "some_token", "token_type": "bearer", "state": "state0", "scope": "openid", }, ) rsps.add( responses.POST, "https://example.com/userinfo", content_type="application/json", json={ "name": "Ilja", "sub": "some_sub", "email": "*****@*****.**", "nickname": "Ilja", "verified": True, }, ) result = self.consumer.do_authorization_request( state=_state, request_args=args ) parsed = urlparse(result.headers["location"]) self.consumer.parse_response( AuthorizationResponse, info=parsed.query, sformat="urlencoded" ) response = self.consumer.complete(_state) result = self.consumer.get_userinfo_claims( response["access_token"], self.consumer.userinfo_endpoint ) assert isinstance(result, OpenIDSchema) assert _eq(result.keys(), ["name", "email", "verified", "nickname", "sub"]) def real_test_discover(self): c = Consumer(None, None) principal = "*****@*****.**" res = c.discover(principal) assert isinstance(res, ProviderConfigurationResponse) assert _eq( res.keys(), [ "registration_endpoint", "scopes_supported", "identifiers_supported", "token_endpoint", "flows_supported", "version", "userinfo_endpoint", "authorization_endpoint", "x509_url", "issuer", ], ) assert res.version == "3.0" # type: ignore assert _eq( res.flows_supported, # type: ignore [ "code", "token", "id_token", "code token", "code id_token", "id_token token", ], ) def test_discover(self): c = Consumer(None, None) webfinger = { "subject": "acct:[email protected]", "links": [ { "rel": "http://openid.net/specs/connect/1.0/issuer", "href": "https://*****:*****@example.com" with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/.well-known/webfinger" "?resource=acct%3Afoo%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer", json=webfinger, ) res = c.discover(principal) assert res == "https://localhost:8088/" def test_client_register(self): c = Consumer(None, None) c.redirect_uris = ["https://example.com/authz"] reg_resp = { "client_id": "some_client", "client_secret": "super_secret", "client_secret_expires_at": 123456789, "redirect_uris": ["https://example.com/authz"], } with responses.RequestsMock() as rsps: rsps.add(responses.POST, "https://example.com/register/", json=reg_resp) c.register("https://example.com/register/") assert json.loads(rsps.calls[0].request.body) == { "application_type": "web", "response_types": ["code"], "redirect_uris": ["https://example.com/authz"], "grant_types": ["authorization_code"], } assert c.client_id == "some_client" assert c.client_secret == "super_secret" assert c.registration_expires == 123456789 def test_client_register_token(self): c = Consumer(None, None) c.redirect_uris = ["https://example.com/authz"] client_info = { "client_id": "clientid", "redirect_uris": ["https://example.com/authz"], } with responses.RequestsMock() as rsps: rsps.add( rsps.POST, "https://provider.example.com/registration/", json=client_info, ) c.register( "https://provider.example.com/registration/", registration_token="initial_registration_token", ) header = rsps.calls[0].request.headers["Authorization"] assert header == "Bearer aW5pdGlhbF9yZWdpc3RyYXRpb25fdG9rZW4=" def _faulty_id_token(self): idval = { "nonce": "KUEYfRM2VzKDaaKD", "sub": "EndUserSubject", "iss": "https://alpha.cloud.nds.rub.de", "exp": 1420823073, "iat": 1420822473, "aud": "TestClient", } idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=[SYMKey(key="TestPassword")], algorithm="HS256") # Mess with the signed id_token p = _signed_jwt.split(".") p[2] = "aaa" return ".".join(p) def test_faulty_id_token(self): _faulty_signed_jwt = self._faulty_id_token() with pytest.raises(BadSignature): IdToken().from_jwt(_faulty_signed_jwt, key=[SYMKey(key="TestPassword")]) # What if no verification key is given ? # Should also result in an exception with pytest.raises(MissingSigningKey): IdToken().from_jwt(_faulty_signed_jwt) def test_faulty_id_token_in_access_token_response(self): c = Consumer(None, None) c.keyjar.add_symmetric("", "TestPassword", ["sig"]) _info = { "access_token": "accessTok", "id_token": self._faulty_id_token(), "token_type": "Bearer", } _json = json.dumps(_info) with pytest.raises(ValueError): c.parse_response(AccessTokenResponse, _json, sformat="json") def test_faulty_idtoken_from_accesstoken_endpoint(self, mitm_server): mfos = mitm_server("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request _state = "state0" self.consumer.consumer_config["response_type"] = ["id_token"] args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], } result = self.consumer.do_authorization_request(state=_state, request_args=args) self.consumer._backup("state0") assert result.status_code == 302 query = urlparse(result.headers["location"]).query with pytest.raises(BadSignature): self.consumer.parse_authz(query=query)
class TestOICConsumer(): def setup_class(self): self.consumer = Consumer(SessionDB(SERVER_INFO["issuer"]), CONFIG, CLIENT_CONFIG, SERVER_INFO) self.consumer.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.consumer.client_secret = CLIENT_SECRET def test_init(self): assert self.consumer def test_backup_keys(self): keys = self.consumer.__dict__.keys() print keys _dict = self.consumer.dictionary() print _dict.keys() dkeys = [key for key in keys if key not in _dict.keys()] print dkeys assert _eq(dkeys, IGNORE) def test_backup_restore(self): _dict = self.consumer.__dict__.items() self.consumer._backup("sid") self.consumer.restore("sid") assert _dict == self.consumer.__dict__.items() self.consumer.authorization_endpoint = AUTHZ_URL assert _dict != self.consumer.__dict__.items() self.consumer.restore("sid") assert _dict == self.consumer.__dict__.items() def test_backup_restore_update(self): self.consumer.authorization_endpoint = AUTHZ_URL self.consumer.token_endpoint = "http://example.com/token" self.consumer.userinfo_endpoint = "http://example.com/userinfo" self.consumer._backup("sid") self.consumer.authorization_endpoint = AUTHZ_ORG_URL self.consumer.token_endpoint = "http://example.org/token" self.consumer.userinfo_endpoint = "" assert self.consumer.authorization_endpoint == AUTHZ_ORG_URL assert self.consumer.token_endpoint == "http://example.org/token" assert self.consumer.userinfo_endpoint == "" self.consumer.update("sid") assert self.consumer.authorization_endpoint == AUTHZ_ORG_URL assert self.consumer.token_endpoint == "http://example.org/token" assert self.consumer.userinfo_endpoint == "http://example.com/userinfo" def test_begin(self): self.consumer.authorization_endpoint = AUTHZ_URL self.consumer.keyjar[""].append(KC_RSA) # self.consumer.keyjar.set_sign_key(rsapub, "rsa") #self.consumer.keyjar.set_verify_key(rsapub, "rsa") srv = Server() srv.keyjar = SRVKEYS print "redirect_uris", self.consumer.redirect_uris print "config", self.consumer.config sid, location = self.consumer.begin("openid", "code") print location authreq = srv.parse_authorization_request(url=location) print authreq.keys() assert _eq(authreq.keys(), [ 'request', 'state', 'max_age', 'claims', 'response_type', 'client_id', 'scope', 'redirect_uri' ]) assert authreq["state"] == sid assert authreq["scope"] == self.consumer.config["scope"] assert authreq["client_id"] == self.consumer.client_id def test_begin_file(self): tempdir = tempfile.mkdtemp() self.consumer.config["request_method"] = "file" self.consumer.config["temp_dir"] = tempdir self.consumer.config["temp_path"] = tempdir self.consumer.config["authz_page"] = "/authz" srv = Server() srv.keyjar = SRVKEYS sid, location = self.consumer.begin("openid", "code", path="http://localhost:8087") print location # vkeys = {".":srv.keyjar.get_verify_key()} authreq = srv.parse_authorization_request(url=location) print authreq.keys() assert _eq(authreq.keys(), [ 'max_age', 'state', 'redirect_uri', 'response_type', 'client_id', 'scope', 'claims', 'request_uri' ]) assert authreq["state"] == sid assert authreq["scope"] == self.consumer.config["scope"] assert authreq["client_id"] == self.consumer.client_id assert authreq["redirect_uri"].startswith( "http://localhost:8087/authz") # Cleanup the file we have created shutil.rmtree(tempdir) def test_complete(self): mfos = MyFakeOICServer("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request _state = "state0" self.consumer.nonce = rndstr() self.consumer.redirect_uris = ["https://example.com/cb"] args = { "client_id": self.consumer.client_id, "response_type": "code", "scope": ["openid"], } result = self.consumer.do_authorization_request(state=_state, request_args=args) assert result.status_code == 302 print "redirect_uris", self.consumer.redirect_uris print result.headers["location"] assert result.headers["location"].startswith( self.consumer.redirect_uris[0]) _, query = result.headers["location"].split("?") # vkeys = {".": self.consumer.keyjar.get_verify_key()} self.consumer.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") resp = self.consumer.complete(_state) print resp assert resp.type() == "AccessTokenResponse" print resp.keys() assert _eq(resp.keys(), [ 'token_type', 'state', 'access_token', 'scope', 'expires_in', 'refresh_token' ]) assert resp["state"] == _state def test_parse_authz(self): mfos = MyFakeOICServer("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request _state = "state0" self.consumer.nonce = rndstr() args = { "client_id": self.consumer.client_id, "response_type": "code", "scope": ["openid"], } result = self.consumer.do_authorization_request(state=_state, request_args=args) print self.consumer.sdb["state0"].keys() part = self.consumer.parse_authz(query=result.headers["location"]) print part atr = part[0] assert part[1] is None assert part[2] is None assert atr.type() == "AuthorizationResponse" assert atr["state"] == _state assert "code" in atr def test_parse_authz_implicit(self): mfos = MyFakeOICServer("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request self.consumer.config["response_type"] = ["token"] _state = "statxxx" args = { "client_id": self.consumer.client_id, "response_type": "implicit", "scope": ["openid"], "redirect_uri": "http://localhost:8088/cb" } result = self.consumer.do_authorization_request(state=_state, request_args=args) part = self.consumer.parse_authz(query=result.headers["location"]) print part assert part[0] is None atr = part[1] assert part[2] is None assert atr.type() == "AccessTokenResponse" assert atr["state"] == _state assert "access_token" in atr
class TestProvider(object): @pytest.fixture(autouse=True) def create_provider(self, session_db_factory): self.provider = Provider(SERVER_INFO["issuer"], session_db_factory(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR) self.provider.baseurl = self.provider.name self.cons = Consumer( {}, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) self.cons.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.cons.keyjar[""] = KC_RSA def test_providerinfo(self): self.provider.baseurl = 'http://example.com/path1/path2' resp = self.provider.create_providerinfo() assert resp.to_dict( )['authorization_endpoint'] == 'http://example.com/path1/path2/authorization' def test_providerinfo_trailing(self): self.provider.baseurl = 'http://example.com/path1/path2/' resp = self.provider.create_providerinfo() assert resp.to_dict( )['authorization_endpoint'] == 'http://example.com/path1/path2/authorization' def test_authorization_endpoint(self): bib = { "scope": ["openid"], "state": "id-6da9ca0cc23959f5f33e8becd9b08cae", "redirect_uri": "http://*****:*****@patch('oic.oic.provider.utc_time_sans_frac', Mock(return_value=123456)) def test_client_secret_expiration_time(self): exp_time = self.provider.client_secret_expiration_time() assert exp_time == 209856 def test_registration_endpoint(self): req = RegistrationRequest() req["application_type"] = "web" req["client_name"] = "My super service" req["redirect_uris"] = ["http://example.com/authz"] req["contacts"] = ["*****@*****.**"] req["response_types"] = ["code"] resp = self.provider.registration_endpoint(request=req.to_json()) regresp = RegistrationResponse().deserialize(resp.message, "json") assert _eq(regresp.keys(), [ 'redirect_uris', 'contacts', 'application_type', 'client_name', 'registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'client_secret', 'client_id_issued_at', 'response_types' ]) def test_registration_endpoint_unicode(self): data = 'application_type=web&client_name=M%C3%A1+supe%C5%99+service&' \ 'redirect_uris=http%3A%2F%2Fexample.com%2Fauthz&response_types=code' resp = self.provider.registration_endpoint(request=data) regresp = RegistrationResponse().deserialize(resp.message, "json") assert _eq(regresp.keys(), [ 'redirect_uris', 'application_type', 'client_name', 'registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'client_secret', 'client_id_issued_at', 'response_types' ]) def test_registration_endpoint_with_non_https_redirect_uri_implicit_flow( self): params = { "application_type": "web", "redirect_uris": ["http://example.com/authz"], "response_types": ["id_token", "token"] } req = RegistrationRequest(**params) resp = self.provider.registration_endpoint(request=req.to_json()) assert resp.status == "400 Bad Request" error = json.loads(resp.message) assert error["error"] == "invalid_redirect_uri" def test_verify_redirect_uris_with_https_code_flow(self): params = { "application_type": "web", "redirect_uris": ["http://example.com/authz"], "response_types": ["code"] } request = RegistrationRequest(**params) verified_uris = self.provider.verify_redirect_uris(request) assert verified_uris == [("http://example.com/authz", None)] def test_verify_redirect_uris_with_non_https_redirect_uri_implicit_flow( self): params = { "application_type": "web", "redirect_uris": ["http://example.com/authz"], "response_types": ["id_token", "token"] } request = RegistrationRequest(**params) with pytest.raises(InvalidRedirectURIError) as exc_info: self.provider.verify_redirect_uris(request) assert str(exc_info.value) == "None https redirect_uri not allowed" def test_provider_key_setup(self, tmpdir, session_db_factory): path = tmpdir.strpath provider = Provider("pyoicserv", session_db_factory(SERVER_INFO["issuer"]), None, None, None, None, None, None) provider.baseurl = "http://www.example.com" provider.key_setup(path, path, sig={"format": "jwk", "alg": "RSA"}) keys = provider.keyjar.get_signing_key("RSA") assert len(keys) == 1 assert provider.jwks_uri == "http://www.example.com/{}/jwks".format( path) @pytest.mark.parametrize("uri", [ "http://example.org/foo", "http://example.com/cb", "http://example.org/cb?got=you", "http://example.org/cb/foo?got=you" ]) def test_verify_redirect_uri_faulty_without_query(self, uri): rr = RegistrationRequest(operation="register", redirect_uris=["http://example.org/cb"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest(redirect_uri=uri, client_id=cid, response_type="code", scope="openid") with pytest.raises(RedirectURIError): self.provider._verify_redirect_uri(areq) @pytest.mark.parametrize("uri", [ "http://example.org/cb", ]) def test_verify_redirect_uri_correct_without_query(self, uri): rr = RegistrationRequest(operation="register", redirect_uris=["http://example.org/cb"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest(redirect_uri=uri, client_id=cid, response_type="code", scope="openid") self.provider._verify_redirect_uri(areq) def test_verify_sector_identifier_nonreachable(self): rr = RegistrationRequest(operation="register", sector_identifier_uri="https://example.com") with responses.RequestsMock() as rsps, LogCapture( level=logging.DEBUG) as logcap: rsps.add(rsps.GET, "https://example.com", status=404) message = "Couldn't open sector_identifier_uri" with pytest.raises(InvalidSectorIdentifier, message=message): self.provider._verify_sector_identifier(rr) assert len(logcap.records) == 0 def test_verify_sector_identifier_error(self): rr = RegistrationRequest(operation="register", sector_identifier_uri="https://example.com") error = ConnectionError('broken connection') with responses.RequestsMock() as rsps, LogCapture( level=logging.DEBUG) as logcap: rsps.add(rsps.GET, "https://example.com", body=error) with pytest.raises(InvalidSectorIdentifier, message="Couldn't open sector_identifier_uri"): self.provider._verify_sector_identifier(rr) assert len(logcap.records) == 2 # First log record is from server... assert logcap.records[1].msg == error def test_verify_sector_identifier_malformed(self): rr = RegistrationRequest(operation="register", sector_identifier_uri="https://example.com") body = "This is not the JSON you are looking for" with responses.RequestsMock() as rsps, LogCapture( level=logging.DEBUG) as logcap: rsps.add(rsps.GET, "https://example.com", body=body) with pytest.raises( InvalidSectorIdentifier, message="Error deserializing sector_identifier_uri content" ): self.provider._verify_sector_identifier(rr) assert len(logcap.records) == 1 assert logcap.records[0].msg == "sector_identifier_uri => %s" assert logcap.records[0].args == (body, ) def test_verify_sector_identifier_ru_missing_in_si(self): """Redirect_uris is not present in the sector_identifier_uri content.""" rr = RegistrationRequest(operation="register", sector_identifier_uri="https://example.com", redirect_uris=["http://example.com/missing"]) with responses.RequestsMock() as rsps, LogCapture( level=logging.DEBUG) as logcap: rsps.add(rsps.GET, "https://example.com", body=json.dumps(["http://example.com/present"])) with pytest.raises( InvalidSectorIdentifier, message="redirect uri missing from sector_identifiers"): self.provider._verify_sector_identifier(rr) assert len(logcap.records) == 2 assert logcap.records[0].msg == "sector_identifier_uri => %s" assert logcap.records[0].args == ('["http://example.com/present"]', ) assert logcap.records[1].msg == "redirect_uris: %s" assert logcap.records[1].args == (["http://example.com/missing"], ) def test_verify_sector_identifier_ru_missing(self): """Redirect_uris is not present in the request.""" rr = RegistrationRequest(operation="register", sector_identifier_uri="https://example.com") redirects = ["http://example.com/present"] with responses.RequestsMock() as rsps, LogCapture( level=logging.DEBUG) as logcap: rsps.add(rsps.GET, "https://example.com", body=json.dumps(redirects)) si_redirects, si_url = self.provider._verify_sector_identifier(rr) assert si_url == "https://example.com" assert si_redirects == redirects assert len(logcap.records) == 1 assert logcap.records[0].msg == "sector_identifier_uri => %s" assert logcap.records[0].args == ('["http://example.com/present"]', ) def test_verify_sector_identifier_ru_ok(self): """Redirect_uris is present in the sector_identifier_uri content.""" rr = RegistrationRequest(operation="register", sector_identifier_uri="https://example.com", redirect_uris=["http://example.com/present"]) redirects = ["http://example.com/present"] with responses.RequestsMock() as rsps, LogCapture( level=logging.DEBUG) as logcap: rsps.add(rsps.GET, "https://example.com", body=json.dumps(redirects)) si_redirects, si_url = self.provider._verify_sector_identifier(rr) assert si_url == "https://example.com" assert si_redirects == redirects assert len(logcap.records) == 2 assert logcap.records[0].msg == "sector_identifier_uri => %s" assert logcap.records[0].args == ('["http://example.com/present"]', ) assert logcap.records[1].msg == "redirect_uris: %s" assert logcap.records[1].args == (["http://example.com/present"], ) @pytest.mark.parametrize("uri", [ "http://example.org/cb", "http://example.org/cb?got=you", "http://example.org/cb?foo=you" "http://example.org/cb?foo=bar&got=you", "http://example.org/cb?foo=you&foo=bar" ]) def test_registered_redirect_uri_faulty_with_query_component(self, uri): rr = RegistrationRequest( operation="register", redirect_uris=["http://example.org/cb?foo=bar"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest(redirect_uri=uri, client_id=cid, scope="openid", response_type="code") with pytest.raises(RedirectURIError): self.provider._verify_redirect_uri(areq) def test_registered_redirect_uri_correct_with_query_component(self): rr = RegistrationRequest( operation="register", redirect_uris=["http://example.org/cb?foo=bar"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest( redirect_uri="http://example.org/cb?foo=bar", client_id=cid, scope="openid", response_type="code") self.provider._verify_redirect_uri(areq) def test_verify_redirect_uri_native_http_localhost(self): areq = RegistrationRequest(redirect_uris=["http://localhost/cb"], application_type='native') self.provider.verify_redirect_uris(areq) def test_verify_redirect_uri_native_loopback(self): areq = RegistrationRequest(redirect_uris=["http://127.0.0.1/cb"], application_type='native') self.provider.verify_redirect_uris(areq) def test_verify_redirect_uri_native_http_non_localhost(self): areq = RegistrationRequest(redirect_uris=["http://example.org/cb"], application_type='native') try: self.provider.verify_redirect_uris(areq) except InvalidRedirectURIError: assert True def test_verify_redirect_uri_native_custom(self): areq = RegistrationRequest( redirect_uris=["com.example.app:/oauth2redirect"], application_type='native') self.provider.verify_redirect_uris(areq) def test_verify_redirect_uri_native_https(self): areq = RegistrationRequest(redirect_uris=["https://example.org/cb"], application_type='native') try: self.provider.verify_redirect_uris(areq) except InvalidRedirectURIError: assert True def test_read_registration(self): rr = RegistrationRequest(operation="register", redirect_uris=["http://example.org/new"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) authn = ' '.join(['Bearer', regresp['registration_access_token']]) query = '='.join(['client_id', regresp['client_id']]) resp = self.provider.read_registration(authn, query) assert json.loads(resp.message) == regresp.to_dict() def test_read_registration_wrong_authn(self): resp = self.provider.read_registration('wrong string', 'request') assert resp.status == '400 Bad Request' assert json.loads(resp.message) == { 'error': 'invalid_request', 'error_description': None } def test_key_rollover(self): provider2 = Provider("FOOP", {}, {}, None, None, None, None, None) provider2.keyjar = KEYJAR # Number of KeyBundles assert len(provider2.keyjar.issuer_keys[""]) == 1 kb = ec_init({"type": "EC", "crv": "P-256", "use": ["sig"]}) provider2.do_key_rollover(json.loads(kb.jwks()), "b%d") assert len(provider2.keyjar.issuer_keys[""]) == 2 kb = ec_init({"type": "EC", "crv": "P-256", "use": ["sig"]}) provider2.do_key_rollover(json.loads(kb.jwks()), "b%d") assert len(provider2.keyjar.issuer_keys[""]) == 3 provider2.remove_inactive_keys(-1) assert len(provider2.keyjar.issuer_keys[""]) == 2 def test_endsession_endpoint(self): resp = self.provider.endsession_endpoint("") self._assert_cookies_expired(resp.headers) # End session not allowed if no cookie is sent (can't determine session) resp = self.provider.endsession_endpoint("", cookie="FAIL") assert resp.status == "400 Bad Request" def test_endsession_endpoint_with_id_token_hint(self): id_token = self._auth_with_id_token() assert self.provider.sdb.get_sids_by_sub( id_token["sub"]) # verify we got valid session id_token_hint = id_token.to_jwt(algorithm="none") resp = self.provider.endsession_endpoint( urlencode({"id_token_hint": id_token_hint})) assert not self.provider.sdb.get_sids_by_sub( id_token["sub"]) # verify session has been removed self._assert_cookies_expired(resp.headers) def test_endsession_endpoint_with_post_logout_redirect_uri(self): id_token = self._auth_with_id_token() assert self.provider.sdb.get_sids_by_sub( id_token["sub"]) # verify we got valid session post_logout_redirect_uri = \ CDB[CLIENT_CONFIG["client_id"]]["post_logout_redirect_uris"][0][0] resp = self.provider.endsession_endpoint( urlencode({"post_logout_redirect_uri": post_logout_redirect_uri})) assert isinstance(resp, SeeOther) assert not self.provider.sdb.get_sids_by_sub( id_token["sub"]) # verify session has been removed self._assert_cookies_expired(resp.headers) def test_session_state_in_auth_req_for_session_support( self, session_db_factory): provider = Provider(SERVER_INFO["issuer"], session_db_factory(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR) provider.capabilities.update( {"check_session_iframe": "https://op.example.com/check_session"}) req_args = { "scope": ["openid"], "redirect_uri": "http://localhost:8087/authz", "response_type": ["code"], "client_id": "number5" } areq = AuthorizationRequest(**req_args) resp = provider.authorization_endpoint(request=areq.to_urlencoded()) aresp = self.cons.parse_response(AuthorizationResponse, resp.message, sformat="urlencoded") assert "session_state" in aresp def _assert_cookies_expired(self, http_headers): cookies_string = ";".join( [c[1] for c in http_headers if c[0] == "Set-Cookie"]) all_cookies = SimpleCookie() try: cookies_string = cookies_string.decode() except (AttributeError, UnicodeDecodeError): pass all_cookies.load(cookies_string) now = datetime.datetime.utcnow() # for c in [ self.provider.cookie_name, self.provider.session_cookie_name ]: dt = datetime.datetime.strptime(all_cookies[c]["expires"], "%a, %d-%b-%Y %H:%M:%S GMT") assert dt < now # make sure the cookies have expired to be cleared def _auth_with_id_token(self): state, location = self.cons.begin("openid", "id_token", path="http://localhost:8087") resp = self.provider.authorization_endpoint( request=location.split("?")[1]) aresp = self.cons.parse_response(AuthorizationResponse, resp.message, sformat="urlencoded") return aresp["id_token"] def test_id_token_RS512_sign(self): self.provider.capabilities['id_token_signing_alg_values_supported'] = [ 'RS512' ] self.provider.build_jwx_def() id_token = self._auth_with_id_token() assert id_token.jws_header['alg'] == "RS512" def test_refresh_access_token_request(self): authreq = AuthorizationRequest(state="state", redirect_uri="http://example.com/authz", client_id=CLIENT_ID, response_type="code", scope=["openid", 'offline_access'], prompt='consent') _sdb = self.provider.sdb sid = _sdb.access_token.key(user="******", areq=authreq) access_grant = _sdb.access_token(sid=sid) ae = AuthnEvent("user", "salt") _sdb[sid] = { "oauth_state": "authz", "authn_event": ae.to_json(), "authzreq": authreq.to_json(), "client_id": CLIENT_ID, "code": access_grant, "code_used": False, "scope": ["openid", 'offline_access'], "redirect_uri": "http://example.com/authz", } _sdb.do_sub(sid, "client_salt") # Construct Access token request areq = AccessTokenRequest(code=access_grant, client_id=CLIENT_ID, redirect_uri="http://example.com/authz", client_secret=CLIENT_SECRET, grant_type='authorization_code') txt = areq.to_urlencoded() resp = self.provider.token_endpoint(request=txt) atr = AccessTokenResponse().deserialize(resp.message, "json") rareq = RefreshAccessTokenRequest(grant_type="refresh_token", refresh_token=atr['refresh_token'], client_id=CLIENT_ID, client_secret=CLIENT_SECRET, scope=['openid']) resp = self.provider.token_endpoint(request=rareq.to_urlencoded()) atr2 = AccessTokenResponse().deserialize(resp.message, "json") assert atr2['access_token'] != atr['access_token'] assert atr2['refresh_token'] == atr['refresh_token'] assert atr2['token_type'] == 'Bearer'
class TestOICConsumerLogout: @pytest.fixture(autouse=True) def setup_consumer(self, session_db_factory): client_config = { "client_id": CLIENT_ID, "client_authn_method": CLIENT_AUTHN_METHOD, } self.consumer = Consumer(DictSessionBackend(), CONFIG, client_config, SERVER_INFO) self.consumer.keyjar = CLIKEYS self.consumer.redirect_uris = ["https://example.com/authz"] self.consumer.client_secret = "hemlig" self.consumer.secret_type = "basic" self.consumer.issuer = ISSUER_ID self.provider = Provider( ISSUER_ID, session_db_factory(ISSUER_ID), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=SRVKEYS, ) self.provider.baseurl = self.provider.name def test_logout_with_sub(self): # Simulate an authorization sid, request_location = self.consumer.begin("openid", "code", path="https://example.com") resp = self.provider.authorization_endpoint(request=request_location) part = self.consumer.parse_authz(resp.message) assert isinstance(part, tuple) aresp = part[0] assert aresp assert self.consumer.sdb[sid]["issuer"] == self.provider.baseurl # Simulate an accesstoken request areq = AccessTokenRequest( code=aresp["code"], client_id=CLIENT_ID, redirect_uri="http://example.com/authz", client_secret=self.consumer.client_secret, grant_type="authorization_code", ) token_resp = self.provider.code_grant_type(areq) tresp = self.consumer.parse_response(AccessTokenResponse, token_resp.message, sformat="json") # Now, for the backchannel logout. This happens on the OP logout_info = { "sub": tresp["id_token"]["sub"], "events": { BACK_CHANNEL_LOGOUT_EVENT: {} }, } alg = "RS256" _jws = JWT( self.provider.keyjar, iss=self.provider.baseurl, lifetime=86400, sign_alg=alg, ) logout_token = _jws.pack(aud=CLIENT_ID, **logout_info) # The logout request that gets sent to the RP request = BackChannelLogoutRequest(logout_token=logout_token) # The RP evaluates the request. If everything is OK a session ID (== original state # value) is returned. _sid = self.consumer.backchannel_logout(request_args=request.to_dict()) assert _sid == sid # Test other coding _sid = self.consumer.backchannel_logout( request=request.to_urlencoded()) assert _sid == sid def test_not_for_me(self): _sub = "sub" logout_info = {"sub": _sub, "events": {BACK_CHANNEL_LOGOUT_EVENT: {}}} alg = "RS256" _jws = JWT( self.provider.keyjar, iss=self.provider.baseurl, lifetime=86400, sign_alg=alg, ) logout_token = _jws.pack(aud="someone", **logout_info) # The logout request that gets sent to the RP request = BackChannelLogoutRequest(logout_token=logout_token) with pytest.raises(MessageException): self.consumer.backchannel_logout(request_args=request.to_dict()) def test_logout_without_sub(self): # Simulate an authorization sid, request_location = self.consumer.begin("openid", "code", path="https://example.com") resp = self.provider.authorization_endpoint(request=request_location) part = self.consumer.parse_authz(resp.message) assert isinstance(part, tuple) aresp = part[0] assert aresp assert self.consumer.sdb[sid]["issuer"] == self.provider.baseurl # Simulate an accesstoken request areq = AccessTokenRequest( code=aresp["code"], client_id=CLIENT_ID, redirect_uri="http://example.com/authz", client_secret=self.consumer.client_secret, grant_type="authorization_code", ) token_resp = self.provider.code_grant_type(areq) self.consumer.parse_response(AccessTokenResponse, token_resp.message, sformat="json") # Have to fake this until the provider changes are in place _smid = "session_management_id" self.consumer.sso_db.update(sid, "smid", _smid) # Now, for the backchannel logout. This happens on the OP logout_info = {"sid": _smid, "events": {BACK_CHANNEL_LOGOUT_EVENT: {}}} alg = "RS256" _jws = JWT( self.provider.keyjar, iss=self.provider.baseurl, lifetime=86400, sign_alg=alg, ) logout_token = _jws.pack(aud=CLIENT_ID, **logout_info) # The logout request that gets sent to the RP request = BackChannelLogoutRequest(logout_token=logout_token) # The RP evaluates the request. If everything is OK a session ID (== original state # value) is returned. _sid = self.consumer.backchannel_logout(request_args=request.to_dict()) assert _sid == [sid] def test_logout_with_none(self): # Now for the backchannel logout. This happens on the OP logout_info = LogoutToken(events={BACK_CHANNEL_LOGOUT_EVENT: {}}) alg = "RS256" _jws = JWT( self.provider.keyjar, iss=self.provider.baseurl, lifetime=86400, sign_alg=alg, ) logout_token = _jws.pack(aud=CLIENT_ID, **logout_info) # The logout request that gets sent to the RP request = BackChannelLogoutRequest(logout_token=logout_token) # The RP evaluates the request. If everything is OK a session ID (== original state # value) is returned. with pytest.raises(MessageException): self.consumer.backchannel_logout(request_args=request.to_dict()) def test_sso_db_dict(self): client_config = { "client_id": CLIENT_ID, "client_authn_method": CLIENT_AUTHN_METHOD, } _consumer = Consumer({}, CONFIG, client_config, SERVER_INFO, sso_db={}) _consumer.keyjar = CLIKEYS _consumer.redirect_uris = ["https://example.com/authz"] _consumer.client_secret = "hemlig" _consumer.secret_type = "basic" _consumer.issuer = ISSUER_ID # Simulate an authorization sid, request_location = _consumer.begin("openid", "code", path="https://example.com") resp = self.provider.authorization_endpoint(request=request_location) part = _consumer.parse_authz(resp.message) assert isinstance(part, tuple) aresp = part[0] assert aresp assert _consumer.sdb[sid]["issuer"] == self.provider.baseurl # Simulate an accesstoken request areq = AccessTokenRequest( code=aresp["code"], client_id=CLIENT_ID, redirect_uri="http://example.com/authz", client_secret=_consumer.client_secret, grant_type="authorization_code", ) token_resp = self.provider.code_grant_type(areq) tresp = _consumer.parse_response(AccessTokenResponse, token_resp.message, sformat="json") # Now, for the backchannel logout. This happens on the OP logout_info = { "sub": tresp["id_token"]["sub"], "events": { BACK_CHANNEL_LOGOUT_EVENT: {} }, } alg = "RS256" _jws = JWT( self.provider.keyjar, iss=self.provider.baseurl, lifetime=86400, sign_alg=alg, ) logout_token = _jws.pack(aud=CLIENT_ID, **logout_info) # The logout request that gets sent to the RP request = BackChannelLogoutRequest(logout_token=logout_token) # The RP evaluates the request. If everything is OK a session ID (== original state # value) is returned. _sid = _consumer.backchannel_logout(request_args=request.to_dict()) assert _sid == sid def test_attribute_error(self): self.consumer.sdb.update("sid", "foo", "bar") self.consumer.update("sid") with pytest.raises(AttributeError): getattr(self.consumer, "foo")
class TestOICConsumer(): def setup_class(self): self.consumer = Consumer(SessionDB(), CONFIG, CLIENT_CONFIG, SERVER_INFO) self.consumer.client_secret = CLIENT_SECRET def test_init(self): assert self.consumer def test_backup_keys(self): keys = self.consumer.__dict__.keys() print keys _dict = self.consumer.dictionary() print _dict.keys() dkeys = [key for key in keys if key not in _dict.keys()] print dkeys assert _eq(dkeys, IGNORE) def test_backup_restore(self): _dict = self.consumer.__dict__.items() self.consumer._backup("sid") self.consumer.restore("sid") assert _dict == self.consumer.__dict__.items() self.consumer.authorization_endpoint = AUTHZ_URL assert _dict != self.consumer.__dict__.items() self.consumer.restore("sid") assert _dict == self.consumer.__dict__.items() def test_backup_restore_update(self): self.consumer.authorization_endpoint = AUTHZ_URL self.consumer.token_endpoint = "http://example.com/token" self.consumer.userinfo_endpoint = "http://example.com/userinfo" self.consumer._backup("sid") self.consumer.authorization_endpoint = AUTHZ_ORG_URL self.consumer.token_endpoint = "http://example.org/token" self.consumer.userinfo_endpoint = "" assert self.consumer.authorization_endpoint == AUTHZ_ORG_URL assert self.consumer.token_endpoint == "http://example.org/token" assert self.consumer.userinfo_endpoint == "" self.consumer.update("sid") assert self.consumer.authorization_endpoint == AUTHZ_ORG_URL assert self.consumer.token_endpoint == "http://example.org/token" assert self.consumer.userinfo_endpoint == "http://example.com/userinfo" def test_begin(self): self.consumer.authorization_endpoint = AUTHZ_URL self.consumer.keyjar[""].append(KC_RSA) #self.consumer.keyjar.set_sign_key(rsapub, "rsa") #self.consumer.keyjar.set_verify_key(rsapub, "rsa") srv = Server() srv.keyjar = SRVKEYS print "redirect_uris", self.consumer.redirect_uris print "config", self.consumer.config location = self.consumer.begin("openid", "code") print location authreq = srv.parse_authorization_request(url=location) print authreq.keys() assert _eq(authreq.keys(), ['request', 'state', 'max_age', 'claims', 'response_type', 'client_id', 'scope', 'redirect_uri']) assert authreq["state"] == self.consumer.state assert authreq["scope"] == self.consumer.config["scope"] assert authreq["client_id"] == self.consumer.client_id def test_begin_file(self): self.consumer.config["request_method"] = "file" self.consumer.config["temp_dir"] = "./file" self.consumer.config["temp_path"] = "/tmp/" self.consumer.config["authz_page"] = "/authz" srv = Server() srv.keyjar = SRVKEYS location = self.consumer.begin("openid", "code", path="http://localhost:8087") print location #vkeys = {".":srv.keyjar.get_verify_key()} authreq = srv.parse_authorization_request(url=location) print authreq.keys() assert _eq(authreq.keys(), ['max_age', 'state', 'redirect_uri', 'response_type', 'client_id', 'scope', 'claims', 'request_uri']) assert authreq["state"] == self.consumer.state assert authreq["scope"] == self.consumer.config["scope"] assert authreq["client_id"] == self.consumer.client_id assert authreq["redirect_uri"].startswith("http://localhost:8087/authz") def test_complete(self): mfos = MyFakeOICServer("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request self.consumer.state = "state0" self.consumer.nonce = rndstr() self.consumer.redirect_uris = ["https://example.com/cb"] args = { "client_id": self.consumer.client_id, "response_type": "code", "scope": ["openid"], } result = self.consumer.do_authorization_request( state=self.consumer.state, request_args=args) assert result.status_code == 302 print "redirect_uris", self.consumer.redirect_uris print result.headers["location"] assert result.headers["location"].startswith( self.consumer.redirect_uris[0]) _, query = result.headers["location"].split("?") #vkeys = {".": self.consumer.keyjar.get_verify_key()} self.consumer.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") resp = self.consumer.complete() print resp assert resp.type() == "AccessTokenResponse" print resp.keys() assert _eq(resp.keys(), ['token_type', 'state', 'access_token', 'scope', 'expires_in', 'refresh_token']) assert resp["state"] == self.consumer.state def test_parse_authz(self): mfos = MyFakeOICServer("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request self.consumer.state = "state0" self.consumer.nonce = rndstr() args = { "client_id": self.consumer.client_id, "response_type": "code", "scope": ["openid"], } result = self.consumer.do_authorization_request( state=self.consumer.state, request_args=args) print self.consumer.sdb.keys() print self.consumer.sdb["state0"].keys() part = self.consumer.parse_authz(query=result.headers["location"]) print part atr = part[0] assert part[1] is None assert part[2] is None assert atr.type() == "AuthorizationResponse" assert atr["state"] == "state0" assert "code" in atr def test_parse_authz_implicit(self): self.consumer.config["response_type"] = "implicit" args = { "client_id": self.consumer.client_id, "response_type": "implicit", "scope": ["openid"], } result = self.consumer.do_authorization_request( state=self.consumer.state, request_args=args) part = self.consumer.parse_authz(query=result.headers["location"]) print part assert part[0] is None atr = part[1] assert part[2] is None assert atr.type() == "AccessTokenResponse" assert atr["state"] == "state0" assert "access_token" in atr
def test_sso_db_dict(self): client_config = { "client_id": CLIENT_ID, "client_authn_method": CLIENT_AUTHN_METHOD, } _consumer = Consumer({}, CONFIG, client_config, SERVER_INFO, sso_db={}) _consumer.keyjar = CLIKEYS _consumer.redirect_uris = ["https://example.com/authz"] _consumer.client_secret = "hemlig" _consumer.secret_type = "basic" _consumer.issuer = ISSUER_ID # Simulate an authorization sid, request_location = _consumer.begin("openid", "code", path="https://example.com") resp = self.provider.authorization_endpoint(request=request_location) part = _consumer.parse_authz(resp.message) assert isinstance(part, tuple) aresp = part[0] assert aresp assert _consumer.sdb[sid]["issuer"] == self.provider.baseurl # Simulate an accesstoken request areq = AccessTokenRequest( code=aresp["code"], client_id=CLIENT_ID, redirect_uri="http://example.com/authz", client_secret=_consumer.client_secret, grant_type="authorization_code", ) token_resp = self.provider.code_grant_type(areq) tresp = _consumer.parse_response(AccessTokenResponse, token_resp.message, sformat="json") # Now, for the backchannel logout. This happens on the OP logout_info = { "sub": tresp["id_token"]["sub"], "events": { BACK_CHANNEL_LOGOUT_EVENT: {} }, } alg = "RS256" _jws = JWT( self.provider.keyjar, iss=self.provider.baseurl, lifetime=86400, sign_alg=alg, ) logout_token = _jws.pack(aud=CLIENT_ID, **logout_info) # The logout request that gets sent to the RP request = BackChannelLogoutRequest(logout_token=logout_token) # The RP evaluates the request. If everything is OK a session ID (== original state # value) is returned. _sid = _consumer.backchannel_logout(request_args=request.to_dict()) assert _sid == sid
class TestProvider(object): @pytest.fixture(autouse=True) def create_provider(self): self.provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR) self.provider.baseurl = self.provider.name self.cons = Consumer({}, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) self.cons.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]} self.cons.keyjar[""] = KC_RSA def test_authorization_endpoint(self): bib = {"scope": ["openid"], "state": "id-6da9ca0cc23959f5f33e8becd9b08cae", "redirect_uri": "http://*****:*****@example.com"] req["response_types"] = ["code"] resp = self.provider.registration_endpoint(request=req.to_json()) regresp = RegistrationResponse().deserialize(resp.message, "json") assert _eq(regresp.keys(), ['redirect_uris', 'contacts', 'application_type', 'client_name', 'registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'client_secret', 'client_id_issued_at', 'response_types']) def test_registration_endpoint_with_non_https_redirect_uri_implicit_flow( self): params = {"application_type": "web", "redirect_uris": ["http://example.com/authz"], "response_types": ["id_token", "token"]} req = RegistrationRequest(**params) resp = self.provider.registration_endpoint(request=req.to_json()) assert resp.status == "400 Bad Request" error = json.loads(resp.message) assert error["error"] == "invalid_redirect_uri" def test_verify_redirect_uris_with_https_code_flow(self): params = {"application_type": "web", "redirect_uris": ["http://example.com/authz"], "response_types": ["code"]} request = RegistrationRequest(**params) verified_uris = self.provider._verify_redirect_uris(request) assert verified_uris == [("http://example.com/authz", None)] def test_verify_redirect_uris_with_non_https_redirect_uri_implicit_flow(self): params = {"application_type": "web", "redirect_uris": ["http://example.com/authz"], "response_types": ["id_token", "token"]} request = RegistrationRequest(**params) with pytest.raises(InvalidRedirectURIError) as exc_info: self.provider._verify_redirect_uris(request) assert str(exc_info.value) == "None https redirect_uri not allowed" @pytest.mark.network def test_registration_endpoint_openid4us(self): req = RegistrationRequest( **{'token_endpoint_auth_method': u'client_secret_post', 'redirect_uris': [ u'https://connect.openid4.us:5443/phpRp/index.php/callback', u'https://connect.openid4.us:5443/phpRp/authcheck.php/authcheckcb'], 'jwks_uri': u'https://connect.openid4.us:5443/phpRp/rp/rp.jwk', 'userinfo_encrypted_response_alg': u'RSA1_5', 'contacts': [u'*****@*****.**'], 'userinfo_encrypted_response_enc': u'A128CBC-HS256', 'application_type': u'web', 'client_name': u'ABRP-17', 'grant_types': [u'authorization_code', u'implicit'], 'post_logout_redirect_uris': [ u'https://connect.openid4.us:5443/phpRp/index.php/logoutcb'], 'subject_type': u'public', 'response_types': [u'code', u'token', u'id_token', u'code token', u'code id_token', u'id_token token', u'code id_token token'], 'policy_uri': u'https://connect.openid4.us:5443/phpRp/index.php/policy', 'logo_uri': u'https://connect.openid4.us:5443/phpRp/media/logo.png'}) resp = self.provider.registration_endpoint(request=req.to_json()) regresp = RegistrationResponse().deserialize(resp.message, "json") assert _eq(regresp.keys(), list(req.keys()) + ['registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'client_secret', 'client_id_issued_at']) def test_provider_key_setup(self, tmpdir): path = tmpdir.strpath provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), None, None, None, None, None, "") provider.baseurl = "http://www.example.com" provider.key_setup(path, path, sig={"format": "jwk", "alg": "RSA"}) keys = provider.keyjar.get_signing_key("RSA") assert len(keys) == 1 assert provider.jwks_uri == "http://www.example.com/{}/jwks".format( path) @pytest.mark.parametrize("uri", [ "http://example.org/foo", "http://example.com/cb", "http://example.org/cb?got=you", "http://example.org/cb/foo?got=you" ]) def test_verify_redirect_uri_faulty_without_query(self, uri): rr = RegistrationRequest(operation="register", redirect_uris=["http://example.org/cb"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest(redirect_uri=uri, client_id=cid, response_type="code", scope="openid") with pytest.raises(RedirectURIError): self.provider._verify_redirect_uri(areq) @pytest.mark.parametrize("uri", [ "http://example.org/cb", "http://example.org/cb/foo" ]) def test_verify_redirect_uri_correct_without_query(self, uri): rr = RegistrationRequest(operation="register", redirect_uris=["http://example.org/cb"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest(redirect_uri=uri, client_id=cid, response_type="code", scope="openid") self.provider._verify_redirect_uri(areq) @pytest.mark.parametrize("uri", [ "http://example.org/cb", "http://example.org/cb/foo", "http://example.org/cb?got=you", "http://example.org/cb?foo=you" "http://example.org/cb?foo=bar&got=you", "http://example.org/cb?foo=you&foo=bar" ]) def test_registered_redirect_uri_faulty_with_query_component(self, uri): rr = RegistrationRequest(operation="register", redirect_uris=[ "http://example.org/cb?foo=bar"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest(redirect_uri=uri, client_id=cid, scope="openid", response_type="code") with pytest.raises(RedirectURIError): self.provider._verify_redirect_uri(areq) def test_registered_redirect_uri_correct_with_query_component(self): rr = RegistrationRequest(operation="register", redirect_uris=[ "http://example.org/cb?foo=bar"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest( redirect_uri="http://example.org/cb?foo=bar", client_id=cid, scope="openid", response_type="code") self.provider._verify_redirect_uri(areq) def test_key_rollover(self): provider2 = Provider("FOOP", {}, {}, None, None, None, None, "") provider2.keyjar = KEYJAR # Number of KeyBundles assert len(provider2.keyjar.issuer_keys[""]) == 1 kb = ec_init({"type": "EC", "crv": "P-256", "use": ["sig"]}) provider2.do_key_rollover(json.loads(kb.jwks()), "b%d") assert len(provider2.keyjar.issuer_keys[""]) == 2 kb = ec_init({"type": "EC", "crv": "P-256", "use": ["sig"]}) provider2.do_key_rollover(json.loads(kb.jwks()), "b%d") assert len(provider2.keyjar.issuer_keys[""]) == 3 provider2.remove_inactive_keys(-1) assert len(provider2.keyjar.issuer_keys[""]) == 2 def test_endsession_endpoint(self): resp = self.provider.endsession_endpoint("") self._assert_cookies_expired(resp.headers) # End session not allowed if no cookie is sent (can't determine session) resp = self.provider.endsession_endpoint("", cookie="FAIL") assert resp.status == "400 Bad Request" def test_endsession_endpoint_with_id_token_hint(self): id_token = self._auth_with_id_token() assert self.provider.sdb.get_sids_by_sub( id_token["sub"]) # verify we got valid session id_token_hint = id_token.to_jwt(algorithm="none") resp = self.provider.endsession_endpoint( urlencode({"id_token_hint": id_token_hint})) assert not self.provider.sdb.get_sids_by_sub( id_token["sub"]) # verify session has been removed self._assert_cookies_expired(resp.headers) def test_endsession_endpoint_with_post_logout_redirect_uri(self): id_token = self._auth_with_id_token() assert self.provider.sdb.get_sids_by_sub( id_token["sub"]) # verify we got valid session post_logout_redirect_uri = \ CDB[CLIENT_CONFIG["client_id"]]["post_logout_redirect_uris"][0][0] resp = self.provider.endsession_endpoint(urlencode( {"post_logout_redirect_uri": post_logout_redirect_uri})) assert isinstance(resp, Redirect) assert not self.provider.sdb.get_sids_by_sub( id_token["sub"]) # verify session has been removed self._assert_cookies_expired(resp.headers) def test_session_state_in_auth_req_for_session_support(self): provider = Provider("foo", SessionDB(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR, capabilities={ "check_session_iframe": "https://op.example.com/check_session"}) req_args = {"scope": ["openid"], "redirect_uri": "http://localhost:8087/authz", "response_type": ["code"], "client_id": "a1b2c3" } areq = AuthorizationRequest(**req_args) resp = provider.authorization_endpoint( request=areq.to_urlencoded()) aresp = self.cons.parse_response(AuthorizationResponse, resp.message, sformat="urlencoded") assert "session_state" in aresp def _assert_cookies_expired(self, http_headers): cookies_string = ";".join( [c[1] for c in http_headers if c[0] == "Set-Cookie"]) all_cookies = SimpleCookie() all_cookies.load(cookies_string) now = datetime.datetime.now() for c in [self.provider.cookie_name, self.provider.session_cookie_name]: dt = datetime.datetime.strptime(all_cookies[c]["expires"], "%a, %d-%b-%Y %H:%M:%S GMT") assert dt < now # make sure the cookies have expired to be cleared def _auth_with_id_token(self): state, location = self.cons.begin("openid", "id_token", path="http://localhost:8087") resp = self.provider.authorization_endpoint( request=location.split("?")[1]) aresp = self.cons.parse_response(AuthorizationResponse, resp.message, sformat="urlencoded") return aresp["id_token"]
class TestOICProvider(object): def setup_class(self): self.server = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR) self.cons = Consumer( {}, CONSUMER_CONFIG, CLIENT_CONFIG, server_info=SERVER_INFO, ) self.cons.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.cons.debug = True self.cons.keyjar[""] = KC_RSA def test_server_init(self): assert self.server assert self.server.authn_broker == AUTHN_BROKER print self.server.urlmap assert self.server.urlmap["client_1"] == ["https://example.com/authz"] def test_server_authorization_endpoint(self): bib = { "scope": ["openid"], "state": "id-6da9ca0cc23959f5f33e8becd9b08cae", "redirect_uri": "http://*****:*****@example.com"] req["response_types"] = ["code"] print req.to_dict() resp = self.server.registration_endpoint(request=req.to_json()) print resp.message regresp = RegistrationResponse().deserialize(resp.message, "json") print regresp.keys() assert _eq(regresp.keys(), [ 'redirect_uris', 'contacts', 'application_type', 'client_name', 'registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'client_secret', 'client_id_issued_at', 'response_types' ]) def test_provider_key_setup(self): provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), None, None, None, None, None, "") provider.baseurl = "http://www.example.com/" provider.key_setup("static", sig={"format": "jwk", "alg": "RSA"}) keys = provider.keyjar.get_signing_key("RSA") assert len(keys) == 1 assert provider.jwks_uri == "http://www.example.com/static/jwks" def _client_id(self, cdb): cid = None for k, item in cdb.items(): if item in cdb.keys(): cid = item break return cid def test_registered_redirect_uri_without_query_component(self): provider = Provider("FOO", {}, {}, None, None, None, None, "") rr = RegistrationRequest(operation="register", redirect_uris=["http://example.org/cb"], response_types=["code"]) registration_req = rr.to_json() provider.registration_endpoint(request=registration_req) correct = [ "http://example.org/cb", "http://example.org/cb/foo", ] faulty = [ "http://example.org/foo", "http://example.com/cb", "http://example.org/cb?got=you", "http://example.org/cb/foo?got=you" ] cid = self._client_id(provider.cdb) for ruri in faulty: areq = AuthorizationRequest(redirect_uri=ruri, client_id=cid, response_type="code", scope="openid") print areq try: provider._verify_redirect_uri(areq) assert False except RedirectURIError: pass for ruri in correct: areq = AuthorizationRequest(redirect_uri=ruri, client_id=cid, response_type="code", scope="openid") print areq try: provider._verify_redirect_uri(areq) except RedirectURIError, err: print err assert False