def id_token_as_signed_jwt(client, alg="RS256"): if alg.startswith("HS"): ckey = client.keyjar.get_signing_key(alg2keytype(alg), "") else: ckey = client.keyjar.get_signing_key(alg2keytype(alg), "") _signed_jwt = client.id_token.to_jwt(key=ckey, algorithm=alg) return _signed_jwt
def __call__(self, sid, sinfo=None, kid='', **kwargs): keys = self.keyjar.get_signing_key(alg2keytype(self.sign_alg), owner='', kid=kid) if not keys: raise NoSuitableSigningKeys('kid={}'.format(kid)) key = keys[0] # Might be more then one if kid == '' rt = ' '.join(sinfo['response_type']) try: exp = utc_time_sans_frac() + self.lifetime[rt] except KeyError: exp = utc_time_sans_frac() + self.lifetime[''] _jti = '{}-{}'.format(self.type, uuid.uuid4().hex) _tok = TokenAssertion( iss=self.iss, azp=sinfo['client_id'], sub=sinfo['sub'], kid=key.kid, exp=exp, jti=_jti ) self.db[_jti] = sid try: _tok['aud'] = kwargs['aud'] except KeyError: pass return _tok.to_jwt([key], self.sign_alg)
def client_secret_jwt(cli, cis, request_args=None, http_args=None, **kwargs): """ Constructs a client assertion and signs it with the clients secret key. The request is modified as a side effect. :param cli: Client instance :param cis: The request :param request_args: request arguments :param http_args: HTTP arguments :param kwargs: Extra arguments :return: Constructed HTTP arguments, in this case none """ # audience is the OP endpoint audience = cli._endpoint(REQUEST2ENDPOINT[cis.type()]) try: algorithm = kwargs["algorithm"] except KeyError: #algorithm = cli.behaviour["require_signed_request_object"] algorithm = DEF_SIGN_ALG["client_secret_jwt"] signing_key = cli.keyjar.get_signing_key(alg2keytype(algorithm)) cis["client_assertion"] = assertion_jwt(cli, signing_key, audience, algorithm) cis["client_assertion_type"] = JWT_BEARER try: del cis["client_secret"] except KeyError: pass return {}
def construct_AuthorizationRequest(self, request=AuthorizationRequest, request_args=None, extra_args=None, request_param=None, **kwargs): if request_args is not None: # if "claims" in request_args: # kwargs["claims"] = request_args["claims"] # del request_args["claims"] if "nonce" not in request_args: _rt = request_args["response_type"] if "token" in _rt or "id_token" in _rt: request_args["nonce"] = rndstr(12) elif "response_type" in kwargs: if "token" in kwargs["response_type"]: request_args = {"nonce": rndstr(12)} else: # Never wrong to specify a nonce request_args = {"nonce": rndstr(12)} if "request_method" in kwargs: if kwargs["request_method"] == "file": request_param = "request_uri" del kwargs["request_method"] areq = oauth2.Client.construct_AuthorizationRequest(self, request, request_args, extra_args, **kwargs) if request_param: alg = self.behaviour["request_object_signing_alg"] if "algorithm" not in kwargs: kwargs["algorithm"] = alg if "keys" not in kwargs and alg: _kty = alg2keytype(alg) try: kwargs["keys"] = self.keyjar.get_signing_key( _kty, kid=self.kid["sig"][_kty]) except KeyError: kwargs["keys"] = self.keyjar.get_signing_key(_kty) _req = make_openid_request(areq, **kwargs) if request_param == "request": areq["request"] = _req else: _filedir = kwargs["local_dir"] _webpath = kwargs["base_path"] _name = rndstr(10) filename = os.path.join(_filedir, _name) while os.path.exists(filename): _name = rndstr(10) filename = os.path.join(_filedir, _name) fid = open(filename, mode="w") fid.write(_req) fid.close() _webname = "%s%s" % (_webpath, _name) areq["request_uri"] = _webname return areq
def keys_by_alg_and_usage(self, issuer, alg, usage): if usage in ["sig", "ver"]: ktype = jws.alg2keytype(alg) else: ktype = jwe.alg2keytype(alg) return self.get(usage, ktype, issuer)
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 pack(self, kid='', owner='', **kwargs): keys = self.keyjar.get_signing_key(jws.alg2keytype(self.sign_alg), owner=owner, kid=kid) if not keys: raise NoSuitableSigningKeys('kid={}'.format(kid)) key = keys[0] # Might be more then one if kid == '' iat = utc_time_sans_frac() if not 'exp' in kwargs: kwargs['exp'] = iat + self.lifetime try: _encrypt = kwargs['encrypt'] except KeyError: _encrypt = self.encrypt else: del kwargs['encrypt'] _jwt = self.message_type(iss=self.iss, iat=iat, **kwargs) if 'jti' in self.message_type.c_param: try: _jti = kwargs['jti'] except: _jti = uuid.uuid4().hex _jwt['jti'] = _jti _jws = _jwt.to_jwt([key], self.sign_alg) if _encrypt: return self._encrypt(_jws) else: return _jws
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_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", } with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.org/end_session", status=302, headers={"location": ""}, ) resp = self.client.do_end_session_request(request_args=args, state="state1") parsed = parse_qs(urlparse(resp.request.url).query) assert parsed["redirect_url"] == ["http://example.com/end"] assert parsed["id_token"] is not None
def store_signed_jwks(keyjar, sign_keyjar, path, alg, iss=''): _jwks = keyjar.export_jwks() _jws = JWS(_jwks, alg=alg) _jwt = _jws.sign_compact( sign_keyjar.get_signing_key(owner=iss, key_type=alg2keytype(alg))) fp = open(path, 'w') fp.write(_jwt) fp.close()
def pack_key(self, owner='', kid=''): keys = self.keyjar.get_signing_key(jws.alg2keytype(self.sign_alg), owner=owner, kid=kid) if not keys: raise NoSuitableSigningKeys('kid={}'.format(kid)) return keys[0] # Might be more then one if kid == ''
def _verify(self, rj, token): _msg = json.loads(rj.jwt.part[1].decode('utf8')) if _msg['iss'] == self.iss: owner = '' else: owner = _msg['iss'] keys = self.keyjar.get_verify_key(jws.alg2keytype(rj.jwt.headers['alg']), owner=owner) return rj.verify_compact(token, keys)
def test_parse_check_session_request(self): csreq = CheckSessionRequest( id_token=IDTOKEN.to_jwt(key=KC_SYM_S.get(alg2keytype("HS256")), algorithm="HS256")) request = self.srv.parse_check_session_request( query=csreq.to_urlencoded()) assert isinstance(request, IdToken) assert _eq(request.keys(), ['nonce', 'sub', 'aud', 'iss', 'exp', 'iat']) assert request["aud"] == ["client_1"]
def construct_AuthorizationRequest(self, request=AuthorizationRequest, request_args=None, extra_args=None, request_param=None, **kwargs): if request_args is not None: # if "claims" in request_args: # kwargs["claims"] = request_args["claims"] # del request_args["claims"] if "nonce" not in request_args: _rt = request_args["response_type"] if "token" in _rt or "id_token" in _rt: request_args["nonce"] = rndstr(12) elif "response_type" in kwargs: if "token" in kwargs["response_type"]: request_args = {"nonce": rndstr(12)} else: # Never wrong to specify a nonce request_args = {"nonce": rndstr(12)} if "request_method" in kwargs: if kwargs["request_method"] == "file": request_param = "request_uri" del kwargs["request_method"] areq = oauth2.Client.construct_AuthorizationRequest( self, request, request_args, extra_args, **kwargs) if request_param: alg = self.behaviour["require_signed_request_object"] if "algorithm" not in kwargs: kwargs["algorithm"] = alg if "keys" not in kwargs and alg: atype = alg2keytype(alg) kwargs["keys"] = self.keyjar.get_signing_key(atype) _req = make_openid_request(areq, **kwargs) if request_param == "request": areq["request"] = _req else: _filedir = kwargs["local_dir"] _webpath = kwargs["base_path"] _name = rndstr(10) filename = os.path.join(_filedir, _name) while os.path.exists(filename): _name = rndstr(10) filename = os.path.join(_filedir, _name) fid = open(filename, mode="w") fid.write(_req) fid.close() _webname = "%s%s" % (_webpath, _name) areq["request_uri"] = _webname return areq
def test_parse_check_session_request(self): csreq = CheckSessionRequest(id_token=IDTOKEN.to_jwt( key=KC_SYM_S.get(alg2keytype("HS256")), algorithm="HS256")) request = self.srv.parse_check_session_request( query=csreq.to_urlencoded()) assert isinstance(request, IdToken) assert _eq(request.keys(), ["nonce", "sub", "aud", "iss", "exp", "iat"]) assert request["aud"] == ["client_1"]
def get_key_by_kid(self, kid, algorithm): _key = self.cli.keyjar.get_key_by_kid(kid) if _key: ktype = alg2keytype(algorithm) if _key.kty == ktype: return _key else: raise NoMatchingKey("Wrong key type") else: raise NoMatchingKey("No key with kid:%s" % kid)
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 _verify(self, rj, token): _msg = json.loads(rj.jwt.part[1].decode('utf8')) if _msg['iss'] == self.iss: owner = '' else: owner = _msg['iss'] keys = self.keyjar.get_signing_key(jws.alg2keytype(rj.jwt.headers['alg']), owner=owner) return rj.verify_compact(token, keys)
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 create_software_statement(sws_data): sws_data["iss"] = "https://{host}:{port}/static/jwks.json".format(host=HOST, port=PORT) sws = SWSMessage() sws.from_dict(sws_data) _, keyjar, _ = build_keyjar(KEYS) alg = 'RS256' ckey = keyjar.get_signing_key(alg2keytype(alg), "", alg=alg) return sws.to_jwt(key=ckey, algorithm=alg)
def authorization_endpoint(self, query): req = self.parse_authorization_request(query=query) aevent = AuthnEvent("user", "salt", authn_info="acr") sid = self.sdb.create_authz_session(aevent, areq=req) self.sdb.do_sub(sid, 'client_salt') _info = self.sdb[sid] if "code" in req["response_type"]: if "token" in req["response_type"]: grant = _info["code"] _dict = self.sdb.upgrade_to_token(grant) _dict["oauth_state"] = "authz", _dict = by_schema(AuthorizationResponse(), **_dict) resp = AuthorizationResponse(**_dict) # resp.code = grant else: _state = req["state"] resp = AuthorizationResponse(state=_state, code=_info["code"]) else: # "implicit" in req.response_type: grant = _info["code"] params = AccessTokenResponse.c_param.keys() if "token" in req["response_type"]: _dict = dict([ (k, v) for k, v in self.sdb.upgrade_to_token(grant).items() if k in params ]) try: del _dict["refresh_token"] except KeyError: pass else: _dict = {"state": req["state"]} if "id_token" in req["response_type"]: _idt = self.make_id_token(_info, issuer=self.name) alg = "RS256" ckey = self.keyjar.get_signing_key(alg2keytype(alg), _info["client_id"]) _signed_jwt = _idt.to_jwt(key=ckey, algorithm=alg) p = _signed_jwt.split(".") p[2] = "aaa" _dict["id_token"] = ".".join(p) resp = AuthorizationResponse(**_dict) location = resp.request(req["redirect_uri"]) response = Response() response.headers = {"location": location} response.status_code = 302 response.text = "" return response
def _verify(self, rj, token): _msg = json.loads(rj.jwt.part[1].decode("utf8")) if _msg["iss"] == self.iss: owner = "" else: owner = _msg["iss"] keys = self.keyjar.get_verify_key(jws.alg2keytype( rj.jwt.headers["alg"]), owner=owner) return rj.verify_compact(token, keys)
def authorization_endpoint(self, query): req = self.parse_authorization_request(query=query) aevent = AuthnEvent("user", authn_info="acr") sid = self.sdb.create_authz_session(aevent, areq=req) _ = self.sdb.do_sub(sid) _info = self.sdb[sid] if "code" in req["response_type"]: if "token" in req["response_type"]: grant = _info["code"] _dict = self.sdb.upgrade_to_token(grant) _dict["oauth_state"] = "authz", _dict = by_schema(AuthorizationResponse(), **_dict) resp = AuthorizationResponse(**_dict) # resp.code = grant else: _state = req["state"] resp = AuthorizationResponse(state=_state, code=_info["code"]) else: # "implicit" in req.response_type: grant = _info["code"] params = AccessTokenResponse.c_param.keys() if "token" in req["response_type"]: _dict = dict([(k, v) for k, v in self.sdb.upgrade_to_token(grant).items() if k in params]) try: del _dict["refresh_token"] except KeyError: pass else: _dict = {"state": req["state"]} if "id_token" in req["response_type"]: _idt = self.make_id_token(_info, issuer=self.name) alg = "RS256" ckey = self.keyjar.get_signing_key(alg2keytype(alg), _info["client_id"]) _signed_jwt = _idt.to_jwt(key=ckey, algorithm=alg) p = _signed_jwt.split(".") p[2] = "aaa" _dict["id_token"] = ".".join(p) resp = AuthorizationResponse(**_dict) location = resp.request(req["redirect_uri"]) response = Response() response.headers = {"location": location} response.status_code = 302 response.text = "" return response
def id_token_as_signed_jwt(self, session, loa="2", alg="RS256", code=None, access_token=None, user_info=None): logger.debug("Signing alg: %s [%s]" % (alg, alg2keytype(alg))) _idt = self.server.make_id_token(session, loa, self.name, alg, code, access_token, user_info) logger.debug("id_token: %s" % _idt.to_dict()) # My signing key if its RS*, can use client secret if HS* if alg.startswith("HS"): logger.debug("client_id: %s" % session["client_id"]) ckey = self.keyjar.get_signing_key(alg2keytype(alg), session["client_id"]) else: for b in self.keyjar[""]: logger.debug("OC3 server keys: %s" % b) ckey = self.keyjar.get_signing_key(alg2keytype(alg), "") logger.debug("ckey: %s" % ckey) _signed_jwt = _idt.to_jwt(key=ckey, algorithm=alg) return _signed_jwt
def test_example(self): _symkey = KC_SYM_S.get(alg2keytype("HS256")) esreq = EndSessionRequest(id_token_hint=IDTOKEN.to_jwt( key=_symkey, algorithm="HS256", lifetime=300), redirect_url="http://example.org/jqauthz", state="state0") request = EndSessionRequest().from_urlencoded(esreq.to_urlencoded()) request.verify(key=_symkey) assert isinstance(request, EndSessionRequest) assert _eq(request.keys(), ['id_token_hint', 'redirect_url', 'state']) assert request["state"] == "state0" assert request["id_token_hint"]["aud"] == ["client_1"]
def do_user_info_request(self, method="POST", state="", scope="openid", request="openid", **kwargs): kwargs["request"] = request path, body, method, h_args = self.user_info_request( method, state, scope, **kwargs) logger.debug("[do_user_info_request] PATH:%s BODY:%s H_ARGS: %s" % (path, body, h_args)) try: resp = self.http_request(path, method, data=body, **h_args) except oauth2.MissingRequiredAttribute: raise if resp.status_code == 200: try: assert "application/json" in resp.headers["content-type"] sformat = "json" except AssertionError: assert "application/jwt" in resp.headers["content-type"] sformat = "jwt" elif resp.status_code == 500: raise PyoidcError("ERROR: Something went wrong: %s" % resp.text) else: raise PyoidcError("ERROR: Something went wrong [%s]: %s" % (resp.status_code, resp.text)) try: _schema = kwargs["user_info_schema"] except KeyError: _schema = OpenIDSchema logger.debug("Reponse text: '%s'" % resp.text) if sformat == "json": return _schema().from_json(txt=resp.text) else: algo = self.client_prefs["userinfo_signed_response_alg"] _kty = alg2keytype(algo) # Keys of the OP ? try: keys = self.keyjar.get_signing_key(_kty, self.kid["sig"][_kty]) except KeyError: keys = self.keyjar.get_signing_key(_kty) return _schema().from_jwt(resp.text, keys)
def verify(self, areq, **kwargs): try: try: argv = {'sender': areq['client_id']} except KeyError: argv = {} bjwt = AuthnToken().from_jwt(areq["client_assertion"], keyjar=self.cli.keyjar, **argv) except (Invalid, MissingKey) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") logger.debug("authntoken: %s" % sanitize(bjwt.to_dict())) areq['parsed_client_assertion'] = bjwt # logger.debug("known clients: %s" % sanitize(self.cli.cdb.keys())) try: cid = kwargs["client_id"] except KeyError: cid = bjwt["iss"] try: # There might not be a client_id in the request assert str(cid) in self.cli.cdb # It's a client I know except KeyError: pass # aud can be a string or a list _aud = bjwt["aud"] logger.debug("audience: %s, baseurl: %s" % (_aud, self.cli.baseurl)) # figure out authn method if alg2keytype(bjwt.jws_header['alg']) == 'oct': # Symmetric key authn_method = 'client_secret_jwt' else: authn_method = 'private_key_jwt' try: if isinstance(_aud, six.string_types): assert str(_aud).startswith(self.cli.baseurl) else: for target in _aud: if target.startswith(self.cli.baseurl): return cid, authn_method raise NotForMe("Not for me!") except AssertionError: raise NotForMe("Not for me!") return cid, authn_method
def construct(self, cis, request_args=None, http_args=None, **kwargs): """ Constructs a client assertion and signs it with a key. The request is modified as a side effect. :param cis: The request :param request_args: request arguments :param http_args: HTTP arguments :param kwargs: Extra arguments :return: Constructed HTTP arguments, in this case none """ # audience is the OP endpoint audience = self.cli._endpoint(REQUEST2ENDPOINT[cis.type()]) algorithm = self.choose_algorithm(**kwargs) ktype = alg2keytype(algorithm) try: if 'kid' in kwargs: signing_key = [self.get_key_by_kid(kwargs["kid"], algorithm)] elif ktype in self.cli.kid["sig"]: try: signing_key = [ self.get_key_by_kid(self.cli.kid["sig"][ktype], algorithm) ] except KeyError: signing_key = self.get_signing_key(algorithm) else: signing_key = self.get_signing_key(algorithm) except NoMatchingKey as err: logger.error("%s" % err) raise SystemError() cis["client_assertion"] = assertion_jwt(self.cli, signing_key, audience, algorithm) cis["client_assertion_type"] = JWT_BEARER try: del cis["client_secret"] except KeyError: pass if not cis.c_param["client_id"][VREQUIRED]: try: del cis["client_id"] except KeyError: pass return {}
def test_parse_end_session_request(self): esreq = EndSessionRequest( id_token=IDTOKEN.to_jwt(key=KC_SYM_S.get(alg2keytype("HS256")), algorithm="HS256"), redirect_url="http://example.org/jqauthz", state="state0") request = self.srv.parse_end_session_request( query=esreq.to_urlencoded()) assert isinstance(request, EndSessionRequest) assert _eq(request.keys(), ['id_token', 'redirect_url', 'state']) assert request["state"] == "state0" assert request["id_token"]["aud"] == ["client_1"]
def test_parse_end_session_request(self): esreq = EndSessionRequest(id_token=IDTOKEN.to_jwt(key=KC_SYM_S.get( alg2keytype("HS256")), algorithm="HS256"), redirect_url="http://example.org/jqauthz", state="state0") request = self.srv.parse_end_session_request( query=esreq.to_urlencoded()) assert isinstance(request, EndSessionRequest) assert _eq(request.keys(), ['id_token', 'redirect_url', 'state']) assert request["state"] == "state0" assert request["id_token"]["aud"] == ["client_1"]
def get_verify_keys(self, keyjar, key, jso, header, jwt, **kwargs): try: _iss = jso["iss"] except KeyError: pass else: if "jku" in header: if not keyjar.find(header["jku"], _iss): # This is really questionable try: if kwargs["trusting"]: keyjar.add(jso["iss"], header["jku"]) except KeyError: pass if "kid" in header and header["kid"]: jwt["kid"] = header["kid"] try: _key = keyjar.get_key_by_kid(header["kid"], _iss) if _key: key.append(_key) except KeyError: pass try: self._add_key(keyjar, kwargs["opponent_id"], key) except KeyError: pass try: _key_type = alg2keytype(header['alg']) except KeyError: _key_type = '' for ent in ["iss", "aud", "client_id"]: if ent not in jso: continue if ent == "aud": # list or basestring if isinstance(jso["aud"], six.string_types): _aud = [jso["aud"]] else: _aud = jso["aud"] for _e in _aud: self._add_key(keyjar, _e, key, _key_type) else: self._add_key(keyjar, jso[ent], key, _key_type) return key
def construct(self, cis, request_args=None, http_args=None, **kwargs): """ Constructs a client assertion and signs it with a key. The request is modified as a side effect. :param cis: The request :param request_args: request arguments :param http_args: HTTP arguments :param kwargs: Extra arguments :return: Constructed HTTP arguments, in this case none """ # audience is the OP endpoint audience = self.cli._endpoint(REQUEST2ENDPOINT[cis.type()]) algorithm = self.choose_algorithm(**kwargs) ktype = alg2keytype(algorithm) try: if 'kid' in kwargs: signing_key = [self.get_key_by_kid(kwargs["kid"], algorithm)] elif ktype in self.cli.kid["sig"]: try: signing_key = [self.get_key_by_kid( self.cli.kid["sig"][ktype], algorithm)] except KeyError: signing_key = self.get_signing_key(algorithm) else: signing_key = self.get_signing_key(algorithm) except NoMatchingKey as err: logger.error("%s" % err) raise SystemError() cis["client_assertion"] = assertion_jwt(self.cli, signing_key, audience, algorithm) cis["client_assertion_type"] = JWT_BEARER try: del cis["client_secret"] except KeyError: pass if not cis.c_param["client_id"][VREQUIRED]: try: del cis["client_id"] except KeyError: pass return {}
def authorization_endpoint(self, query): req = self.parse_authorization_request(query=query) sid = self.sdb.create_authz_session(sub="user", areq=req) _info = self.sdb[sid] _info["sub"] = _info["local_sub"] if "code" in req["response_type"]: if "token" in req["response_type"]: grant = _info["code"] _dict = self.sdb.upgrade_to_token(grant) _dict["oauth_state"] = "authz", _dict = by_schema(AuthorizationResponse(), **_dict) resp = AuthorizationResponse(**_dict) #resp.code = grant else: resp = AuthorizationResponse(state=req["state"], code=_info["code"]) else: # "implicit" in req.response_type: grant = _info["code"] params = AccessTokenResponse.c_param.keys() _dict = dict([(k, v) for k, v in self.sdb.upgrade_to_token(grant).items() if k in params]) try: del _dict["refresh_token"] except KeyError: pass if "id_token" in req["response_type"]: _idt = self.make_id_token(_info, issuer=self.name, access_token=_dict["access_token"]) alg = "RS256" ckey = self.keyjar.get_signing_key(alg2keytype(alg), _info["client_id"]) _dict["id_token"] = _idt.to_jwt(key=ckey, algorithm=alg) resp = AccessTokenResponse(**_dict) location = resp.request(req["redirect_uri"]) response = Response() response.headers = {"location": location} response.status_code = 302 response.text = "" return response
def get_verify_keys(self, keyjar, key, jso, header, jwt, **kwargs): try: _iss = jso["iss"] except KeyError: pass else: if "jku" in header: if not keyjar.find(header["jku"], _iss): # This is really questionable try: if kwargs["trusting"]: keyjar.add(jso["iss"], header["jku"]) except KeyError: pass if "kid" in header and header["kid"]: jwt["kid"] = header["kid"] try: _key = keyjar.get_key_by_kid(header["kid"], _iss) if _key: key.append(_key) except KeyError: pass try: self._add_key(keyjar, kwargs["opponent_id"], key) except KeyError: pass try: _key_type = alg2keytype(header["alg"]) except KeyError: _key_type = "" for ent in ["iss", "aud", "client_id"]: if ent not in jso: continue if ent == "aud": # list or basestring if isinstance(jso["aud"], six.string_types): _aud = [jso["aud"]] else: _aud = jso["aud"] for _e in _aud: self._add_key(keyjar, _e, key, _key_type) else: self._add_key(keyjar, jso[ent], key, _key_type) return key
def verify(self, areq, **kwargs): try: try: argv = {"sender": areq["client_id"]} except KeyError: argv = {} bjwt = AuthnToken().from_jwt(areq["client_assertion"], keyjar=self.cli.keyjar, **argv) except (Invalid, MissingKey) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") logger.debug("authntoken: %s" % sanitize(bjwt.to_dict())) areq["parsed_client_assertion"] = bjwt # logger.debug("known clients: %s" % sanitize(self.cli.cdb.keys())) try: cid = kwargs["client_id"] except KeyError: cid = bjwt["iss"] try: # There might not be a client_id in the request assert str(cid) in self.cli.cdb # It's a client I know except KeyError: pass # aud can be a string or a list _aud = bjwt["aud"] logger.debug("audience: %s, baseurl: %s" % (_aud, self.cli.baseurl)) # figure out authn method if alg2keytype(bjwt.jws_header["alg"]) == "oct": # Symmetric key authn_method = "client_secret_jwt" else: authn_method = "private_key_jwt" try: if isinstance(_aud, six.string_types): assert str(_aud).startswith(self.cli.baseurl) else: for target in _aud: if target.startswith(self.cli.baseurl): return cid, authn_method raise NotForMe("Not for me!") except AssertionError: raise NotForMe("Not for me!") return cid, authn_method
def pack_key(self, owner='', kid=''): """ Find a key to be used for signing the Json Web Token :param owner: Owner of the keys to chose from :param kid: Key ID :return: One key """ keys = self.keyjar.get_signing_key(jws.alg2keytype(self.sign_alg), owner=owner, kid=kid) if not keys: raise NoSuitableSigningKeys('kid={}'.format(kid)) return keys[0] # Might be more then one if kid == ''
def test_do_check_session_request(self): self.client.redirect_uris = ["https://www.example.com/authz"] self.client.client_id = CLIENT_ID self.client.check_session_endpoint = "https://example.org/check_session" # RSA signing alg = "RS256" ktyp = alg2keytype(alg) _sign_key = self.client.keyjar.get_signing_key(ktyp) print _sign_key args = {"id_token": IDTOKEN.to_jwt(key=_sign_key, algorithm=alg)} print self.client.keyjar.issuer_keys resp = self.client.do_check_session_request(request_args=args) assert resp.type() == "IdToken" assert _eq(resp.keys(), ['nonce', 'sub', 'aud', 'iss', 'exp', 'iat'])
def do_user_info_request(self, method="POST", state="", scope="openid", request="openid", **kwargs): kwargs["request"] = request path, body, method, h_args = self.user_info_request(method, state, scope, **kwargs) logger.debug("[do_user_info_request] PATH:%s BODY:%s H_ARGS: %s" % ( path, body, h_args)) try: resp = self.http_request(path, method, data=body, **h_args) except oauth2.MissingRequiredAttribute: raise if resp.status_code == 200: try: assert "application/json" in resp.headers["content-type"] sformat = "json" except AssertionError: assert "application/jwt" in resp.headers["content-type"] sformat = "jwt" elif resp.status_code == 500: raise PyoidcError("ERROR: Something went wrong: %s" % resp.text) else: raise PyoidcError("ERROR: Something went wrong [%s]: %s" % ( resp.status_code, resp.text)) try: _schema = kwargs["user_info_schema"] except KeyError: _schema = OpenIDSchema logger.debug("Reponse text: '%s'" % resp.text) if sformat == "json": return _schema().from_json(txt=resp.text) else: algo = self.client_prefs["userinfo_signed_response_alg"] _kty = alg2keytype(algo) # Keys of the OP ? try: keys = self.keyjar.get_signing_key(_kty, self.kid["sig"][_kty]) except KeyError: keys = self.keyjar.get_signing_key(_kty) return _schema().from_jwt(resp.text, keys)
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 verify(self, areq, **kwargs): try: try: argv = {"sender": areq["client_id"]} except KeyError: argv = {} bjwt = AuthnToken().from_jwt(areq["client_assertion"], keyjar=self.cli.keyjar, **argv) except (Invalid, MissingKey) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") logger.debug("authntoken: %s" % sanitize(bjwt.to_dict())) areq["parsed_client_assertion"] = bjwt try: cid = kwargs["client_id"] except KeyError: cid = bjwt["iss"] # There might not be a client_id in the request if cid not in self.cli.cdb: raise AuthnFailure("Unknown client id") # aud can be a string or a list _aud = bjwt["aud"] logger.debug("audience: %s, baseurl: %s" % (_aud, self.cli.baseurl)) # figure out authn method if alg2keytype(bjwt.jws_header["alg"]) == "oct": # Symmetric key authn_method = "client_secret_jwt" else: authn_method = "private_key_jwt" if isinstance(_aud, str): if not str(_aud).startswith(self.cli.baseurl): raise NotForMe("Not for me!") else: for target in _aud: if target.startswith(self.cli.baseurl): return cid, authn_method raise NotForMe("Not for me!") return cid, authn_method
def id_token_as_signed_jwt(self, session, loa="2", alg="RS256", code=None, access_token=None, user_info=None): _idt = self.server.make_id_token(session, loa, self.name, alg, code, access_token, user_info) # mess with the at_hash or the c_hash if "at_hash" in _idt: _idt["at_hash"] += "a" if "c_hash" in _idt: _idt["c_hash"] += "c" LOGGER.debug("Signing alg: %s" % alg) LOGGER.debug("keys: %s" % self.keystore.keys_by_owner(session["client_id"])) ckey = get_signing_key(self.keystore, alg2keytype(alg), session["client_id"]) LOGGER.debug("ckey: %s" % ckey) return _idt.to_jwt(key=ckey, algorithm=alg)
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)} with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/check_session", content_type="application/json", body=IDTOKEN.to_json(), ) resp = self.client.do_check_session_request(request_args=args) parsed = parse_qs(urlparse(rsps.calls[0].request.url).query) assert parsed["id_token"] is not None assert isinstance(resp, IdToken) assert _eq(resp.keys(), ["nonce", "sub", "aud", "iss", "exp", "iat"])
def pack(self, kid='', owner='', **kwargs): keys = self.keyjar.get_signing_key(jws.alg2keytype(self.sign_alg), owner=owner, kid=kid) if not keys: raise NoSuitableSigningKeys('kid={}'.format(kid)) key = keys[0] # Might be more then one if kid == '' if key.kid: kwargs['kid'] = key.kid iat = utc_time_sans_frac() if not 'exp' in kwargs: kwargs['exp'] = iat + self.lifetime try: _encrypt = kwargs['encrypt'] except KeyError: _encrypt = self.encrypt else: del kwargs['encrypt'] _jwt = self.message_type(iss=self.iss, iat=iat, **kwargs) if 'jti' in self.message_type.c_param: try: _jti = kwargs['jti'] except: _jti = uuid.uuid4().hex _jwt['jti'] = _jti _jws = _jwt.to_jwt([key], self.sign_alg) if _encrypt: return self._encrypt(_jws) else: return _jws
def _unpack_jwt(self, token, only_info=False): if not token: raise KeyError _rj = factory(token) _msg = json.loads(_rj.jwt.part[1].decode('utf8')) if _msg['iss'] == self.iss: owner = '' else: owner = _msg['iss'] keys = self.keyjar.get_signing_key(alg2keytype(_rj.jwt.headers['alg']), owner=owner) info = _rj.verify_compact(token, keys) if only_info: return info try: sid = self.db[info['jti']] except KeyError: raise return sid, info
def private_key_jwt(cli, cis, request_args=None, http_args=None, **kwargs): """ Constructs a client assertion and signs it with the clients public RSA key. The request is modified as a side effect. :param cli: Client instance :param cis: The request :param request_args: request arguments :param http_args: HTTP arguments :param kwargs: Extra arguments :return: Constructed HTTP arguments, in this case none """ # audience is the OP endpoint audience = cli._endpoint(REQUEST2ENDPOINT[cis.type()]) try: algorithm = kwargs["algorithm"] except KeyError: algorithm = DEF_SIGN_ALG["private_key_jwt"] if not algorithm: raise Exception("Missing algorithm specification") # signing key should be the clients rsa key signing_key = cli.keyjar.get_signing_key(alg2keytype(algorithm), "") cis["client_assertion"] = assertion_jwt(cli, signing_key, audience, algorithm) cis["client_assertion_type"] = JWT_BEARER try: del cis["client_secret"] except KeyError: pass return {}
def userinfo_endpoint(self, data): _ = self.parse_user_info_request(data) _info = { "sub": "melgar", "name": "Melody Gardot", "nickname": "Mel", "email": "*****@*****.**", "verified": True, } resp = OpenIDSchema(**_info) response = Response() if self.userinfo_signed_response_alg: alg = self.userinfo_signed_response_alg response.headers = {"content-type": "application/jwt"} key = self.keyjar.get_signing_key(alg2keytype(alg), "", alg=alg) response.text = resp.to_jwt(key, alg) else: response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response
def userinfo_endpoint(self, data): self.parse_user_info_request(data) _info = { "sub": "melgar", "name": "Melody Gardot", "nickname": "Mel", "email": "*****@*****.**", "verified": True, } resp = OpenIDSchema(**_info) response = Response() if self.userinfo_signed_response_alg: alg = self.userinfo_signed_response_alg response.headers = {"content-type": "application/jwt"} key = self.keyjar.get_signing_key(alg2keytype(alg), "", alg=alg) response.text = resp.to_jwt(key, alg) else: response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response
def id_token_as_signed_jwt(client, id_token, alg="RS256"): ckey = client.keyjar.get_signing_key(alg2keytype(alg), "") _signed_jwt = id_token.to_jwt(key=ckey, algorithm=alg) return _signed_jwt
def get_signing_key(self, algorithm): return self.cli.keyjar.get_signing_key(alg2keytype(algorithm), "", alg=algorithm)
def construct(self, cis, request_args=None, http_args=None, **kwargs): """ Construct a client assertion and signs it with a key. The request is modified as a side effect. :param cis: The request :param request_args: request arguments :param http_args: HTTP arguments :param kwargs: Extra arguments :return: Constructed HTTP arguments, in this case none """ # audience is the OP endpoint # OR OP identifier algorithm = None if kwargs['authn_endpoint'] in ['token', 'refresh']: try: algorithm = self.cli.registration_info[ 'token_endpoint_auth_signing_alg'] except (KeyError, AttributeError): pass audience = self.cli.provider_info['token_endpoint'] else: audience = self.cli.provider_info['issuer'] if not algorithm: algorithm = self.choose_algorithm(**kwargs) ktype = alg2keytype(algorithm) try: if 'kid' in kwargs: signing_key = [self.get_key_by_kid(kwargs["kid"], algorithm)] elif ktype in self.cli.kid["sig"]: try: signing_key = [ self.get_key_by_kid(self.cli.kid["sig"][ktype], algorithm) ] except KeyError: signing_key = self.get_signing_key(algorithm) else: signing_key = self.get_signing_key(algorithm) except NoMatchingKey as err: logger.error("%s" % sanitize(err)) raise if 'client_assertion' in kwargs: cis["client_assertion"] = kwargs['client_assertion'] if 'client_assertion_type' in kwargs: cis['client_assertion_type'] = kwargs['client_assertion_type'] else: cis["client_assertion_type"] = JWT_BEARER elif 'client_assertion' in cis: if 'client_assertion_type' not in cis: cis["client_assertion_type"] = JWT_BEARER else: try: _args = {'lifetime': kwargs['lifetime']} except KeyError: _args = {} cis["client_assertion"] = assertion_jwt(self.cli, signing_key, audience, algorithm, **_args) cis["client_assertion_type"] = JWT_BEARER try: del cis["client_secret"] except KeyError: pass if not cis.c_param["client_id"][VREQUIRED]: try: del cis["client_id"] except KeyError: pass return {}
def get_verify_keys(self, keyjar, key, jso, header, jwt, **kwargs): """ Get keys from a keyjar that can be used to verify a signed JWT :param keyjar: A KeyJar instance :param key: List of keys to start with :param jso: The payload of the JWT, expected to be a dictionary. :param header: The header of the JWT :param jwt: A jwkest.jwt.JWT instance :param kwargs: Other key word arguments :return: list of usable keys """ try: _kid = header['kid'] except KeyError: _kid = '' try: _iss = jso["iss"] except KeyError: pass else: # First extend the keyjar if allowed if "jku" in header: if not keyjar.find(header["jku"], _iss): # This is really questionable try: if kwargs["trusting"]: keyjar.add(jso["iss"], header["jku"]) except KeyError: pass # If there is a kid and a key is found with that kid at the issuer # then I'm done if _kid: jwt["kid"] = _kid try: _key = keyjar.get_key_by_kid(_kid, _iss) if _key: key.append(_key) return key except KeyError: pass try: nki = kwargs['no_kid_issuer'] except KeyError: nki = {} try: _key_type = alg2keytype(header['alg']) except KeyError: _key_type = '' try: self._add_key(keyjar, kwargs["opponent_id"], key, _key_type, _kid, nki) except KeyError: pass for ent in ["iss", "aud", "client_id"]: if ent not in jso: continue if ent == "aud": # list or basestring if isinstance(jso["aud"], six.string_types): _aud = [jso["aud"]] else: _aud = jso["aud"] for _e in _aud: self._add_key(keyjar, _e, key, _key_type, _kid, nki) else: self._add_key(keyjar, jso[ent], key, _key_type, _kid, nki) return key
from jwkest.jws import alg2keytype from oic.oic.message import IdToken from oic.utils.keyio import KeyBundle, KeyJar __author__ = 'rohe0002' b0 = KeyBundle(source="http://localhost:8090/exports/jwk.json", src_type="jwk", usage=["ver", "dec", "sig"]) b1 = KeyBundle(source="http://localhost:8090/exports/cert.pem", src_type="x509", usage=["ver", "dec", "sig"]) print b0 print b1 kj = KeyJar() kj["foobar"] = [b0, b1] idt = IdToken().from_dict({"user_id": "diana", "aud": "uo5nowsdL3ck", "iss": "https://localhost:8092", "acr": "2", "exp": 1354442188, "iat": 1354359388}) ckey = kj.get_signing_key(alg2keytype("RS256"), "foobar") _signed_jwt = idt.to_jwt(key=ckey, algorithm="RS256")
def get_signing_key(self, algorithm): return self.cli.keyjar.get_signing_key(alg2keytype(algorithm), "")