class TestClient(object): @pytest.fixture(autouse=True) def create_client(self, fake_oic_server): self.redirect_uri = "http://example.com/redirect" self.client = Client(CLIENT_ID, client_authn_method=CLIENT_AUTHN_METHOD) self.client.redirect_uris = [self.redirect_uri] self.client.authorization_endpoint = "http://example.com/authorization" self.client.token_endpoint = "http://example.com/token" self.client.userinfo_endpoint = "http://example.com/userinfo" self.client.check_session_endpoint = "https://example.com/check_session" self.client.client_secret = "abcdefghijklmnop" self.client.keyjar[""] = KC_RSA self.client.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.mfos = fake_oic_server("http://example.com") self.mfos.keyjar = KEYJ self.client.http_request = self.mfos.http_request def test_construct_authz_req_with_request_object(self, tmpdir): path = tmpdir.strpath request_uri_args = { "local_dir": path, "base_path": "http://example.com/" } areq = self.client.construct_AuthorizationRequest( request_method="file", **request_uri_args) p = urlparse(areq["request_uri"]) local_path = os.path.join(path, p.path.lstrip("/")) with open(local_path) as f: data = f.read() jwt = JWT().unpack(data) payload = jwt.payload() assert payload["redirect_uri"] == "http://example.com/redirect" assert payload["client_id"] == CLIENT_ID assert "nonce" in payload os.remove(local_path) def test_construct_authz_req_nonce_for_token(self): assert "nonce" in self.client.construct_AuthorizationRequest( response_type="token") assert "nonce" in self.client.construct_AuthorizationRequest( response_type="id_token") assert "nonce" in self.client.construct_AuthorizationRequest( response_type="token id_token") def test_do_authorization_request(self): args = {"response_type": ["code"], "scope": "openid"} result = self.client.do_authorization_request(state="state0", request_args=args) assert result.status_code == 302 _loc = result.headers["location"] assert _loc.startswith(self.client.redirect_uris[0]) _, query = _loc.split("?") self.client.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") def test_access_token_request(self): args = {"response_type": ["code"], "scope": ["openid"]} r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") resp = self.client.do_access_token_request(scope="openid", state="state0") assert isinstance(resp, AccessTokenResponse) assert _eq(resp.keys(), ["token_type", "state", "access_token", "scope"]) def test_access_token_request_with_custom_response_class(self): # AccessTokenResponse wrapper class class AccessTokenResponseWrapper(AccessTokenResponse): c_param = AccessTokenResponse.c_param.copy() c_param.update({"raw_id_token": SINGLE_OPTIONAL_STRING}) def __init__(self, **kwargs): super(AccessTokenResponseWrapper, self).__init__(**kwargs) self["raw_id_token"] = None def verify(self, **kwargs): if "id_token" in self: self["raw_id_token"] = self["id_token"] return super(AccessTokenResponseWrapper, self).verify(**kwargs) args = {"response_type": ["code"], "scope": ["openid"]} class CustomMessageFactory(OIDCMessageFactory): token_endpoint = MessageTuple(AccessTokenRequest, AccessTokenResponseWrapper) # Change message_factory self.client.message_factory = CustomMessageFactory r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") resp = self.client.do_access_token_request(scope="openid", state="state0") assert isinstance(resp, AccessTokenResponse) assert isinstance(resp, AccessTokenResponseWrapper) assert _eq( resp.keys(), ["token_type", "state", "access_token", "scope", "raw_id_token"], ) assert len(self.client.grant["state0"].tokens) == 1 def test_do_user_info_request(self): resp = AuthorizationResponse(code="code", state="state") grant = Grant(10) # expired grant grant.add_code(resp) resp2 = AccessTokenResponse(refresh_token="refresh_with_me", access_token="access", token_type="Bearer") token = Token(resp2) grant.tokens.append(token) self.client.grant["state0"] = grant resp3 = self.client.do_user_info_request(state="state0") assert isinstance(resp3, OpenIDSchema) assert _eq(resp3.keys(), ["name", "email", "verified", "nickname", "sub"]) assert resp3["name"] == "Melody Gardot" def test_do_access_token_refresh(self): args = { "response_type": ["code"], "scope": ["openid", "offline_access"], "prompt": ["consent"], } r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") self.client.do_access_token_request(scope="openid offline_access", state="state0") resp = self.client.do_access_token_refresh( scope="openid offline_access", state="state0") assert len(self.client.grant["state0"].tokens) == 1 assert isinstance(resp, AccessTokenResponse) assert _eq( resp.keys(), ["token_type", "access_token", "refresh_token", "scope", "state"], ) def test_client_id(self): resp = AuthorizationResponse(code="code", state="stateX").to_urlencoded() self.client.parse_response(AuthorizationResponse, resp, sformat="urlencoded") args = { "code": "code", "redirect_uri": self.client.redirect_uris[0], "client_id": self.client.client_id, } url, query, ht_args, cis = self.client.request_info( AccessTokenRequest, method="POST", request_args=args, state="stateX", authn_method="client_secret_basic", grant_type="authorization_code", ) assert "client_id" not in cis args = {"code": "code", "redirect_uri": self.client.redirect_uris[0]} url, query, ht_args, cis = self.client.request_info( AccessTokenRequest, method="POST", request_args=args, state="stateX", authn_method="client_secret_basic", grant_type="authorization_code", ) assert "client_id" not in cis def test_do_check_session_request(self): # RSA signing alg = "RS256" ktyp = alg2keytype(alg) _sign_key = self.client.keyjar.get_signing_key(ktyp) args = {"id_token": IDTOKEN.to_jwt(key=_sign_key, algorithm=alg)} resp = self.client.do_check_session_request(request_args=args) assert isinstance(resp, IdToken) assert _eq(resp.keys(), ["nonce", "sub", "aud", "iss", "exp", "iat"]) def test_do_end_session_request(self): self.client.redirect_uris = ["https://www.example.com/authz"] self.client.client_id = "a1b2c3" self.client.end_session_endpoint = "https://example.org/end_session" # RSA signing alg = "RS256" ktyp = alg2keytype(alg) _sign_key = self.client.keyjar.get_signing_key(ktyp) args = { "id_token": IDTOKEN.to_jwt(key=_sign_key, algorithm=alg), "redirect_url": "http://example.com/end", } resp = self.client.do_end_session_request(request_args=args, state="state1") assert resp.status_code == 302 assert resp.headers["location"].startswith("http://example.com/end") def test_do_registration_request(self): self.client.registration_endpoint = "https://example.org/registration" args = { "operation": "register", "application_type": "web", "application_name": "my service", "redirect_uri": "http://example.com/authz", } resp = self.client.do_registration_request(request_args=args) assert _eq( resp.keys(), [ "redirect_uris", u"redirect_uri", "application_type", "registration_client_uri", "client_secret_expires_at", "registration_access_token", "client_id", "application_name", "client_secret", "response_types", ], ) def test_do_registration_response_missing_attribute(self): # this is lacking the required "redirect_uris" claim in the registration response msg = { "client_id": "s6BhdRkqt3", "client_secret": "ZJYCqe3GGRvdrudKyZS0XhGv_Z45DuKhCUk0gBR1vZk", "token_endpoint_auth_method": "client_secret_basic", } r = Response() r.status_code = 201 r._content = str.encode(json.dumps(msg)) # type: ignore with pytest.raises(RegistrationError) as ex: self.client.handle_registration_info(response=r) assert "Missing required attribute 'redirect_uris'" in str( ex.value) def test_do_user_info_request_with_access_token_refresh(self): args = { "response_type": ["code"], "scope": ["openid offline_access"], "prompt": "consent", } r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") self.client.do_access_token_request(scope="openid offline_access", state="state0") token = self.client.get_token(state="state0", scope="openid offline_access") token.token_expiration_time = utc_time_sans_frac() - 86400 resp = self.client.do_user_info_request(state="state0") assert isinstance(resp, OpenIDSchema) assert _eq(resp.keys(), ["name", "email", "verified", "nickname", "sub"]) assert resp["name"] == "Melody Gardot" def test_openid_request_with_claims_request(self): claims = { "name": { "essential": True }, "nickname": None, "email": { "essential": True }, "verified": { "essential": True }, "picture": None, } areq = self.client.construct_AuthorizationRequest( request_args={ "scope": "openid", "response_type": ["code"], "claims": ClaimsRequest( userinfo=Claims(**claims), id_token=Claims(auth_time=None, acr={"values": ["2"]}), ), "max_age": 86400, }, request_param="request", ) assert "request" in areq def test_openid_request_with_id_token_claims_request(self): areq = self.client.construct_AuthorizationRequest( request_args={ "scope": "openid", "response_type": ["code"], "claims": { "id_token": { "sub": { "value": "248289761001" } } }, }, request_param="request", ) jwtreq = OpenIDRequest().deserialize(areq["request"], "jwt", keyjar=self.client.keyjar) assert _eq( jwtreq.keys(), ["claims", "redirect_uri", "response_type", "client_id", "scope"], ) def test_construct_UserInfoRequest_with_req_args(self): uir = self.client.construct_UserInfoRequest( request_args={"access_token": "access_token"}) assert uir["access_token"] == "access_token" def test_construct_UserInfoRequest_2_with_token(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse( refresh_token="refresh_with_me", access_token="access", id_token="IDTOKEN", scope=["openid"], ) self.client.grant["foo"].tokens.append(Token(resp)) uir = self.client.construct_UserInfoRequest(state="foo", scope=["openid"]) assert uir["access_token"] == "access" def test_construct_CheckSessionRequest_with_req_args(self): csr = self.client.construct_CheckSessionRequest( request_args={"id_token": "id_token"}) assert csr["id_token"] == "id_token" def test_construct_CheckSessionRequest_2(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(id_token="id_id_id_id", access_token="access", scope=["openid"]) self.client.grant["foo"].tokens.append(Token(resp)) csr = self.client.construct_CheckSessionRequest(state="foo", scope=["openid"]) assert csr["id_token"] == "id_id_id_id" def test_construct_RegistrationRequest(self): request_args = { "type": "client_associate", "client_id": CLIENT_ID, "contacts": ["*****@*****.**"], "application_type": "web", "application_name": "EXAMPLE OIC service", } crr = self.client.construct_RegistrationRequest( request_args=request_args) assert _eq( crr.keys(), [ "application_name", "application_type", "type", "client_id", "contacts", "redirect_uris", "response_types", ], ) def test_construct_EndSessionRequest(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(id_token="id_id_id_id", access_token="access", scope=["openid"]) self.client.grant["foo"].tokens.append(Token(resp)) args = {"redirect_url": "http://example.com/end"} esr = self.client.construct_EndSessionRequest(state="foo", request_args=args) assert _eq(esr.keys(), ["id_token", "state", "redirect_url"]) def test_construct_OpenIDRequest(self): self.client.scope = ["openid", "profile"] request_args = { "response_type": "code id_token", "state": "af0ifjsldkj" } areq = self.client.construct_AuthorizationRequest( request_args=request_args) assert _eq( areq.keys(), [ "nonce", "state", "redirect_uri", "response_type", "client_id", "scope" ], ) def test_userinfo_request(self): aresp = AuthorizationResponse(code="code", state="state000") tresp = AccessTokenResponse( access_token="access_token", token_type="Bearer", expires_in=600, refresh_token="refresh", scope=["openid"], ) self.client.parse_response( AuthorizationResponse, aresp.to_urlencoded(), sformat="urlencoded", state="state0", ) self.client.parse_response(AccessTokenResponse, tresp.to_json(), state="state0") path, body, method, h_args = self.client.user_info_request( state="state0") assert path == "http://example.com/userinfo" assert method == "GET" assert body is None assert h_args == {"headers": {"Authorization": "Bearer access_token"}} def test_userinfo_request_post(self): aresp = AuthorizationResponse(code="code", state="state000") tresp = AccessTokenResponse( access_token="access_token", token_type="bearer", expires_in=600, refresh_token="refresh", scope=["openid"], ) self.client.parse_response( AuthorizationResponse, aresp.to_urlencoded(), sformat="urlencoded", state="state0", ) self.client.parse_response(AccessTokenResponse, tresp.to_json(), state="state0") path, body, method, h_args = self.client.user_info_request( method="POST", state="state0") assert path == "http://example.com/userinfo" assert method == "POST" assert body == "access_token=access_token" assert h_args == { "headers": { "Content-Type": "application/x-www-form-urlencoded" } } def test_sign_enc_request(self): KC_RSA_ENC = KeyBundle({"key": _key, "kty": "RSA", "use": "enc"}) self.client.keyjar["test_provider"] = [KC_RSA_ENC] request_args = { "redirect_uri": self.redirect_uri, "client_id": self.client.client_id, "scope": "openid", "response_type": "code", } kwargs = { "request_object_signing_alg": "none", "request_object_encryption_alg": "RSA1_5", "request_object_encryption_enc": "A128CBC-HS256", "request_method": "parameter", "target": "test_provider", } areq = self.client.construct_AuthorizationRequest( request_args=request_args, **kwargs) assert areq["request"] def test_verify_id_token_reject_wrong_aud(self, monkeypatch): issuer = "https://provider.example.com" monkeypatch.setattr(self.client, "provider_info", {"issuer": issuer}) id_token = IdToken(**dict(iss=issuer, aud=["nobody"])) with pytest.raises(OtherError) as exc: self.client._verify_id_token(id_token) assert "me" in str(exc.value) def test_verify_id_token_reject_wrong_azp(self, monkeypatch): issuer = "https://provider.example.com" monkeypatch.setattr(self.client, "provider_info", {"issuer": issuer}) id_token = IdToken(**dict( iss=issuer, aud=["nobody", "somebody", self.client.client_id], azp="nobody", )) with pytest.raises(OtherError) as exc: self.client._verify_id_token(id_token) assert "me" in str(exc.value) def test_clean_tokens_fresh(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse( refresh_token="refresh_with_me", access_token="access", id_token="IDTOKEN", scope=["openid"], ) self.client.grant["foo"].tokens.append(Token(resp)) self.client.clean_tokens() assert len(self.client.grant["foo"].tokens) == 1 def test_clean_tokens_replaced(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse( refresh_token="refresh_with_me", access_token="access", id_token="IDTOKEN", scope=["openid"], ) self.client.grant["foo"].tokens.append(Token(resp)) self.client.grant["foo"].tokens[0].replaced = True self.client.clean_tokens() assert len(self.client.grant["foo"].tokens) == 0 def test_clean_tokens_stale(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse( refresh_token="refresh_with_me", access_token="access", id_token="IDTOKEN", scope=["openid"], ) self.client.grant["foo"].tokens.append(Token(resp)) self.client.grant["foo"].tokens[0].token_expiration_time = 10 self.client.clean_tokens() assert len(self.client.grant["foo"].tokens) == 0
class TestClient(object): @pytest.fixture(autouse=True) def create_client(self): self.redirect_uri = "http://example.com/redirect" self.client = Client(CLIENT_ID, client_authn_method=CLIENT_AUTHN_METHOD) self.client.redirect_uris = [self.redirect_uri] self.client.authorization_endpoint = "http://example.com/authorization" self.client.token_endpoint = "http://example.com/token" self.client.userinfo_endpoint = "http://example.com/userinfo" self.client.check_session_endpoint = "https://example.com/check_session" self.client.client_secret = "abcdefghijklmnop" self.client.keyjar[""] = KC_RSA self.client.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]} mfos = MyFakeOICServer() mfos.keyjar = KEYJ self.client.http_request = mfos.http_request def test_construct_authz_req_with_request_object(self, tmpdir): path = tmpdir.strpath request_uri_args = { "local_dir": path, "base_path": "http://example.com/" } areq = self.client.construct_AuthorizationRequest(request_method="file", **request_uri_args) p = urlparse(areq["request_uri"]) local_path = os.path.join(path, p.path.lstrip("/")) with open(local_path) as f: data = f.read() jwt = JWT().unpack(data) payload = jwt.payload() assert payload["redirect_uri"] == "http://example.com/redirect" assert payload["client_id"] == CLIENT_ID assert "nonce" in payload os.remove(local_path) def test_construct_authz_req_nonce_for_token(self): assert "nonce" in self.client.construct_AuthorizationRequest( response_type="token") assert "nonce" in self.client.construct_AuthorizationRequest( response_type="id_token") assert "nonce" in self.client.construct_AuthorizationRequest( response_type="token id_token") def test_do_authorization_request(self): args = {"response_type": ["code"], "scope": "openid"} result = self.client.do_authorization_request(state="state0", request_args=args) assert result.status_code == 302 _loc = result.headers["location"] assert _loc.startswith(self.client.redirect_uris[0]) _, query = _loc.split("?") self.client.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") def test_access_token_request(self): args = {"response_type": ["code"], "scope": ["openid"]} r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") resp = self.client.do_access_token_request(scope="openid", state="state0") assert isinstance(resp, AccessTokenResponse) assert _eq(resp.keys(), ['token_type', 'state', 'access_token', 'scope']) def test_do_user_info_request(self): resp = AuthorizationResponse(code="code", state="state") grant = Grant(10) # expired grant grant.add_code(resp) resp = AccessTokenResponse(refresh_token="refresh_with_me", access_token="access", token_type="Bearer") token = Token(resp) grant.tokens.append(token) self.client.grant["state0"] = grant resp = self.client.do_user_info_request(state="state0") assert isinstance(resp, OpenIDSchema) assert _eq(resp.keys(), ['name', 'email', 'verified', 'nickname', 'sub']) assert resp["name"] == "Melody Gardot" def test_do_access_token_refresh(self): args = {"response_type": ["code"], "scope": ["openid", "offline_access"], "prompt": ["consent"]} r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") self.client.do_access_token_request(scope="openid offline_access", state="state0") resp = self.client.do_access_token_refresh(scope="openid offline_access", state="state0") assert isinstance(resp, AccessTokenResponse) assert _eq(resp.keys(), ['token_type', 'access_token', 'refresh_token', 'scope', 'state']) def test_do_check_session_request(self): # RSA signing alg = "RS256" ktyp = alg2keytype(alg) _sign_key = self.client.keyjar.get_signing_key(ktyp) args = {"id_token": IDTOKEN.to_jwt(key=_sign_key, algorithm=alg)} resp = self.client.do_check_session_request(request_args=args) assert isinstance(resp, IdToken) assert _eq(resp.keys(), ['nonce', 'sub', 'aud', 'iss', 'exp', 'iat']) def test_do_end_session_request(self): self.client.redirect_uris = ["https://www.example.com/authz"] self.client.client_id = "a1b2c3" self.client.end_session_endpoint = "https://example.org/end_session" # RSA signing alg = "RS256" ktyp = alg2keytype(alg) _sign_key = self.client.keyjar.get_signing_key(ktyp) args = {"id_token": IDTOKEN.to_jwt(key=_sign_key, algorithm=alg), "redirect_url": "http://example.com/end"} resp = self.client.do_end_session_request(request_args=args, state="state1") assert resp.status_code == 302 assert resp.headers["location"].startswith("http://example.com/end") def test_do_registration_request(self): self.client.registration_endpoint = "https://example.org/registration" args = {"operation": "register", "application_type": "web", "application_name": "my service", "redirect_uri": "http://example.com/authz"} resp = self.client.do_registration_request(request_args=args) assert _eq(resp.keys(), ['redirect_uris', u'redirect_uri', 'application_type', 'registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'application_name', 'client_secret', 'response_types']) def test_do_user_info_request_with_access_token_refresh(self): args = {"response_type": ["code"], "scope": ["openid offline_access"], "prompt": "consent"} r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") self.client.do_access_token_request(scope="openid offline_access", state="state0") token = self.client.get_token(state="state0", scope="openid offline_access") token.token_expiration_time = utc_time_sans_frac() - 86400 resp = self.client.do_user_info_request(state="state0") assert isinstance(resp, OpenIDSchema) assert _eq(resp.keys(), ['name', 'email', 'verified', 'nickname', 'sub']) assert resp["name"] == "Melody Gardot" def test_openid_request_with_claims_request(self): claims = { "name": {"essential": True}, "nickname": None, "email": {"essential": True}, "verified": {"essential": True}, "picture": None } areq = self.client.construct_AuthorizationRequest( request_args={ "scope": "openid", "response_type": ["code"], "claims": ClaimsRequest(userinfo=Claims(**claims), id_token=Claims(auth_time=None, acr={"values": ["2"]})), "max_age": 86400, }, request_param="request") assert "request" in areq def test_openid_request_with_id_token_claims_request(self): areq = self.client.construct_AuthorizationRequest( request_args={"scope": "openid", "response_type": ["code"], "claims": { "id_token": {"sub": {"value": "248289761001"}}}}, request_param="request" ) jwtreq = OpenIDRequest().deserialize(areq["request"], "jwt", keyjar=self.client.keyjar) assert _eq(jwtreq.keys(), ['claims', 'redirect_uri', 'response_type', 'client_id', 'scope']) def test_construct_UserInfoRequest_with_req_args(self): uir = self.client.construct_UserInfoRequest( request_args={"access_token": "access_token"}) assert uir["access_token"] == "access_token" def test_construct_UserInfoRequest_2_with_token(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(refresh_token="refresh_with_me", access_token="access", id_token="IDTOKEN", scope=["openid"]) self.client.grant["foo"].tokens.append(Token(resp)) uir = self.client.construct_UserInfoRequest(state="foo", scope=["openid"]) assert uir["access_token"] == "access" def test_construct_CheckSessionRequest_with_req_args(self): csr = self.client.construct_CheckSessionRequest( request_args={"id_token": "id_token"}) assert csr["id_token"] == "id_token" def test_construct_CheckSessionRequest_2(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(id_token="id_id_id_id", access_token="access", scope=["openid"]) self.client.grant["foo"].tokens.append(Token(resp)) csr = self.client.construct_CheckSessionRequest(state="foo", scope=["openid"]) assert csr["id_token"] == "id_id_id_id" def test_construct_RegistrationRequest(self): request_args = { "type": "client_associate", "client_id": CLIENT_ID, "contacts": ["*****@*****.**"], "application_type": "web", "application_name": "EXAMPLE OIC service", } crr = self.client.construct_RegistrationRequest( request_args=request_args) assert _eq(crr.keys(), ['application_name', 'application_type', 'type', 'client_id', 'contacts', 'redirect_uris', 'response_types']) def test_construct_EndSessionRequest(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(id_token="id_id_id_id", access_token="access", scope=["openid"]) self.client.grant["foo"].tokens.append(Token(resp)) args = {"redirect_url": "http://example.com/end"} esr = self.client.construct_EndSessionRequest(state="foo", request_args=args) assert _eq(esr.keys(), ['id_token', 'state', "redirect_url"]) def test_construct_OpenIDRequest(self): self.client.scope = ["openid", "profile"] request_args = {"response_type": "code id_token", "state": "af0ifjsldkj"} areq = self.client.construct_AuthorizationRequest( request_args=request_args) assert _eq(areq.keys(), ['nonce', 'state', 'redirect_uri', 'response_type', 'client_id', 'scope']) def test_userinfo_request(self): aresp = AuthorizationResponse(code="code", state="state000") tresp = AccessTokenResponse(access_token="access_token", token_type="Bearer", expires_in=600, refresh_token="refresh", scope=["openid"]) self.client.parse_response(AuthorizationResponse, aresp.to_urlencoded(), sformat="urlencoded", state="state0") self.client.parse_response(AccessTokenResponse, tresp.to_json(), state="state0") path, body, method, h_args = self.client.user_info_request( state="state0") assert path == "http://example.com/userinfo" assert method == "GET" assert body is None assert h_args == {'headers': {'Authorization': 'Bearer access_token'}} def test_userinfo_request_post(self): aresp = AuthorizationResponse(code="code", state="state000") tresp = AccessTokenResponse(access_token="access_token", token_type="bearer", expires_in=600, refresh_token="refresh", scope=["openid"]) self.client.parse_response(AuthorizationResponse, aresp.to_urlencoded(), sformat="urlencoded", state="state0") self.client.parse_response(AccessTokenResponse, tresp.to_json(), state="state0") path, body, method, h_args = self.client.user_info_request( method="POST", state="state0") assert path == "http://example.com/userinfo" assert method == "POST" assert body == "access_token=access_token" assert h_args == {'headers': { 'Content-Type': 'application/x-www-form-urlencoded'}} def test_sign_enc_request(self): KC_RSA_ENC = KeyBundle({"key": _key, "kty": "RSA", "use": "enc"}) self.client.keyjar["test_provider"] = [KC_RSA_ENC] request_args = {"redirect_uri": self.redirect_uri, "client_id": self.client.client_id, "scope": "openid", "response_type": "code"} kwargs = {"request_object_signing_alg": "none", "request_object_encryption_alg": "RSA1_5", "request_object_encryption_enc": "A128CBC-HS256", "request_method": "parameter", "target": "test_provider"} areq = self.client.construct_AuthorizationRequest( request_args=request_args, **kwargs) assert areq["request"] def test_verify_id_token_reject_wrong_aud(self, monkeypatch): issuer = "https://provider.example.com" monkeypatch.setattr(self.client, "provider_info", {"issuer": issuer}) id_token = IdToken(**dict(iss=issuer, aud=["nobody"])) with pytest.raises(OtherError) as exc: self.client._verify_id_token(id_token) assert "me" in str(exc.value) def test_verify_id_token_reject_wrong_azp(self, monkeypatch): issuer = "https://provider.example.com" monkeypatch.setattr(self.client, "provider_info", {"issuer": issuer}) id_token = IdToken( **dict(iss=issuer, aud=["nobody", "somebody", self.client.client_id], azp="nobody")) with pytest.raises(OtherError) as exc: self.client._verify_id_token(id_token) assert "me" in str(exc.value)
class TestClient(object): @pytest.fixture(autouse=True) def create_client(self, fake_oic_server): self.redirect_uri = "http://example.com/redirect" self.client = Client(CLIENT_ID, client_authn_method=CLIENT_AUTHN_METHOD) self.client.redirect_uris = [self.redirect_uri] self.client.authorization_endpoint = "http://example.com/authorization" self.client.token_endpoint = "http://example.com/token" self.client.userinfo_endpoint = "http://example.com/userinfo" self.client.check_session_endpoint = "https://example.com/check_session" self.client.client_secret = "abcdefghijklmnop" self.client.keyjar[""] = KC_RSA self.client.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.mfos = fake_oic_server("http://example.com") self.mfos.keyjar = KEYJ self.client.http_request = self.mfos.http_request def test_construct_authz_req_with_request_object(self, tmpdir): path = tmpdir.strpath request_uri_args = { "local_dir": path, "base_path": "http://example.com/" } areq = self.client.construct_AuthorizationRequest( request_method="file", **request_uri_args) p = urlparse(areq["request_uri"]) local_path = os.path.join(path, p.path.lstrip("/")) with open(local_path) as f: data = f.read() jwt = JWT().unpack(data) payload = jwt.payload() assert payload["redirect_uri"] == "http://example.com/redirect" assert payload["client_id"] == CLIENT_ID assert "nonce" in payload os.remove(local_path) def test_construct_authz_req_nonce_for_token(self): assert "nonce" in self.client.construct_AuthorizationRequest( response_type="token") assert "nonce" in self.client.construct_AuthorizationRequest( response_type="id_token") assert "nonce" in self.client.construct_AuthorizationRequest( response_type="token id_token") def test_do_authorization_request(self): args = {"response_type": ["code"], "scope": "openid"} result = self.client.do_authorization_request(state="state0", request_args=args) assert result.status_code == 302 _loc = result.headers["location"] assert _loc.startswith(self.client.redirect_uris[0]) _, query = _loc.split("?") self.client.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") def test_access_token_request(self): args = {"response_type": ["code"], "scope": ["openid"]} r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") resp = self.client.do_access_token_request(scope="openid", state="state0") assert isinstance(resp, AccessTokenResponse) assert _eq(resp.keys(), ['token_type', 'state', 'access_token', 'scope']) def test_do_user_info_request(self): resp = AuthorizationResponse(code="code", state="state") grant = Grant(10) # expired grant grant.add_code(resp) resp = AccessTokenResponse(refresh_token="refresh_with_me", access_token="access", token_type="Bearer") token = Token(resp) grant.tokens.append(token) self.client.grant["state0"] = grant resp = self.client.do_user_info_request(state="state0") assert isinstance(resp, OpenIDSchema) assert _eq(resp.keys(), ['name', 'email', 'verified', 'nickname', 'sub']) assert resp["name"] == "Melody Gardot" def test_do_access_token_refresh(self): args = { "response_type": ["code"], "scope": ["openid", "offline_access"], "prompt": ["consent"] } r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") self.client.do_access_token_request(scope="openid offline_access", state="state0") resp = self.client.do_access_token_refresh( scope="openid offline_access", state="state0") assert isinstance(resp, AccessTokenResponse) assert _eq( resp.keys(), ['token_type', 'access_token', 'refresh_token', 'scope', 'state']) def test_client_id(self): resp = AuthorizationResponse(code="code", state="stateX").to_urlencoded() self.client.parse_response(AuthorizationResponse, resp, sformat="urlencoded") args = { "code": "code", "redirect_uri": self.client.redirect_uris[0], "client_id": self.client.client_id, } url, query, ht_args, cis = self.client.request_info( AccessTokenRequest, method="POST", request_args=args, state='stateX', authn_method='client_secret_basic', grant_type='authorization_code') assert cis['client_id'] == self.client.client_id args = { "code": "code", "redirect_uri": self.client.redirect_uris[0], # "client_id": self.client.client_id, } url, query, ht_args, cis = self.client.request_info( AccessTokenRequest, method="POST", request_args=args, state='stateX', authn_method='client_secret_basic', grant_type='authorization_code') assert cis['client_id'] == self.client.client_id def test_do_check_session_request(self): # RSA signing alg = "RS256" ktyp = alg2keytype(alg) _sign_key = self.client.keyjar.get_signing_key(ktyp) args = {"id_token": IDTOKEN.to_jwt(key=_sign_key, algorithm=alg)} resp = self.client.do_check_session_request(request_args=args) assert isinstance(resp, IdToken) assert _eq(resp.keys(), ['nonce', 'sub', 'aud', 'iss', 'exp', 'iat']) def test_do_end_session_request(self): self.client.redirect_uris = ["https://www.example.com/authz"] self.client.client_id = "a1b2c3" self.client.end_session_endpoint = "https://example.org/end_session" # RSA signing alg = "RS256" ktyp = alg2keytype(alg) _sign_key = self.client.keyjar.get_signing_key(ktyp) args = { "id_token": IDTOKEN.to_jwt(key=_sign_key, algorithm=alg), "redirect_url": "http://example.com/end" } resp = self.client.do_end_session_request(request_args=args, state="state1") assert resp.status_code == 302 assert resp.headers["location"].startswith("http://example.com/end") def test_do_registration_request(self): self.client.registration_endpoint = "https://example.org/registration" args = { "operation": "register", "application_type": "web", "application_name": "my service", "redirect_uri": "http://example.com/authz" } resp = self.client.do_registration_request(request_args=args) assert _eq(resp.keys(), [ 'redirect_uris', u'redirect_uri', 'application_type', 'registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'application_name', 'client_secret', 'response_types' ]) def test_do_user_info_request_with_access_token_refresh(self): args = { "response_type": ["code"], "scope": ["openid offline_access"], "prompt": "consent" } r = self.client.do_authorization_request(state="state0", request_args=args) self.client.parse_response(AuthorizationResponse, r.headers["location"], sformat="urlencoded") self.client.do_access_token_request(scope="openid offline_access", state="state0") token = self.client.get_token(state="state0", scope="openid offline_access") token.token_expiration_time = utc_time_sans_frac() - 86400 resp = self.client.do_user_info_request(state="state0") assert isinstance(resp, OpenIDSchema) assert _eq(resp.keys(), ['name', 'email', 'verified', 'nickname', 'sub']) assert resp["name"] == "Melody Gardot" def test_openid_request_with_claims_request(self): claims = { "name": { "essential": True }, "nickname": None, "email": { "essential": True }, "verified": { "essential": True }, "picture": None } areq = self.client.construct_AuthorizationRequest( request_args={ "scope": "openid", "response_type": ["code"], "claims": ClaimsRequest(userinfo=Claims(**claims), id_token=Claims(auth_time=None, acr={"values": ["2"]})), "max_age": 86400, }, request_param="request") assert "request" in areq def test_openid_request_with_id_token_claims_request(self): areq = self.client.construct_AuthorizationRequest( request_args={ "scope": "openid", "response_type": ["code"], "claims": { "id_token": { "sub": { "value": "248289761001" } } } }, request_param="request") jwtreq = OpenIDRequest().deserialize(areq["request"], "jwt", keyjar=self.client.keyjar) assert _eq( jwtreq.keys(), ['claims', 'redirect_uri', 'response_type', 'client_id', 'scope']) def test_construct_UserInfoRequest_with_req_args(self): uir = self.client.construct_UserInfoRequest( request_args={"access_token": "access_token"}) assert uir["access_token"] == "access_token" def test_construct_UserInfoRequest_2_with_token(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(refresh_token="refresh_with_me", access_token="access", id_token="IDTOKEN", scope=["openid"]) self.client.grant["foo"].tokens.append(Token(resp)) uir = self.client.construct_UserInfoRequest(state="foo", scope=["openid"]) assert uir["access_token"] == "access" def test_construct_CheckSessionRequest_with_req_args(self): csr = self.client.construct_CheckSessionRequest( request_args={"id_token": "id_token"}) assert csr["id_token"] == "id_token" def test_construct_CheckSessionRequest_2(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(id_token="id_id_id_id", access_token="access", scope=["openid"]) self.client.grant["foo"].tokens.append(Token(resp)) csr = self.client.construct_CheckSessionRequest(state="foo", scope=["openid"]) assert csr["id_token"] == "id_id_id_id" def test_construct_RegistrationRequest(self): request_args = { "type": "client_associate", "client_id": CLIENT_ID, "contacts": ["*****@*****.**"], "application_type": "web", "application_name": "EXAMPLE OIC service", } crr = self.client.construct_RegistrationRequest( request_args=request_args) assert _eq(crr.keys(), [ 'application_name', 'application_type', 'type', 'client_id', 'contacts', 'redirect_uris', 'response_types' ]) def test_construct_EndSessionRequest(self): self.client.grant["foo"] = Grant() self.client.grant["foo"].grant_expiration_time = time.time() + 60 self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(id_token="id_id_id_id", access_token="access", scope=["openid"]) self.client.grant["foo"].tokens.append(Token(resp)) args = {"redirect_url": "http://example.com/end"} esr = self.client.construct_EndSessionRequest(state="foo", request_args=args) assert _eq(esr.keys(), ['id_token', 'state', "redirect_url"]) def test_construct_OpenIDRequest(self): self.client.scope = ["openid", "profile"] request_args = { "response_type": "code id_token", "state": "af0ifjsldkj" } areq = self.client.construct_AuthorizationRequest( request_args=request_args) assert _eq(areq.keys(), [ 'nonce', 'state', 'redirect_uri', 'response_type', 'client_id', 'scope' ]) def test_userinfo_request(self): aresp = AuthorizationResponse(code="code", state="state000") tresp = AccessTokenResponse(access_token="access_token", token_type="Bearer", expires_in=600, refresh_token="refresh", scope=["openid"]) self.client.parse_response(AuthorizationResponse, aresp.to_urlencoded(), sformat="urlencoded", state="state0") self.client.parse_response(AccessTokenResponse, tresp.to_json(), state="state0") path, body, method, h_args = self.client.user_info_request( state="state0") assert path == "http://example.com/userinfo" assert method == "GET" assert body is None assert h_args == {'headers': {'Authorization': 'Bearer access_token'}} def test_userinfo_request_post(self): aresp = AuthorizationResponse(code="code", state="state000") tresp = AccessTokenResponse(access_token="access_token", token_type="bearer", expires_in=600, refresh_token="refresh", scope=["openid"]) self.client.parse_response(AuthorizationResponse, aresp.to_urlencoded(), sformat="urlencoded", state="state0") self.client.parse_response(AccessTokenResponse, tresp.to_json(), state="state0") path, body, method, h_args = self.client.user_info_request( method="POST", state="state0") assert path == "http://example.com/userinfo" assert method == "POST" assert body == "access_token=access_token" assert h_args == { 'headers': { 'Content-Type': 'application/x-www-form-urlencoded' } } def test_sign_enc_request(self): KC_RSA_ENC = KeyBundle({"key": _key, "kty": "RSA", "use": "enc"}) self.client.keyjar["test_provider"] = [KC_RSA_ENC] request_args = { "redirect_uri": self.redirect_uri, "client_id": self.client.client_id, "scope": "openid", "response_type": "code" } kwargs = { "request_object_signing_alg": "none", "request_object_encryption_alg": "RSA1_5", "request_object_encryption_enc": "A128CBC-HS256", "request_method": "parameter", "target": "test_provider" } areq = self.client.construct_AuthorizationRequest( request_args=request_args, **kwargs) assert areq["request"] def test_verify_id_token_reject_wrong_aud(self, monkeypatch): issuer = "https://provider.example.com" monkeypatch.setattr(self.client, "provider_info", {"issuer": issuer}) id_token = IdToken(**dict(iss=issuer, aud=["nobody"])) with pytest.raises(OtherError) as exc: self.client._verify_id_token(id_token) assert "me" in str(exc.value) def test_verify_id_token_reject_wrong_azp(self, monkeypatch): issuer = "https://provider.example.com" monkeypatch.setattr(self.client, "provider_info", {"issuer": issuer}) id_token = IdToken( **dict(iss=issuer, aud=["nobody", "somebody", self.client.client_id], azp="nobody")) with pytest.raises(OtherError) as exc: self.client._verify_id_token(id_token) assert "me" in str(exc.value)