Esempio n. 1
0
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)
Esempio n. 3
0
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)