def test_verify_id_token_at_hash_and_chash(): token = 'AccessTokenWhichCouldBeASignedJWT' at_hash = left_hash(token) code = 'AccessCode1' c_hash = left_hash(code) idt = IdToken(**{ "sub": "553df2bcf909104751cfd8b2", "aud": [ "5542958437706128204e0000", "554295ce3770612820620000" ], "auth_time": 1441364872, "azp": "554295ce3770612820620000", "at_hash": at_hash, 'c_hash': c_hash }) kj = KeyJar() kj.add_symmetric("", 'dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ', ['sig']) kj.add_symmetric("https://sso.qa.7pass.ctf.prosiebensat1.com", 'dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ', ['sig']) packer = JWT(kj, sign_alg='HS256', iss="https://sso.qa.7pass.ctf.prosiebensat1.com", lifetime=3600) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(access_token=token, id_token=_jws, code=code) verify_id_token(msg, check_hash=True, keyjar=kj, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="554295ce3770612820620000")
def verify_id_token(instance, check_hash=False, **kwargs): # Try to decode the JWT, checks the signature args = {} for arg in ["key", "keyjar", "algs", "sender"]: try: args[arg] = kwargs[arg] except KeyError: pass _jws = str(instance["id_token"]) # It can be encrypted, so try to decrypt first _jwe = JWE_factory(_jws) if _jwe is not None: try: _jws = _jwe.decrypt(keys=kwargs["keyjar"].get_decrypt_key()) except JWEException as err: raise VerificationError("Could not decrypt id_token", err) _packer = JWT() _body = _packer.unpack(_jws).payload() if "keyjar" in kwargs: try: if _body["iss"] not in kwargs["keyjar"]: raise ValueError("Unknown issuer") except KeyError: raise MissingRequiredAttribute("iss") if _jwe is not None: # Use the original encrypted token to set correct headers idt = IdToken().from_jwt(str(instance["id_token"]), **args) else: idt = IdToken().from_jwt(_jws, **args) if not idt.verify(**kwargs): raise VerificationError("Could not verify id_token", idt) if check_hash: _alg = idt.jws_header["alg"] # What if _alg == 'none' hfunc = "HS" + _alg[-3:] if "access_token" in instance: 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 "c_hash" not in idt: raise MissingRequiredAttribute("Missing c_hash property", idt) if idt["c_hash"] != jws.left_hash(instance["code"], hfunc): raise CHashError("Failed to verify code hash", idt) return idt
def verify_id_token(instance, check_hash=False, **kwargs): # Try to decode the JWT, checks the signature args = {} for arg in ["key", "keyjar", "algs", "sender"]: try: args[arg] = kwargs[arg] except KeyError: pass _jws = str(instance["id_token"]) # It can be encrypted, so try to decrypt first _jwe = JWE_factory(_jws) if _jwe is not None: try: _jws = _jwe.decrypt(keys=kwargs['keyjar'].get_decrypt_key()) except JWEException as err: raise VerificationError("Could not decrypt id_token", err) _packer = JWT() _body = _packer.unpack(_jws).payload() if 'keyjar' in kwargs: try: if _body['iss'] not in kwargs['keyjar']: raise ValueError('Unknown issuer') except KeyError: raise MissingRequiredAttribute('iss') if _jwe is not None: # Use the original encrypted token to set correct headers idt = IdToken().from_jwt(str(instance['id_token']), **args) else: idt = IdToken().from_jwt(_jws, **args) if not idt.verify(**kwargs): raise VerificationError("Could not verify id_token", idt) if check_hash: _alg = idt.jws_header["alg"] # What if _alg == 'none' hfunc = "HS" + _alg[-3:] if "access_token" in instance: 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 "c_hash" not in idt: raise MissingRequiredAttribute("Missing c_hash property", idt) if idt["c_hash"] != jws.left_hash(instance["code"], hfunc): raise CHashError("Failed to verify code hash", idt) return idt
def _create_signed_id_token( self, client_id, # type: str sub, # type: str user_claims=None, # type: Optional[Mapping[str, Union[str, List[str]]]] nonce=None, # type: Optional[str] authorization_code=None, # type: Optional[str] access_token_value=None, # type: Optional[str] extra_id_token_claims=None ): # type: Optional[Mappings[str, Union[str, List[str]]]] # type: (...) -> str """ Creates a signed ID Token. :param client_id: who the ID Token is intended for :param sub: who the ID Token is regarding :param user_claims: any claims about the user to be included :param nonce: nonce from the authentication request :param authorization_code: the authorization code issued together with this ID Token :param access_token_value: the access token issued together with this ID Token :param extra_id_token_claims: any extra claims that should be included in the ID Token :return: a JWS, containing the ID Token as payload """ alg = self.clients[client_id].get( 'id_token_signed_response_alg', self.configuration_information[ 'id_token_signing_alg_values_supported'][0]) args = {} hash_alg = 'HS{}'.format(alg[-3:]) if authorization_code: args['c_hash'] = jws.left_hash(authorization_code.encode('utf-8'), hash_alg) if access_token_value: args['at_hash'] = jws.left_hash(access_token_value.encode('utf-8'), hash_alg) if user_claims: args.update(user_claims) if extra_id_token_claims: args.update(extra_id_token_claims) id_token = IdToken(iss=self.configuration_information['issuer'], sub=sub, aud=client_id, iat=int(time.time()), exp=int(time.time()) + self.id_token_lifetime, **args) if nonce: id_token['nonce'] = nonce logger.debug('signed id_token with kid=%s using alg=%s', self.signing_key, alg) return id_token.to_jwt([self.signing_key], alg)
def validate_access_token(alg, expected_hash, access_token): if alg == None: raise OAuth2ProtocolError("No hash algorithm specified") if alg.lower() == 'rs256': hash = jws.left_hash(access_token) else: hash = jws.left_hash(access_token, alg) if hash == None: raise OAuth2Exception("Hash failed, alg = '" + alg + "', token is '" + access_token + "'") if hash != expected_hash: raise OAuth2Exception("Bad hash value in access token")
def verify(self, **kwargs): super(AuthorizationResponse, self).verify(**kwargs) if "aud" in self: if "client_id" in kwargs: # check that it's for me if kwargs["client_id"] not in self["aud"]: return False if "id_token" in self: # Try to decode the JWT, checks the signature args = {} for arg in ["key", "keyjar", "algs", "sender"]: try: args[arg] = kwargs[arg] except KeyError: pass idt = IdToken().from_jwt(str(self["id_token"]), **args) if not idt.verify(**kwargs): raise VerificationError("Could not verify id_token", idt) _alg = idt.jws_header["alg"] # What if _alg == 'none' hfunc = "HS" + _alg[-3:] if "access_token" in self: try: assert "at_hash" in idt except AssertionError: raise MissingRequiredAttribute("Missing at_hash property", idt) try: assert idt["at_hash"] == jws.left_hash( self["access_token"], hfunc) except AssertionError: raise AtHashError( "Failed to verify access_token hash", idt) if "code" in self: try: assert "c_hash" in idt except AssertionError: raise MissingRequiredAttribute("Missing c_hash property", idt) try: assert idt["c_hash"] == jws.left_hash(self["code"], hfunc) except AssertionError: raise CHashError("Failed to verify code hash", idt) self["id_token"] = idt return True
def verify(self, **kwargs): super(AuthorizationResponse, self).verify(**kwargs) if "aud" in self: if "client_id" in kwargs: # check that it's for me if kwargs["client_id"] not in self["aud"]: return False if "id_token" in self: # Try to decode the JWT, checks the signature args = {} for arg in ["key", "keyjar", "algs", "sender"]: try: args[arg] = kwargs[arg] except KeyError: pass idt = IdToken().from_jwt(str(self["id_token"]), **args) if not idt.verify(**kwargs): raise VerificationError("Could not verify id_token", idt) _alg = idt.jws_header["alg"] # What if _alg == 'none' hfunc = "HS" + _alg[-3:] if "access_token" in self: try: assert "at_hash" in idt except AssertionError: raise MissingRequiredAttribute("Missing at_hash property", idt) try: assert idt["at_hash"] == jws.left_hash( self["access_token"], hfunc) except AssertionError: raise AtHashError("Failed to verify access_token hash", idt) if "code" in self: try: assert "c_hash" in idt except AssertionError: raise MissingRequiredAttribute("Missing c_hash property", idt) try: assert idt["c_hash"] == jws.left_hash(self["code"], hfunc) except AssertionError: raise CHashError("Failed to verify code hash", idt) self["id_token"] = idt return True
def test_make_id_token(self): self.srv.keyjar["http://oic.example/rp"] = KC_RSA session = {"sub": "user0", "client_id": "http://oic.example/rp"} issuer = "http://oic.example/idp" code = "abcdefghijklmnop" _idt = self.srv.make_id_token(session, loa="2", issuer=issuer, code=code, access_token="access_token") algo = "RS256" ckey = self.srv.keyjar.get_signing_key(alg2keytype(algo), session["client_id"]) _signed_jwt = _idt.to_jwt(key=ckey, algorithm="RS256") idt = IdToken().from_jwt(_signed_jwt, keyjar=self.srv.keyjar) _jwt = JWT().unpack(_signed_jwt) lha = left_hash(code.encode("utf-8"), func="HS" + _jwt.headers["alg"][-3:]) assert lha == idt["c_hash"] atr = AccessTokenResponse(id_token=_signed_jwt, access_token="access_token", token_type="Bearer") atr["code"] = code assert atr.verify(keyjar=self.srv.keyjar)
def test_make_id_token(): srv = Server() srv.keyjar = KEYJ srv.keyjar["http://oic.example/rp"] = KC_RSA session = {"sub": "user0", "client_id": "http://oic.example/rp"} issuer = "http://oic.example/idp" code = "abcdefghijklmnop" _idt = srv.make_id_token(session, loa="2", issuer=issuer, code=code, access_token="access_token") algo = "RS256" ckey = srv.keyjar.get_signing_key(alg2keytype(algo), session["client_id"]) _signed_jwt = _idt.to_jwt(key=ckey, algorithm="RS256") idt = IdToken().from_jwt(_signed_jwt, keyjar=srv.keyjar) print idt header = unpack(_signed_jwt) lha = left_hash(code, func="HS" + header[0]["alg"][-3:]) assert lha == idt["c_hash"] atr = AccessTokenResponse(id_token=_signed_jwt, access_token="access_token", token_type="Bearer") atr["code"] = code assert atr.verify(keyjar=srv.keyjar)
def test_verify_id_token_at_hash_fail(): token = 'AccessTokenWhichCouldBeASignedJWT' token2 = 'ACompletelyOtherAccessToken' lhsh = left_hash(token) idt = IdToken(**{ "sub": "553df2bcf909104751cfd8b2", "aud": [ "5542958437706128204e0000", "554295ce3770612820620000" ], "auth_time": 1441364872, "azp": "554295ce3770612820620000", "at_hash": lhsh }) kj = KeyJar() kj.add_symmetric("", 'dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ', ['sig']) kj.add_symmetric("https://sso.qa.7pass.ctf.prosiebensat1.com", 'dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ', ['sig']) packer = JWT(kj, sign_alg='HS256', iss="https://sso.qa.7pass.ctf.prosiebensat1.com", lifetime=3600) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(access_token=token2, id_token=_jws) with pytest.raises(AtHashError): verify_id_token(msg, check_hash=True, keyjar=kj, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="554295ce3770612820620000")
def test_verify_id_token_at_hash(): token = "AccessTokenWhichCouldBeASignedJWT" lhsh = left_hash(token) idt = IdToken( **{ "sub": "553df2bcf909104751cfd8b2", "aud": ["5542958437706128204e0000", "554295ce3770612820620000"], "auth_time": 1441364872, "azp": "554295ce3770612820620000", "at_hash": lhsh, }) kj = KeyJar() kj.add_symmetric("", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"]) kj.add_symmetric( "https://sso.qa.7pass.ctf.prosiebensat1.com", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"], ) packer = JWT( kj, sign_alg="HS256", iss="https://sso.qa.7pass.ctf.prosiebensat1.com", lifetime=3600, ) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(access_token=token, id_token=_jws) verify_id_token( msg, check_hash=True, keyjar=kj, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="554295ce3770612820620000", )
def test_handle_implicit_authentication_response(self, time_mock, utc_time_sans_frac_mock): # freeze time since ID Token validation includes expiration timestamps timestamp = time.mktime(datetime(2017, 1, 1).timetuple()) time_mock.return_value = timestamp utc_time_sans_frac_mock.return_value = int(timestamp) # mock auth response access_token = 'test_access_token' user_id = 'user1' exp_time = 10 nonce = 'test_nonce' id_token_claims = { 'iss': self.PROVIDER_BASEURL, 'aud': [self.CLIENT_ID], 'sub': user_id, 'exp': int(timestamp) + exp_time, 'iat': int(timestamp), 'nonce': nonce, 'at_hash': jws.left_hash(access_token) } id_token_jwt, id_token_signing_key = signed_id_token(id_token_claims) responses.add(responses.GET, self.PROVIDER_BASEURL + '/jwks', json={'keys': [id_token_signing_key.serialize()]}) # mock userinfo response userinfo = {'sub': user_id, 'name': 'Test User'} userinfo_endpoint = self.PROVIDER_BASEURL + '/userinfo' responses.add(responses.GET, userinfo_endpoint, json=userinfo) authn = self.init_app( provider_metadata_extras={'userinfo_endpoint': userinfo_endpoint}) state = 'test_state' auth_response = AuthorizationResponse( **{ 'state': state, 'access_token': access_token, 'token_type': 'Bearer', 'id_token': id_token_jwt }) with self.app.test_request_context('/redirect_uri?{}'.format( auth_response.to_urlencoded())): UserSession(flask.session, self.PROVIDER_NAME) flask.session['destination'] = '/' flask.session['auth_request'] = json.dumps({ 'state': state, 'nonce': nonce }) authn._handle_authentication_response() session = UserSession(flask.session) assert session.access_token == access_token assert session.id_token == id_token_claims assert session.id_token_jwt == id_token_jwt assert session.userinfo == userinfo
def val_hash(self, alg): halg = "HS%s" % alg[-3:] for attr, hash_attr in self.hashable.items(): try: self[hash_attr] = jws.left_hash(as_unicode(self[attr]), halg) except KeyError: pass else: del self[attr]
def test_verify_id_token_c_hash_fail(): code = "AccessCode1" lhsh = left_hash(code) idt = IdToken( **{ "sub": "553df2bcf909104751cfd8b2", "aud": ["5542958437706128204e0000", "554295ce3770612820620000"], "auth_time": 1441364872, "azp": "554295ce3770612820620000", "c_hash": lhsh, } ) kj = KeyJar() kj.add_symmetric("", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"]) kj.add_symmetric( "https://sso.qa.7pass.ctf.prosiebensat1.com", "dYMmrcQksKaPkhdgRNYk3zzh5l7ewdDJ", ["sig"], ) packer = JWT( kj, sign_alg="HS256", iss="https://sso.qa.7pass.ctf.prosiebensat1.com", lifetime=3600, ) _jws = packer.pack(**idt.to_dict()) msg = AuthorizationResponse(code="AccessCode289", id_token=_jws) with pytest.raises(CHashError): verify_id_token( msg, check_hash=True, keyjar=kj, iss="https://sso.qa.7pass.ctf.prosiebensat1.com", client_id="554295ce3770612820620000", )
def make_id_token( self, session, loa="2", issuer="", alg="RS256", code=None, access_token=None, user_info=None, auth_time=0, exp=None, extra_claims=None, ): """ :param session: Session information :param loa: Level of Assurance/Authentication context :param issuer: My identifier :param alg: Which signing algorithm to use for the IdToken :param code: Access grant :param access_token: Access Token :param user_info: If user info are to be part of the IdToken :return: IDToken instance """ # defaults if exp is None: inawhile = {"days": 1} else: inawhile = exp # Handle the idtoken_claims extra = {} itc = self.id_token_claims(session) if itc: try: inawhile = {"seconds": itc["max_age"]} except KeyError: pass for key, val in itc.items(): if key == "auth_time": extra["auth_time"] = auth_time elif key == "acr": # ["2","http://id.incommon.org/assurance/bronze"] extra["acr"] = verify_acr_level(val, loa) else: if auth_time: extra["auth_time"] = auth_time if loa: extra["acr"] = loa if not user_info: _args = {} else: try: _args = user_info.to_dict() except AttributeError: _args = user_info # Make sure that there are no name clashes for key in ["iss", "sub", "aud", "exp", "acr", "nonce", "auth_time"]: try: del _args[key] except KeyError: pass halg = "HS%s" % alg[-3:] if extra_claims is not None: _args.update(extra_claims) if code: _args["c_hash"] = jws.left_hash(code.encode("utf-8"), halg) if access_token: _args["at_hash"] = jws.left_hash(access_token.encode("utf-8"), halg) # Should better be done elsewhere if not issuer.endswith("/"): issuer += "/" idt = IdToken( iss=issuer, sub=session["sub"], aud=session["client_id"], exp=time_util.epoch_in_a_while(**inawhile), acr=loa, iat=time_util.utc_time_sans_frac(), **_args ) for key, val in extra.items(): idt[key] = val if "nonce" in session: idt["nonce"] = session["nonce"] return idt
def test_left_hash_hs512(): hsh = jws.left_hash("Please take a moment to register today", "HS512") assert hsh == "_h6feWLt8zbYcOFnaBmekTzMJYEHdVTaXlDgJSWsEeY"
def test_left_hash_hs256(): hsh = jws.left_hash("Please take a moment to register today") assert hsh == "rCFHVJuxTqRxOsn2IUzgvA"
def make_id_token(self, session, loa="2", issuer="", alg="RS256", code=None, access_token=None, user_info=None, auth_time=0): """ :param session: Session information :param loa: Level of Assurance/Authentication context :param issuer: My identifier :param alg: Which signing algorithm to use for the IdToken :param code: Access grant :param access_token: Access Token :param user_info: If user info are to be part of the IdToken :return: IDToken instance """ #defaults inawhile = {"days": 1} # Handle the idtoken_claims extra = {} itc = self.id_token_claims(session) if itc: try: inawhile = {"seconds": itc["max_age"]} except KeyError: inawhile = {} for key, val in itc.items(): if key == "auth_time": extra["auth_time"] = auth_time elif key == "acr": #["2","http://id.incommon.org/assurance/bronze"] extra["acr"] = verify_acr_level(val, loa) if user_info is None: _args = {} else: try: _args = user_info.to_dict() except AttributeError: _args = user_info # Make sure that there are no name clashes for key in ["iss", "sub", "aud", "exp", "acr", "nonce", "auth_time"]: try: del _args[key] except KeyError: pass halg = "HS%s" % alg[-3:] if code: _args["c_hash"] = jws.left_hash(code, halg) if access_token: _args["at_hash"] = jws.left_hash(access_token, halg) idt = IdToken(iss=issuer, sub=session["sub"], aud=session["client_id"], exp=time_util.epoch_in_a_while(**inawhile), acr=loa, iat=time_util.utc_now(), **_args) for key, val in extra.items(): idt[key] = val if "nonce" in session: idt["nonce"] = session["nonce"] return idt
def test_left_hash_hs512(): hsh = jws.left_hash('Please take a moment to register today', "HS512") assert hsh == '_h6feWLt8zbYcOFnaBmekTzMJYEHdVTaXlDgJSWsEeY'
def make_id_token(self, session, loa="2", issuer="", alg="RS256", code=None, access_token=None, user_info=None): """ :param session: Session information :param loa: Level of Assurance/Authentication context :param issuer: My identifier :param alg: Which signing algorithm to use for the IdToken :param code: Access grant :param access_token: Access Token :param user_info: If user info are to be part of the IdToken :return: IDToken instance """ #defaults inawhile = {"days": 1} # Handle the idtoken_claims extra = {} itc = self.id_token_claims(session) if itc: try: inawhile = {"seconds": itc["max_age"]} except KeyError: inawhile = {} if "claims" in itc: for key, val in itc["claims"].items(): if key == "auth_time": extra["auth_time"] = time_util.utc_time_sans_frac() elif key == "acr": #["2","http://id.incommon.org/assurance/bronze"] extra["acr"] = verify_acr_level(val, loa) if user_info is None: _args = {} else: _args = user_info.to_dict() # Make sure that there are no name clashes for key in ["iss", "sub", "aud", "exp", "acr", "nonce", "auth_time"]: try: del _args[key] except KeyError: pass halg = "HS%s" % alg[-3:] if code: _args["c_hash"] = jws.left_hash(code, halg) if access_token: _args["at_hash"] = jws.left_hash(access_token, halg) idt = IdToken(iss=issuer, sub=session["sub"], aud = session["client_id"], exp = time_util.epoch_in_a_while(**inawhile), acr=loa, iat = time_util.utc_now(), **_args) for key, val in extra.items(): idt[key] = val if "nonce" in session: idt.nonce = session["nonce"] return idt
def __call__(self, request): data = request.body req = self.provider.parse_token_request(body=data) if 'grant_type' not in req: return (400, {}, 'Missing grant_type') if req['grant_type'] == 'authorization_code': authz_code = req['code'] authz_info = self.provider.authz_codes[authz_code] auth_req = authz_info['auth_req'] client_id = auth_req['client_id'] if authz_info['used']: raise Exception('code already used') return (400, {}, 'Invalid authorization code') if authz_info['exp'] < time.time(): raise Exception('code expired') return (400, {}, 'Invalid authorization code') authz_info['used'] = True access_token = { 'value': rndstr(), 'expires_in': self.provider.access_token_lifetime, 'type': 'Bearer' } at_value = access_token['value'] self.provider.access_tokens[at_value] = { 'iat': time.time(), 'exp': time.time() + self.provider.access_token_lifetime, 'sub': 'test-sub', 'client_id': client_id, 'aud': [client_id], 'scope': authz_info['granted_scope'], 'granted_scope': authz_info['granted_scope'], 'token_type': access_token['type'], 'auth_req': auth_req } resp = AccessTokenResponse() resp['access_token'] = at_value resp['token_type'] = access_token['type'] resp['expires_in'] = access_token['expires_in'] resp['refresh_token'] = None args = { 'c_hash': jws.left_hash(authz_code.encode('utf-8'), 'HS256'), 'at_hash': jws.left_hash(at_value.encode('utf-8'), 'HS256'), } id_token = IdToken(iss=self.config['issuer'], sub='test-sub', aud=client_id, iat=time.time(), exp=time.time() + self.provider.id_token_lifetime, **args) if 'nonce' in auth_req: id_token['nonce'] = auth_req['nonce'] resp['id_token'] = id_token.to_jwt([self.provider.signing_key], 'RS256') json_data = resp.to_json() return (200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache', }, json_data) return (400, {}, 'Unsupported grant_type')
def test_left_hash_hs256(): hsh = jws.left_hash('Please take a moment to register today') assert hsh == 'rCFHVJuxTqRxOsn2IUzgvA'
def __call__(self, request): data = request.body req = self.provider.parse_token_request(body=data) if 'grant_type' not in req: return (400, {}, 'Missing grant_type') if req['grant_type'] == 'authorization_code': authz_code = req['code'] authz_info = self.provider.authz_codes[authz_code] auth_req = authz_info['auth_req'] client_id = auth_req['client_id'] if authz_info['used']: raise Exception('code already used') return (400, {}, 'Invalid authorization code') if authz_info['exp'] < time.time(): raise Exception('code expired') return (400, {}, 'Invalid authorization code') authz_info['used'] = True access_token = { 'value': rndstr(), 'expires_in': self.provider.access_token_lifetime, 'type': 'Bearer' } at_value = access_token['value'] self.provider.access_tokens[at_value] = { 'iat': time.time(), 'exp': time.time() + self.provider.access_token_lifetime, 'sub': 'test-sub', 'client_id': client_id, 'aud': [client_id], 'scope': authz_info['granted_scope'], 'granted_scope': authz_info['granted_scope'], 'token_type': access_token['type'], 'auth_req': auth_req } resp = AccessTokenResponse() resp['access_token'] = at_value resp['token_type'] = access_token['type'] resp['expires_in'] = access_token['expires_in'] resp['refresh_token'] = None args = { 'c_hash': jws.left_hash(authz_code.encode('utf-8'), 'HS256'), 'at_hash': jws.left_hash(at_value.encode('utf-8'), 'HS256'), } id_token = IdToken( iss=self.config['issuer'], sub='test-sub', aud=client_id, iat=time.time(), exp=time.time() + self.provider.id_token_lifetime, **args) if 'nonce' in auth_req: id_token['nonce'] = auth_req['nonce'] resp['id_token'] = id_token.to_jwt( [self.provider.signing_key], 'RS256') json_data = resp.to_json() return ( 200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache', }, json_data ) return (400, {}, 'Unsupported grant_type')