Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-oic for openSUSE:Factory 
checked in at 2021-05-03 22:07:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-oic (Old)
 and      /work/SRC/openSUSE:Factory/.python-oic.new.2988 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-oic"

Mon May  3 22:07:57 2021 rev:6 rq:889724 version:1.2.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-oic/python-oic.changes    2021-02-08 
11:47:16.989695982 +0100
+++ /work/SRC/openSUSE:Factory/.python-oic.new.2988/python-oic.changes  
2021-05-03 22:08:00.260534906 +0200
@@ -1,0 +2,6 @@
+Fri Apr 30 18:17:14 UTC 2021 - Ben Greiner <[email protected]>
+
+- Update to 1.2.1
+  * Fixed several client vulnerabilities (CVE-2020-26244)
+
+-------------------------------------------------------------------

Old:
----
  oic-1.2.0.tar.gz

New:
----
  oic-1.2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-oic.spec ++++++
--- /var/tmp/diff_new_pack.oOyjvv/_old  2021-05-03 22:08:01.440529563 +0200
+++ /var/tmp/diff_new_pack.oOyjvv/_new  2021-05-03 22:08:01.444529545 +0200
@@ -20,21 +20,21 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %global modname oic
 Name:           python-oic
-Version:        1.2.0
+Version:        1.2.1
 Release:        0
 Summary:        A complete OpenID Connect implementation in Python
 License:        Apache-2.0
 Group:          Development/Languages/Python
 URL:            https://github.com/OpenIDC/pyoidc
 Source:         
https://github.com/OpenIDC/pyoidc/archive/%{version}.tar.gz#/%{modname}-%{version}.tar.gz
+BuildRequires:  %{python_module Beaker}
 BuildRequires:  %{python_module Mako}
 BuildRequires:  %{python_module cryptography}
 BuildRequires:  %{python_module dbm}
 BuildRequires:  %{python_module defusedxml}
 BuildRequires:  %{python_module freezegun}
-BuildRequires:  %{python_module future}
 BuildRequires:  %{python_module ldap}
-BuildRequires:  %{python_module mock}
+BuildRequires:  %{python_module pycryptodomex}
 BuildRequires:  %{python_module pyjwkest >= 1.3.6}
 BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module requests}
@@ -50,11 +50,9 @@
 Requires:       python-cryptography
 Requires:       python-dbm
 Requires:       python-defusedxml
-Requires:       python-future
 Requires:       python-pycryptodomex
 Requires:       python-pyjwkest >= 1.3.6
 Requires:       python-requests
-Requires:       python-setuptools
 Requires:       python-typing
 Requires:       python-typing_extensions
 Suggests:       python-ldap
@@ -69,6 +67,7 @@
 %prep
 %setup -q -n pyoidc-%{version}
 find src -type f -exec sed -i '1 {/#!/d}' {} +
+sed -i 's/--color=yes//' tox.ini
 
 %build
 %python_build

++++++ oic-1.2.0.tar.gz -> oic-1.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/CHANGELOG.md 
new/pyoidc-1.2.1/CHANGELOG.md
--- old/pyoidc-1.2.0/CHANGELOG.md       2020-02-05 09:31:26.000000000 +0100
+++ new/pyoidc-1.2.1/CHANGELOG.md       2020-12-01 11:32:26.000000000 +0100
@@ -7,6 +7,11 @@
 
 ## Unreleased
 
+## 1.2.1 [2020-12-01]
+
+### Fixed
+- Fixed several client vulnerabilities (CVE-2020-26244)
+
 ## 1.2.0 [2020-02-05]
 
 ### Fixed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/src/oic/__init__.py 
new/pyoidc-1.2.1/src/oic/__init__.py
--- old/pyoidc-1.2.0/src/oic/__init__.py        2020-02-05 09:31:26.000000000 
+0100
+++ new/pyoidc-1.2.1/src/oic/__init__.py        2020-12-01 11:32:26.000000000 
+0100
@@ -24,7 +24,7 @@
         )
 
 __author__ = "Roland Hedberg"
-__version__ = "1.2.0"
+__version__ = "1.2.1"
 
 
 OIDCONF_PATTERN = "%s/.well-known/openid-configuration"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/src/oic/oic/__init__.py 
new/pyoidc-1.2.1/src/oic/oic/__init__.py
--- old/pyoidc-1.2.0/src/oic/oic/__init__.py    2020-02-05 09:31:26.000000000 
+0100
+++ new/pyoidc-1.2.1/src/oic/oic/__init__.py    2020-12-01 11:32:26.000000000 
+0100
@@ -43,6 +43,7 @@
 from oic.oauth2.message import ErrorResponse
 from oic.oauth2.message import Message
 from oic.oauth2.message import MessageFactory
+from oic.oauth2.message import WrongSigningAlgorithm
 from oic.oauth2.util import get_or_post
 from oic.oic.message import SCOPE2CLAIMS
 from oic.oic.message import AccessTokenResponse
@@ -1399,7 +1400,13 @@
         return resp
 
     def _verify_id_token(
-        self, id_token, nonce="", acr_values=None, auth_time=0, max_age=0
+        self,
+        id_token,
+        nonce="",
+        acr_values=None,
+        auth_time=0,
+        max_age=0,
+        response_type="",
     ):
         """
         Verify IdToken.
@@ -1432,6 +1439,11 @@
         if _now > id_token["exp"]:
             raise OtherError("Passed best before date")
 
+        if response_type != ["code"] and id_token.jws_header["alg"] == "none":
+            raise WrongSigningAlgorithm(
+                "none is not allowed outside Authorization Flow."
+            )
+
         if (
             self.id_token_max_age
             and _now > int(id_token["iat"]) + self.id_token_max_age
@@ -1458,7 +1470,7 @@
         except KeyError:
             pass
 
-        for param in ["acr_values", "max_age"]:
+        for param in ["acr_values", "max_age", "response_type"]:
             try:
                 kwa[param] = authn_req[param]
             except KeyError:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/src/oic/oic/consumer.py 
new/pyoidc-1.2.1/src/oic/oic/consumer.py
--- old/pyoidc-1.2.0/src/oic/oic/consumer.py    2020-02-05 09:31:26.000000000 
+0100
+++ new/pyoidc-1.2.1/src/oic/oic/consumer.py    2020-12-01 11:32:26.000000000 
+0100
@@ -3,6 +3,8 @@
 import warnings
 from typing import Dict
 from typing import Optional
+from typing import Tuple
+from typing import Union
 
 from oic import rndstr
 from oic.exception import AuthzError
@@ -22,6 +24,7 @@
 from oic.oic.message import BackChannelLogoutRequest
 from oic.oic.message import Claims
 from oic.oic.message import ClaimsRequest
+from oic.oic.message import IdToken
 from oic.utils import http_util
 from oic.utils.sanitize import sanitize
 from oic.utils.sdb import DictSessionBackend
@@ -340,6 +343,7 @@
         if self.debug:
             _log_info("Redirecting to: %s" % location)
 
+        self.authz_req[areq["state"]] = areq
         return sid, location
 
     def _parse_authz(self, query="", **kwargs):
@@ -364,7 +368,16 @@
         self.redirect_uris = [self.sdb[_state]["redirect_uris"]]
         return aresp, _state
 
-    def parse_authz(self, query="", **kwargs):
+    def parse_authz(
+        self, query="", **kwargs
+    ) -> Union[
+        http_util.BadRequest,
+        Tuple[
+            Optional[AuthorizationResponse],
+            Optional[AccessTokenResponse],
+            Optional[IdToken],
+        ],
+    ]:
         """
         Parse authorization response from server.
 
@@ -375,17 +388,20 @@
         ["id_token"]
         ["id_token", "token"]
         ["token"]
-
-        :return: A AccessTokenResponse instance
         """
         _log_info = logger.info
         logger.debug("- authorization -")
 
+        # FIXME: This shouldn't be here... We should rather raise a sepcific 
Client error
+        # That would simplify the return value of this function
+        # and drop bunch of assertions from tests added in this commit.
         if not query:
             return http_util.BadRequest("Missing query")
 
         _log_info("response: %s" % sanitize(query))
 
+        if "algs" not in kwargs:
+            kwargs["algs"] = self.sign_enc_algs("id_token")
         if "code" in self.consumer_config["response_type"]:
             aresp, _state = self._parse_authz(query, **kwargs)
 
@@ -410,9 +426,10 @@
                 except KeyError:
                     pass
 
-            return aresp, atr, idt
         elif "token" in self.consumer_config["response_type"]:  # implicit flow
             _log_info("Expect Access Token Response")
+            aresp = None
+            _state = None
             atr = self.parse_response(
                 AccessTokenResponse,
                 info=query,
@@ -423,8 +440,8 @@
             if isinstance(atr, ErrorResponse):
                 raise TokenError(atr.get("error"), atr)
 
-            idt = None
-            return None, atr, idt
+            idt = atr.get("id_token")
+
         else:  # only id_token
             aresp, _state = self._parse_authz(query, **kwargs)
 
@@ -437,8 +454,13 @@
                     session_update(self.sso_db, _state, "smid", idt["sid"])
                 except KeyError:
                     pass
+            # Null the aresp as only id_token should be returned
+            aresp = atr = None
 
-            return None, None, idt
+        # Verify the IdToken if it was present
+        if idt is not None:
+            self.verify_id_token(idt, self.authz_req.get(_state or 
atr["state"]))
+        return aresp, atr, idt
 
     def complete(self, state):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/src/oic/oic/message.py 
new/pyoidc-1.2.1/src/oic/oic/message.py
--- old/pyoidc-1.2.0/src/oic/oic/message.py     2020-02-05 09:31:26.000000000 
+0100
+++ new/pyoidc-1.2.1/src/oic/oic/message.py     2020-12-01 11:32:26.000000000 
+0100
@@ -313,17 +313,19 @@
 
     if check_hash:
         _alg = idt.jws_header["alg"]
-        # What if _alg == 'none'
-
-        hfunc = "HS" + _alg[-3:]
+        if _alg != "none":
+            hfunc = "HS" + _alg[-3:]
+        else:
+            # This is allowed only for `code` and it needs to be checked by a 
Client
+            hfunc = None
 
-        if "access_token" in instance:
+        if "access_token" in instance and hfunc is not None:
             if "at_hash" not in idt:
                 raise MissingRequiredAttribute("Missing at_hash property", idt)
             if idt["at_hash"] != jws.left_hash(instance["access_token"], 
hfunc):
                 raise AtHashError("Failed to verify access_token hash", idt)
 
-        if "code" in instance:
+        if "code" in instance and hfunc is not None:
             if "c_hash" not in idt:
                 raise MissingRequiredAttribute("Missing c_hash property", idt)
             if idt["c_hash"] != jws.left_hash(instance["code"], hfunc):
@@ -780,6 +782,11 @@
         else:
             if (_iat + _storage_time) < (_now - _skew):
                 raise IATError("Issued too long ago")
+            if _now < (_iat - _skew):
+                raise IATError("Issued in the future")
+
+        if _exp < _iat:
+            raise EXPError("Invalid expiration time")
 
         return True
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/tests/test_oic_consumer.py 
new/pyoidc-1.2.1/tests/test_oic_consumer.py
--- old/pyoidc-1.2.0/tests/test_oic_consumer.py 2020-02-05 09:31:26.000000000 
+0100
+++ new/pyoidc-1.2.1/tests/test_oic_consumer.py 2020-12-01 11:32:26.000000000 
+0100
@@ -10,12 +10,14 @@
 from jwkest.jwk import SYMKey
 
 from oic.oauth2.message import MissingSigningKey
+from oic.oauth2.message import WrongSigningAlgorithm
 from oic.oic import DEF_SIGN_ALG
 from oic.oic import Server
 from oic.oic import response_types_to_grant_types
 from oic.oic.consumer import IGNORE
 from oic.oic.consumer import Consumer
 from oic.oic.consumer import clean_response
+from oic.oic.message import AccessTokenRequest
 from oic.oic.message import AccessTokenResponse
 from oic.oic.message import AuthorizationResponse
 from oic.oic.message import IdToken
@@ -147,6 +149,9 @@
         self.consumer.userinfo_endpoint = "https://example.com/userinfo";  # 
type: ignore
         self.consumer.client_secret = "hemlig"
         self.consumer.secret_type = "basic"
+        self.consumer.provider_info = ProviderConfigurationResponse(
+            issuer="https://example.com";
+        )  # abs min
 
     def test_backup_keys(self):
         keys = self.consumer.__dict__.keys()
@@ -326,6 +331,7 @@
         self.consumer._backup(_state)
 
         part = self.consumer.parse_authz(query=result.headers["location"])
+        assert isinstance(part, tuple)
         atr = part[0]
         assert part[1] is None
         assert part[2] is None
@@ -359,6 +365,7 @@
             )
 
         part = self.consumer.parse_authz(query=result.headers["location"])
+        assert isinstance(part, tuple)
         assert part[0] is None
         atr = part[1]
         assert part[2] is None
@@ -440,6 +447,7 @@
 
         parsed = urlparse(result.headers["location"])
         part = self.consumer.parse_authz(query=parsed.query)
+        assert isinstance(part, tuple)
         auth = part[0]
         acc = part[1]
         assert part[2] is None
@@ -456,8 +464,67 @@
         _state = "state0"
         self.consumer.consumer_config["response_type"] = ["id_token", "token"]
         self.consumer.registration_response = RegistrationResponse(
-            id_token_signed_response_alg="RS256"
+            id_token_signed_response_alg="HS256"
+        )
+        self.consumer.authz_req = {}  # Store AuthzReq with state as key
+
+        args = {
+            "client_id": self.consumer.client_id,
+            "response_type": self.consumer.consumer_config["response_type"],
+            "scope": ["openid"],
+            "nonce": "nonce",
+        }
+        token = IdToken(
+            iss="https://example.com";,
+            aud="client_1",
+            sub="some_sub",
+            exp=1565348600,
+            iat=1565348300,
+            nonce="nonce",
+        )
+        location = (
+            
"https://example.com/cb?state=state0&access_token=token&token_type=bearer&";
+            "scope=openid&id_token={}".format(
+                token.to_jwt(key=[SYMKey(key="hemlig")], algorithm="HS256")
+            )
+        )
+        with responses.RequestsMock() as rsps:
+            rsps.add(
+                responses.GET,
+                "https://example.com/authorization";,
+                status=302,
+                headers={"location": location},
+            )
+            result = self.consumer.do_authorization_request(
+                state=_state, request_args=args
+            )
+            query = parse_qs(urlparse(result.request.url).query)
+            assert query["client_id"] == ["client_1"]
+            assert query["scope"] == ["openid"]
+            assert query["response_type"] == ["id_token token"]
+            assert query["state"] == ["state0"]
+            assert query["nonce"] == ["nonce"]
+            assert query["redirect_uri"] == ["https://example.com/cb";]
+
+        parsed = urlparse(result.headers["location"])
+
+        with freeze_time("2019-08-09 11:00:00"):
+            part = self.consumer.parse_authz(query=parsed.query)
+        assert isinstance(part, tuple)
+        auth = part[0]
+        atr = part[1]
+        idt = part[2]
+
+        assert auth is None
+        assert isinstance(atr, AccessTokenResponse)
+        assert _eq(
+            atr.keys(), ["access_token", "id_token", "token_type", "state", 
"scope"]
         )
+        assert isinstance(idt, IdToken)
+
+    def test_complete_auth_token_idtoken_no_alg_config(self):
+        _state = "state0"
+        self.consumer.consumer_config["response_type"] = ["id_token", "token"]
         self.consumer.provider_info = ProviderConfigurationResponse(
             issuer="https://example.com";
         )  # abs min
@@ -480,7 +547,7 @@
         location = (
             
"https://example.com/cb?state=state0&access_token=token&token_type=bearer&";
             "scope=openid&id_token={}".format(
-                token.to_jwt(key=KC_RSA.keys(), algorithm="RS256")
+                token.to_jwt(key=[SYMKey(key="hemlig")], algorithm="HS256")
             )
         )
         with responses.RequestsMock() as rsps:
@@ -504,23 +571,196 @@
         parsed = urlparse(result.headers["location"])
 
         with freeze_time("2019-08-09 11:00:00"):
-            part = self.consumer.parse_authz(
-                query=parsed.query, 
algs=self.consumer.sign_enc_algs("id_token")
-            )
+            part = self.consumer.parse_authz(query=parsed.query, algs={"sign": 
"HS256"})
+        assert isinstance(part, tuple)
         auth = part[0]
         atr = part[1]
-        assert part[2] is None
+        idt = part[2]
 
         assert auth is None
         assert isinstance(atr, AccessTokenResponse)
         assert _eq(
             atr.keys(), ["access_token", "id_token", "token_type", "state", 
"scope"]
         )
+        assert isinstance(idt, IdToken)
+
+    def test_complete_auth_token_idtoken_none_cipher_code(self):
+        _state = "state0"
+        self.consumer.consumer_config["response_type"] = ["code"]
+        self.consumer.registration_response = RegistrationResponse(
+            id_token_signed_response_alg="none"
+        )
+        self.consumer.provider_info = ProviderConfigurationResponse(
+            issuer="https://example.com";
+        )  # abs min
+        self.consumer.authz_req = {}  # Store AuthzReq with state as key
+        self.consumer.sdb[_state] = {"redirect_uris": []}
+
+        args = {
+            "client_id": self.consumer.client_id,
+            "response_type": self.consumer.consumer_config["response_type"],
+            "scope": ["openid"],
+            "nonce": "nonce",
+        }
+        token = IdToken(
+            iss="https://example.com";,
+            aud="client_1",
+            sub="some_sub",
+            exp=1565348600,
+            iat=1565348300,
+            nonce="nonce",
+            at_hash="aaa",
+        )
+        # Downgrade the algorithm to `none`
+        location = (
+            
"https://example.com/cb?state=state0&access_token=token&token_type=bearer&";
+            "scope=openid&id_token={}".format(
+                token.to_jwt(key=KC_RSA.keys(), algorithm="none")
+            )
+        )
+        with responses.RequestsMock() as rsps:
+            rsps.add(
+                responses.GET,
+                "https://example.com/authorization";,
+                status=302,
+                headers={"location": location},
+            )
+            result = self.consumer.do_authorization_request(
+                state=_state, request_args=args
+            )
+            query = parse_qs(urlparse(result.request.url).query)
+            assert query["client_id"] == ["client_1"]
+            assert query["scope"] == ["openid"]
+            assert query["response_type"] == ["code"]
+            assert query["state"] == ["state0"]
+            assert query["nonce"] == ["nonce"]
+            assert query["redirect_uri"] == ["https://example.com/cb";]
+
+        parsed = urlparse(result.headers["location"])
+
+        with freeze_time("2019-08-09 11:00:00"):
+            part = self.consumer.parse_authz(query=parsed.query)
+        assert isinstance(part, tuple)
+        auth = part[0]
+        atr = part[1]
+        idt = part[2]
+
+        assert isinstance(auth, AuthorizationResponse)
+        assert isinstance(atr, AccessTokenResponse)
+        assert _eq(
+            atr.keys(), ["access_token", "id_token", "token_type", "state", 
"scope"]
+        )
+        assert isinstance(idt, IdToken)
+
+    def test_complete_auth_token_idtoken_none_cipher_token(self):
+        _state = "state0"
+        self.consumer.consumer_config["response_type"] = ["token"]
+        self.consumer.registration_response = RegistrationResponse(
+            id_token_signed_response_alg="none"
+        )
+        self.consumer.provider_info = ProviderConfigurationResponse(
+            issuer="https://example.com";
+        )  # abs min
+        self.consumer.authz_req = {}  # Store AuthzReq with state as key
+        self.consumer.sdb[_state] = {"redirect_uris": []}
+
+        args = {
+            "client_id": self.consumer.client_id,
+            "response_type": self.consumer.consumer_config["response_type"],
+            "scope": ["openid"],
+            "nonce": "nonce",
+        }
+        token = IdToken(
+            iss="https://example.com";,
+            aud="client_1",
+            sub="some_sub",
+            exp=1565348600,
+            iat=1565348300,
+            nonce="nonce",
+        )
+        # Downgrade the algorithm to `none`
+        location = (
+            
"https://example.com/cb?state=state0&access_token=token&token_type=bearer&";
+            "scope=openid&id_token={}".format(
+                token.to_jwt(key=KC_RSA.keys(), algorithm="none")
+            )
+        )
+        with responses.RequestsMock() as rsps:
+            rsps.add(
+                responses.GET,
+                "https://example.com/authorization";,
+                status=302,
+                headers={"location": location},
+            )
+            result = self.consumer.do_authorization_request(
+                state=_state, request_args=args
+            )
+            query = parse_qs(urlparse(result.request.url).query)
+            assert query["client_id"] == ["client_1"]
+            assert query["scope"] == ["openid"]
+            assert query["response_type"] == ["token"]
+            assert query["state"] == ["state0"]
+            assert query["nonce"] == ["nonce"]
+            assert query["redirect_uri"] == ["https://example.com/cb";]
+
+        parsed = urlparse(result.headers["location"])
 
         with freeze_time("2019-08-09 11:00:00"):
-            self.consumer.verify_id_token(
-                atr["id_token"], self.consumer.authz_req[atr["state"]]
+            with pytest.raises(WrongSigningAlgorithm):
+                self.consumer.parse_authz(query=parsed.query)
+
+    def test_complete_auth_token_idtoken_cipher_downgrade(self):
+        _state = "state0"
+        self.consumer.consumer_config["response_type"] = ["id_token", "token"]
+        self.consumer.provider_info = ProviderConfigurationResponse(
+            issuer="https://example.com";
+        )  # abs min
+        self.consumer.authz_req = {}  # Store AuthzReq with state as key
+
+        args = {
+            "client_id": self.consumer.client_id,
+            "response_type": self.consumer.consumer_config["response_type"],
+            "scope": ["openid"],
+            "nonce": "nonce",
+        }
+        token = IdToken(
+            iss="https://example.com";,
+            aud="client_1",
+            sub="some_sub",
+            exp=1565348600,
+            iat=1565348300,
+            nonce="nonce",
+        )
+        # Downgrade the algorithm to `none`
+        location = (
+            
"https://example.com/cb?state=state0&access_token=token&token_type=bearer&";
+            "scope=openid&id_token={}".format(
+                token.to_jwt(key=KC_RSA.keys(), algorithm="none")
+            )
+        )
+        with responses.RequestsMock() as rsps:
+            rsps.add(
+                responses.GET,
+                "https://example.com/authorization";,
+                status=302,
+                headers={"location": location},
+            )
+            result = self.consumer.do_authorization_request(
+                state=_state, request_args=args
             )
+            query = parse_qs(urlparse(result.request.url).query)
+            assert query["client_id"] == ["client_1"]
+            assert query["scope"] == ["openid"]
+            assert query["response_type"] == ["id_token token"]
+            assert query["state"] == ["state0"]
+            assert query["nonce"] == ["nonce"]
+            assert query["redirect_uri"] == ["https://example.com/cb";]
+
+        parsed = urlparse(result.headers["location"])
+
+        with freeze_time("2019-08-09 11:00:00"):
+            with pytest.raises(WrongSigningAlgorithm):
+                self.consumer.parse_authz(query=parsed.query)
 
     def test_userinfo(self):
         _state = "state0"
@@ -920,6 +1160,7 @@
         self.consumer.sdb[_state] = {"redirect_uris": 
["https://example.org/cb"]}
         resp = AuthorizationResponse(id_token=_signed_jwt, state=_state)
         self.consumer.consumer_config["response_type"] = ["id_token"]
+        self.consumer.authz_req[_state] = 
AccessTokenRequest(nonce="KUEYfRM2VzKDaaKD")
         self.consumer.parse_authz(resp.to_urlencoded())
         assert self.consumer.sso_db["state"]["smid"] == smid
         assert session_get(self.consumer.sso_db, "smid", smid) == [_state]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/tests/test_oic_consumer_logout.py 
new/pyoidc-1.2.1/tests/test_oic_consumer_logout.py
--- old/pyoidc-1.2.0/tests/test_oic_consumer_logout.py  2020-02-05 
09:31:26.000000000 +0100
+++ new/pyoidc-1.2.1/tests/test_oic_consumer_logout.py  2020-12-01 
11:32:26.000000000 +0100
@@ -159,13 +159,16 @@
             "openid", "code", path="https://example.com";
         )
         resp = self.provider.authorization_endpoint(request=request_location)
-        aresp = self.consumer.parse_authz(resp.message)
+        part = self.consumer.parse_authz(resp.message)
+        assert isinstance(part, tuple)
+        aresp = part[0]
+        assert aresp
 
         assert self.consumer.sdb[sid]["issuer"] == self.provider.baseurl
 
         # Simulate an accesstoken request
         areq = AccessTokenRequest(
-            code=aresp[0]["code"],
+            code=aresp["code"],
             client_id=CLIENT_ID,
             redirect_uri="http://example.com/authz";,
             client_secret=self.consumer.client_secret,
@@ -228,13 +231,16 @@
             "openid", "code", path="https://example.com";
         )
         resp = self.provider.authorization_endpoint(request=request_location)
-        aresp = self.consumer.parse_authz(resp.message)
+        part = self.consumer.parse_authz(resp.message)
+        assert isinstance(part, tuple)
+        aresp = part[0]
+        assert aresp
 
         assert self.consumer.sdb[sid]["issuer"] == self.provider.baseurl
 
         # Simulate an accesstoken request
         areq = AccessTokenRequest(
-            code=aresp[0]["code"],
+            code=aresp["code"],
             client_id=CLIENT_ID,
             redirect_uri="http://example.com/authz";,
             client_secret=self.consumer.client_secret,
@@ -308,13 +314,16 @@
             "openid", "code", path="https://example.com";
         )
         resp = self.provider.authorization_endpoint(request=request_location)
-        aresp = _consumer.parse_authz(resp.message)
+        part = _consumer.parse_authz(resp.message)
+        assert isinstance(part, tuple)
+        aresp = part[0]
+        assert aresp
 
         assert _consumer.sdb[sid]["issuer"] == self.provider.baseurl
 
         # Simulate an accesstoken request
         areq = AccessTokenRequest(
-            code=aresp[0]["code"],
+            code=aresp["code"],
             client_id=CLIENT_ID,
             redirect_uri="http://example.com/authz";,
             client_secret=_consumer.client_secret,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/tests/test_oic_message.py 
new/pyoidc-1.2.1/tests/test_oic_message.py
--- old/pyoidc-1.2.0/tests/test_oic_message.py  2020-02-05 09:31:26.000000000 
+0100
+++ new/pyoidc-1.2.1/tests/test_oic_message.py  2020-12-01 11:32:26.000000000 
+0100
@@ -5,6 +5,7 @@
 from urllib.parse import urlencode
 
 import pytest
+from freezegun import freeze_time
 from jwkest import BadSignature
 from jwkest.jwk import SYMKey
 from jwkest.jws import left_hash
@@ -24,7 +25,9 @@
 from oic.oic.message import BackChannelLogoutRequest
 from oic.oic.message import CHashError
 from oic.oic.message import Claims
+from oic.oic.message import EXPError
 from oic.oic.message import FrontChannelLogoutRequest
+from oic.oic.message import IATError
 from oic.oic.message import IdToken
 from oic.oic.message import LogoutToken
 from oic.oic.message import OpenIDSchema
@@ -609,6 +612,69 @@
             at.verify()
 
 
+class TestIdToken(object):
+    """Unittests for IdToken class."""
+
+    @freeze_time("2020-01-01 11:00:00")
+    def test_verify_iat_in_future(self):
+        now = time_util.utc_time_sans_frac()
+
+        idt = IdToken(
+            **{
+                "sub": "553df2bcf909104751cfd8b2",
+                "aud": ["5542958437706128204e0000", 
"554295ce3770612820620000"],
+                "auth_time": 1441364872,
+                "azp": "554295ce3770612820620000",
+                "at_hash": "L4Ign7TCAD_EppRbHAuCyw",
+                "iat": now + 7200,
+                "exp": now + 3600,
+                "iss": "https://sso.qa.7pass.ctf.prosiebensat1.com";,
+            }
+        )
+
+        with pytest.raises(IATError):
+            idt.verify()
+
+    @freeze_time("2020-01-01 11:00:00")
+    def test_verify_iat_in_future_expired(self):
+        now = time_util.utc_time_sans_frac()
+
+        idt = IdToken(
+            **{
+                "sub": "553df2bcf909104751cfd8b2",
+                "aud": ["5542958437706128204e0000", 
"554295ce3770612820620000"],
+                "auth_time": 1441364872,
+                "azp": "554295ce3770612820620000",
+                "at_hash": "L4Ign7TCAD_EppRbHAuCyw",
+                "iat": now + 3600,
+                "exp": now,
+                "iss": "https://sso.qa.7pass.ctf.prosiebensat1.com";,
+            }
+        )
+
+        with pytest.raises(EXPError):
+            idt.verify(skew=7200)
+
+    @freeze_time("2020-01-01 11:00:00")
+    def test_verify_iat_in_future_skew(self):
+        now = time_util.utc_time_sans_frac()
+
+        idt = IdToken(
+            **{
+                "sub": "553df2bcf909104751cfd8b2",
+                "aud": ["5542958437706128204e0000", 
"554295ce3770612820620000"],
+                "auth_time": 1441364872,
+                "azp": "554295ce3770612820620000",
+                "at_hash": "L4Ign7TCAD_EppRbHAuCyw",
+                "iat": now + 7200,
+                "exp": now + 7600,
+                "iss": "https://sso.qa.7pass.ctf.prosiebensat1.com";,
+            }
+        )
+
+        idt.verify(skew=7200)
+
+
 def test_id_token():
     _now = time_util.utc_time_sans_frac()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pyoidc-1.2.0/tests/test_oic_provider.py 
new/pyoidc-1.2.1/tests/test_oic_provider.py
--- old/pyoidc-1.2.0/tests/test_oic_provider.py 2020-02-05 09:31:26.000000000 
+0100
+++ new/pyoidc-1.2.1/tests/test_oic_provider.py 2020-12-01 11:32:26.000000000 
+0100
@@ -38,6 +38,7 @@
 from oic.oic.message import IdToken
 from oic.oic.message import Message
 from oic.oic.message import OpenIDSchema
+from oic.oic.message import ProviderConfigurationResponse
 from oic.oic.message import RefreshAccessTokenRequest
 from oic.oic.message import RegistrationRequest
 from oic.oic.message import RegistrationResponse
@@ -224,6 +225,9 @@
         self.cons.keyjar.import_jwks(
             self.provider.keyjar.export_jwks(), self.cons.issuer
         )
+        self.cons.provider_info = ProviderConfigurationResponse(
+            issuer=SERVER_INFO["issuer"]
+        )
 
         self.cons2 = Consumer(
             {}, CONSUMER_CONFIG.copy(), CLIENT_CONFIG_2, 
server_info=SERVER_INFO
@@ -373,6 +377,7 @@
 
         part = self.cons.parse_authz(query=resp.message)
 
+        assert isinstance(part, tuple)
         aresp = part[0]
         assert part[1] is None
         assert part[2] is None
@@ -410,9 +415,10 @@
 
         part = self.cons.parse_authz(resp.message)
 
+        assert isinstance(part, tuple)
         aresp = part[0]
         assert part[1] is None
-        assert part[2] is not None
+        id_token = part[2]
 
         assert isinstance(aresp, AuthorizationResponse)
         assert _eq(aresp.keys(), ["scope", "state", "id_token", "client_id", 
"code"])
@@ -421,7 +427,6 @@
             self.cons.grant[_state].keys(),
             ["code", "id_token", "tokens", "exp_in", "grant_expiration_time", 
"seed"],
         )
-        id_token = part[2]
         assert isinstance(id_token, IdToken)
         assert _eq(
             id_token.keys(),

Reply via email to