Skip to content

Commit

Permalink
Call verify_id_token in parse_authz to pass already verified token
Browse files Browse the repository at this point in the history
  • Loading branch information
tpazderka committed Dec 1, 2020
1 parent fb0ff05 commit b195d09
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 24 deletions.
34 changes: 27 additions & 7 deletions src/oic/oic/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -340,6 +343,7 @@ def begin(self, scope="", response_type="", use_nonce=False, path="", **kwargs):
if self.debug:
_log_info("Redirecting to: %s" % location)

self.authz_req[areq["state"]] = areq
return sid, location

def _parse_authz(self, query="", **kwargs):
Expand All @@ -364,7 +368,16 @@ def _parse_authz(self, query="", **kwargs):
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.
Expand All @@ -375,12 +388,13 @@ def parse_authz(self, query="", **kwargs):
["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")

Expand Down Expand Up @@ -410,9 +424,10 @@ def parse_authz(self, query="", **kwargs):
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,
Expand All @@ -423,8 +438,8 @@ def parse_authz(self, query="", **kwargs):
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)

Expand All @@ -437,8 +452,13 @@ def parse_authz(self, query="", **kwargs):
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):
"""
Expand Down
21 changes: 12 additions & 9 deletions tests/test_oic_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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
Expand Down Expand Up @@ -147,6 +148,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()
Expand Down Expand Up @@ -326,6 +330,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
Expand Down Expand Up @@ -359,6 +364,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
Expand Down Expand Up @@ -440,6 +446,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
Expand All @@ -458,9 +465,6 @@ def test_complete_auth_token_idtoken(self):
self.consumer.registration_response = RegistrationResponse(
id_token_signed_response_alg="RS256"
)
self.consumer.provider_info = ProviderConfigurationResponse(
issuer="https://example.com"
) # abs min
self.consumer.authz_req = {} # Store AuthzReq with state as key

args = {
Expand Down Expand Up @@ -507,20 +511,18 @@ def test_complete_auth_token_idtoken(self):
part = self.consumer.parse_authz(
query=parsed.query, algs=self.consumer.sign_enc_algs("id_token")
)
assert isinstance(part, tuple)
auth = part[0]
atr = part[1]
assert part[2] is None
idt = part[2]

assert isinstance(part, tuple)
assert auth is None
assert isinstance(atr, AccessTokenResponse)
assert _eq(
atr.keys(), ["access_token", "id_token", "token_type", "state", "scope"]
)

with freeze_time("2019-08-09 11:00:00"):
self.consumer.verify_id_token(
atr["id_token"], self.consumer.authz_req[atr["state"]]
)
assert isinstance(idt, IdToken)

def test_userinfo(self):
_state = "state0"
Expand Down Expand Up @@ -920,6 +922,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]
21 changes: 15 additions & 6 deletions tests/test_oic_consumer_logout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 7 additions & 2 deletions tests/test_oic_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"])
Expand All @@ -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(),
Expand Down

0 comments on commit b195d09

Please sign in to comment.