@@ -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 @@ def setup_consumer(self, session_db_factory):
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 @@ def test_parse_authz(self):
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 @@ def test_parse_authz_implicit(self):
)

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 @@ def test_complete_auth_token(self):

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 @@ def test_complete_auth_token_idtoken(self):
_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 @@ def test_complete_auth_token_idtoken(self):
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 @@ def test_complete_auth_token_idtoken(self):
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 @@ def test_get_session_management_id(self):
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]
@@ -159,13 +159,16 @@ def test_logout_with_sub(self):
"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 @@ def test_logout_without_sub(self):
"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 @@ def test_sso_db_dict(self):
"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,
@@ -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 @@ def test_token_type(self):
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()

@@ -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 @@ def create_provider(self, session_db_factory):
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 @@ def test_authenticated(self):

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 @@ def test_authenticated_hybrid(self):

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 @@ def test_authenticated_hybrid(self):
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(),