def test_query_acct_host_forced(self): wf = WebFinger(OIC_ISSUER) query = wf.query("http://example.com/carol", host='forced.co') assert query == "https://forced.co/.well-known/webfinger?resource" \ "=http%3A%2F%2Fexample.com%2Fcarol&rel=http%3A%2F%2Fopenid" \ ".net%2Fspecs%2Fconnect%2F1.0%2Fissuer"
def test_query_acct(self): wf = WebFinger(OIC_ISSUER) query = wf.query("acct:[email protected]") assert query == "https://example.com/.well-known/webfinger?resource" \ "=acct%3Acarol%40example.com&rel=http%3A%2F%2Fopenid" \ ".net%2Fspecs%2Fconnect%2F1.0%2Fissuer"
def test_query_rel(self): wf = WebFinger() query = wf.query("acct:[email protected]", ["http://webfinger.net/rel/profile-page", "vcard"]) assert query == "https://example.com/.well-known/webfinger?resource" \ "=acct%3Abob%40example.com&rel=http%3A%2F%2Fwebfinger" \ ".net%2Frel%2Fprofile-page&rel=vcard"
def discover(self, *arg, **kwargs): wf = WebFinger(OIC_ISSUER) wf.httpd = PBase() _url = wf.query(kwargs["principal"]) self.trace.request("URL: %s" % _url) url = wf.discovery_query(kwargs["principal"]) return url
def webfinger(environ, events): query = parse_qs(environ["QUERY_STRING"]) _op = environ["oic.op"] try: if query["rel"] != [OIC_ISSUER]: events.store( EV_CONDITION, State('webfinger_parameters', ERROR, message='parameter rel wrong value: {}'.format( query['rel']))) return BadRequest('Parameter value error') else: resource = query["resource"][0] except KeyError as err: events.store( EV_CONDITION, State('webfinger_parameters', ERROR, message='parameter {} missing'.format(err))) resp = BadRequest("Missing parameter in request") else: wf = WebFinger() resp = Response(wf.response(subject=resource, base=_op.baseurl)) return resp
def __init__(self, name=""): Server.__init__(self) self.sdb = SessionDB() self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" self.webfinger = WebFinger()
def setup_webfinger_endpoint(self): wf = WebFinger() resp = Response(wf.response(subject=self.op_base, base=self.op_base)) responses.add(responses.GET, self.op_base + ".well-known/webfinger", body=resp.message, status=200, content_type='application/json')
def dynamic_client(self, issuer="", userid=""): client = self.client_cls( client_authn_method=CLIENT_AUTHN_METHOD, verify_ssl=self.verify_ssl, **self.jwks_info ) if userid: try: issuer = client.wf.discovery_query(userid) except AttributeError: wf = WebFinger(httpd=client) issuer = wf.discovery_query(userid) if not issuer: raise OAuth2Error("Missing issuer") logger.info("issuer: {}".format(issuer)) if issuer in self.client: return self.client[issuer] else: # Gather OP information _pcr = client.provider_config(issuer) logger.info("Provider info: {}".format(sanitize(_pcr.to_dict()))) issuer = _pcr["issuer"] # So no hickup later about trailing '/' # register the client _cinfo = self.config.CLIENTS[""]["client_info"] reg_args = copy.copy(_cinfo) h = hashlib.sha256(self.seed) h.update(issuer.encode("utf8")) # issuer has to be bytes base_urls = _cinfo["redirect_uris"] reg_args["redirect_uris"] = [ u.format(base=self.base_url, iss=h.hexdigest()) for u in base_urls ] try: reg_args["post_logout_redirect_uris"] = [ u.format(base=self.base_url, iss=h.hexdigest()) for u in reg_args["post_logout_redirect_uris"] ] except KeyError: pass self.get_path(reg_args["redirect_uris"], issuer) if client.jwks_uri: reg_args["jwks_uri"] = client.jwks_uri rr = client.register(_pcr["registration_endpoint"], **reg_args) msg = "Registration response: {}" logger.info(msg.format(sanitize(rr.to_dict()))) try: client.behaviour.update(**self.config.CLIENTS[""]["behaviour"]) except KeyError: pass self.client[issuer] = client return client
def test_extra_member_response(self): wf = WebFinger() resp = wf.response('acct:local@domain', 'https://example.com', dummy='foo') # resp should be a JSON document _resp = json.loads(resp) assert _resp['dummy'] == 'foo'
def __init__(self, name="", session_db_factory=None): Server.__init__(self) self.sdb = session_db_factory(name) self.name = name self.client = {} # type: Dict[str, Dict[str, Any]] self.registration_expires_in = 3600 self.host = "" self.webfinger = WebFinger() self.userinfo_signed_response_alg = ""
def _webfinger(provider, request, **kwargs): """Handle webfinger requests.""" params = urlparse.parse_qs(request) if params["rel"][0] == OIC_ISSUER: wf = WebFinger() return Response(wf.response(params["resource"][0], provider.baseurl), headers=[("Content-Type", "application/jrd+json")]) else: return BadRequest("Incorrect webfinger.")
def __init__(self, name=""): Server.__init__(self) self.sdb = SessionDB(name) self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" self.webfinger = WebFinger() self.userinfo_signed_response_alg = ""
def dynamic_client(self, issuer='', userid=''): client = self.client_cls(client_authn_method=CLIENT_AUTHN_METHOD, verify_ssl=self.verify_ssl, **self.jwks_info) if userid: try: issuer = client.wf.discovery_query(userid) except AttributeError: wf = WebFinger(httpd=client) issuer = wf.discovery_query(userid) if not issuer: raise OAuth2Error('Missing issuer') logger.info('issuer: {}'.format(issuer)) if issuer in self.client: return self.client[issuer] else: # Gather OP information _pcr = client.provider_config(issuer) logger.info('Provider info: {}'.format(sanitize(_pcr.to_dict()))) issuer = _pcr['issuer'] # So no hickup later about trailing '/' # register the client _cinfo = self.config.CLIENTS[""]["client_info"] reg_args = copy.copy(_cinfo) h = hashlib.sha256(self.seed) h.update(issuer.encode('utf8')) # issuer has to be bytes base_urls = _cinfo["redirect_uris"] reg_args['redirect_uris'] = [ u.format(base=self.base_url, iss=h.hexdigest()) for u in base_urls] try: reg_args['post_logout_redirect_uris'] = [ u.format(base=self.base_url, iss=h.hexdigest()) for u in reg_args['post_logout_redirect_uris'] ] except KeyError: pass self.get_path(reg_args['redirect_uris'], issuer) if client.jwks_uri: reg_args['jwks_uri'] = client.jwks_uri rr = client.register(_pcr["registration_endpoint"], **reg_args) msg = 'Registration response: {}' logger.info(msg.format(sanitize(rr.to_dict()))) try: client.behaviour.update(**self.config.CLIENTS[""]["behaviour"]) except KeyError: pass self.client[issuer] = client return client
def test_extra_member_response(self): wf = WebFinger() resp = wf.response("acct:local@domain", "https://example.com", dummy="foo") # resp should be a JSON document _resp = json.loads(resp) assert _resp["dummy"] == "foo"
def test_wf4(): wf = WebFinger() jrd0 = wf.load(json.dumps(EX0)) print jrd0 for link in jrd0["links"]: if link["rel"] == "blog": print link["href"] assert link["href"] == "http://blogs.example.com/bob/"
def get(self): params = self.request.query_arguments if params['rel'][0].decode('utf-8') == OIC_ISSUER: wf = WebFinger() self.write( wf.response(params["resource"][0].decode('utf-8'), self.settings['oidc_provider'].baseurl)) else: return self.send_error()
def _webfinger(self): query = request.args try: assert query["rel"] == OIC_ISSUER resource = query["resource"][0] except KeyError: resp = BadRequest("Missing parameter in request") else: wf = WebFinger() resp = Response(wf.response(subject=resource, base=self.provider.baseurl)) return resp
def webfinger(environ, start_response, _): query = parse_qs(environ["QUERY_STRING"]) try: assert query["rel"] == [OIC_ISSUER] resource = query["resource"][0] except KeyError: resp = BadRequest("Missing parameter in request") else: wf = WebFinger() resp = Response(wf.response(subject=resource, base=OAS.baseurl)) return resp(environ, start_response)
def test_query_rel_host_forced(self): wf = WebFinger() query = wf.query( "acct:[email protected]", ["http://webfinger.net/rel/profile-page", "vcard"], host="forced.com:3000", ) assert ( query == "https://forced.com:3000/.well-known/webfinger?resource" "=acct%3Abob%40example.com&rel=http%3A%2F%2Fwebfinger" ".net%2Frel%2Fprofile-page&rel=vcard")
def webfinger(environ): query = parse_qs(environ["QUERY_STRING"]) try: assert query["rel"] == [OIC_ISSUER] resource = query["resource"][0] except KeyError: resp = BadRequest("Missing parameter in request") else: wf = WebFinger() resp = Response(wf.response(subject=resource, base=AUTHZSRV.baseurl)) return resp
def find_srv_discovery_url(self, resource): """ Use Webfinger to find the OP, The input is a unique identifier of the user. Allowed forms are the acct, mail, http and https urls. If no protocol specification is given like if only an email like identifier is given. It will be translated if possible to one of the allowed formats. :param resource: unique identifier of the user. :return: """ wf = WebFinger(httpd=PBase(ca_certs=self.extra["ca_bundle"])) return wf.discovery_query(resource)
def webfinger(self, environ, start_response): query = parse_qs(environ["QUERY_STRING"]) try: rel = query["rel"] resource = query["resource"][0] except KeyError: resp = BadRequest("Missing parameter in request") else: if rel != [OIC_ISSUER]: resp = BadRequest("Bad issuer in request") else: wf = WebFinger() resp = Response( wf.response(subject=resource, base=self.oas.baseurl)) return resp(environ, start_response)
def __init__(self, client_id=None, ca_certs=None, client_prefs=None, client_authn_method=None, keyjar=None, verify_ssl=True): oauth2.Client.__init__(self, client_id, ca_certs, client_authn_method=client_authn_method, keyjar=keyjar, verify_ssl=verify_ssl) self.file_store = "./file/" self.file_uri = "http://localhost/" # OpenID connect specific endpoints for endpoint in ENDPOINTS: setattr(self, endpoint, "") self.id_token = None self.log = None self.request2endpoint = REQUEST2ENDPOINT self.response2error = RESPONSE2ERROR self.grant_class = Grant self.token_class = Token self.provider_info = None self.registration_response = None self.client_prefs = client_prefs or {} self.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.wf = WebFinger(OIC_ISSUER) self.wf.httpd = self self.allow = {} self.post_logout_redirect_uris = [] self.registration_expires = 0 self.registration_access_token = None # Default key by kid for different key types # For instance {"RSA":"abc"} self.kid = {"sig": {}, "enc": {}}
def __init__(self, client_id=None, ca_certs=None, grant_expire_in=600, jwt_keys=None, client_timeout=0, client_prefs=None): oauth2.Client.__init__(self, client_id, ca_certs, grant_expire_in, client_timeout=client_timeout, jwt_keys=jwt_keys) self.file_store = "./file/" self.file_uri = "http://localhost/" # OpenID connect specific endpoints for endpoint in ENDPOINTS: setattr(self, endpoint, "") self.id_token = None self.log = None self.request2endpoint = REQUEST2ENDPOINT self.response2error = RESPONSE2ERROR self.grant_class = Grant self.token_class = Token self.authn_method = AUTHN_METHOD self.provider_info = {} self.client_prefs = client_prefs or {} self.behaviour = {"require_signed_request_object": DEF_SIGN_ALG["openid_request_object"]} self.wf = WebFinger(OIC_ISSUER) self.wf.httpd = self
def __init__(self, client_id=None, ca_certs=None, client_prefs=None, client_authn_method=None, keyjar=None): oauth2.Client.__init__(self, client_id, ca_certs, client_authn_method=client_authn_method, keyjar=keyjar) self.file_store = "./file/" self.file_uri = "http://localhost/" # OpenID connect specific endpoints for endpoint in ENDPOINTS: setattr(self, endpoint, "") self.id_token = None self.log = None self.request2endpoint = REQUEST2ENDPOINT self.response2error = RESPONSE2ERROR self.grant_class = Grant self.token_class = Token self.provider_info = {} self.client_prefs = client_prefs or {} self.behaviour = {"require_signed_request_object": DEF_SIGN_ALG["openid_request_object"]} self.wf = WebFinger(OIC_ISSUER) self.wf.httpd = self self.allow = {} self.post_logout_redirect_uris = []
def webfinger(environ, start_response, session_info, trace, **kwargs): query = parse_qs(environ["QUERY_STRING"]) # Find the identifier session_info["test_id"] = find_identifier(query["resource"][0]) trace.info(HEADER % "WebFinger") trace.request(environ["QUERY_STRING"]) trace.info("QUERY: %s" % (query,)) try: assert query["rel"] == [OIC_ISSUER] resource = query["resource"][0] except AssertionError: errmsg = "Wrong 'rel' value: %s" % query["rel"][0] trace.error(errmsg) resp = BadRequest(errmsg) except KeyError: errmsg = "Missing 'rel' parameter in request" trace.error(errmsg) resp = BadRequest(errmsg) else: wf = WebFinger() p = urlparse(resource) if p.scheme == "acct": l, _ = p.path.split("@") path = pathmap.IDMAP[l.lower()] else: # scheme == http/-s try: path = pathmap.IDMAP[p.path[1:].lower()] except KeyError: path = None if path: _url = os.path.join(kwargs["op_arg"]["baseurl"], session_info["test_id"], path[1:]) resp = Response(wf.response(subject=resource, base=_url), content="application/jrd+json") else: resp = BadRequest("Incorrect resource specification") trace.reply(resp.message) dump_log(session_info, trace) return resp(environ, start_response)
def webfinger(environ, start_response, session_info, trace, **kwargs): query = parse_qs(environ["QUERY_STRING"]) # Find the identifier session_info["test_id"] = find_identifier(query["resource"][0]) trace.info(HEADER % "WebFinger") trace.request(environ["QUERY_STRING"]) trace.info("QUERY: %s" % (query, )) try: assert query["rel"] == [OIC_ISSUER] resource = query["resource"][0] except AssertionError: errmsg = "Wrong 'rel' value: %s" % query["rel"][0] trace.error(errmsg) resp = BadRequest(errmsg) except KeyError: errmsg = "Missing 'rel' parameter in request" trace.error(errmsg) resp = BadRequest(errmsg) else: wf = WebFinger() p = urlparse(resource) if p.scheme == "acct": l, _ = p.path.split("@") path = pathmap.IDMAP[l.lower()] else: # scheme == http/-s try: path = pathmap.IDMAP[p.path[1:].lower()] except KeyError: path = None if path: _url = os.path.join(kwargs["op_arg"]["baseurl"], session_info["test_id"], path[1:]) resp = Response(wf.response(subject=resource, base=_url), content="application/jrd+json") else: resp = BadRequest("Incorrect resource specification") trace.reply(resp.message) dump_log(session_info, trace) return resp(environ, start_response)
def test_wf4(self): EX0 = { "expires": "2012-11-16T19:41:35Z", "subject": "acct:[email protected]", "aliases": [ "http://www.example.com/~bob/" ], "properties": { "http://example.com/ns/role/": "employee" }, "links": [ { "rel": "http://webfinger.net/rel/avatar", "type": "image/jpeg", "href": "http://www.example.com/~bob/bob.jpg" }, { "rel": "http://webfinger.net/rel/profile-page", "href": "http://www.example.com/~bob/" }, { "rel": "blog", "type": "text/html", "href": "http://blogs.example.com/bob/", "titles": { "en-us": "The Magical World of Bob", "fr": "Le monde magique de Bob" } }, { "rel": "vcard", "href": "https://www.example.com/~bob/bob.vcf" } ] } wf = WebFinger() jrd0 = wf.load(json.dumps(EX0)) for link in jrd0["links"]: if link["rel"] == "blog": assert link["href"] == "http://blogs.example.com/bob/" break
def webfinger(environ, start_response, session_info, events, jlog, **kwargs): _query = session_info['parameters'] events.store(EV_REQUEST, Operation("WebFinger", _query)) try: assert _query["rel"] == [OIC_ISSUER] resource = _query["resource"][0] except AssertionError: errmsg = "Wrong 'rel' value: %s" % _query["rel"][0] events.store(EV_FAULT, errmsg) resp = BadRequest(errmsg) except KeyError: errmsg = "Missing 'rel' parameter in request" events.store(EV_FAULT, errmsg) resp = BadRequest(errmsg) else: wf = WebFinger() _url = os.path.join(kwargs["op_arg"]["baseurl"], session_info['oper_id'], session_info["test_id"]) _mesg = wf.response(subject=resource, base=_url) if session_info['test_id'] == 'rp-discovery-webfinger-http-href': _msg = json.loads(_mesg) _msg['links'][0]['href'] = _msg['links'][0]['href'].replace( 'https', 'http') _mesg = json.dumps(_msg) elif session_info['test_id'] == 'rp-discovery-webfinger-unknown-member': _msg = json.loads(_mesg) _msg['dummy'] = 'foobar' _mesg = json.dumps(_msg) resp = Response(_mesg, content="application/jrd+json") events.store(EV_RESPONSE, resp.message) jlog.info(resp2json(resp)) dump_log(session_info, events) return resp(environ, start_response)
def webfinger(environ, start_response, session_info, events, jlog, **kwargs): _query = session_info['parameters'] events.store(EV_REQUEST, Operation("WebFinger", _query)) try: assert _query["rel"] == [OIC_ISSUER] resource = _query["resource"][0] except AssertionError: errmsg = "Wrong 'rel' value: %s" % _query["rel"][0] events.store(EV_FAULT, errmsg) resp = BadRequest(errmsg) except KeyError: errmsg = "Missing 'rel' parameter in request" events.store(EV_FAULT, errmsg) resp = BadRequest(errmsg) else: wf = WebFinger() _url = os.path.join(kwargs["op_arg"]["baseurl"], session_info['oper_id'], session_info["test_id"]) _mesg = wf.response(subject=resource, base=_url) if session_info['test_id'] == 'rp-discovery-webfinger-http-href': _msg = json.loads(_mesg) _msg['links'][0]['href'] = _msg['links'][0]['href'].replace( 'https', 'http') _mesg = json.dumps(_msg) elif session_info[ 'test_id'] == 'rp-discovery-webfinger-unknown-member': _msg = json.loads(_mesg) _msg['dummy'] = 'foobar' _mesg = json.dumps(_msg) resp = Response(_mesg, content="application/jrd+json") events.store(EV_RESPONSE, resp.message) jlog.info(resp2json(resp)) dump_log(session_info, events) return resp(environ, start_response)
def webfinger(environ, start_response, session, trace): query = parse_qs(environ["QUERY_STRING"]) # Find the identifier session["test_id"] = find_identifier(query["resource"][0]) trace.info(HEADER % "WebFinger") trace.request(environ["QUERY_STRING"]) trace.info("QUERY: %s" % (query,)) try: assert query["rel"] == [OIC_ISSUER] resource = query["resource"][0] except AssertionError: errmsg = "Wrong 'rel' value: %s" % query["rel"][0] trace.error(errmsg) resp = BadRequest(errmsg) except KeyError: errmsg = "Missing 'rel' parameter in request" trace.error(errmsg) resp = BadRequest(errmsg) else: wf = WebFinger() p = urlparse(resource) if p.scheme == "acct": l, _ = p.path.split("@") path = pathmap.IDMAP[l] else: # scheme == http/-s path = pathmap.IDMAP[p.path[1:]] _url = os.path.join(OP_ARG["baseurl"], session["test_id"], path[1:]) resp = Response(wf.response(subject=resource, base=_url)) trace.reply(resp.message) dump_log(session, trace) return resp(environ, start_response)
def __init__(self, client_id=None, ca_certs=None, client_prefs=None, client_authn_method=None, keyjar=None, verify_ssl=True): oauth2.Client.__init__(self, client_id, ca_certs, client_authn_method=client_authn_method, keyjar=keyjar, verify_ssl=verify_ssl) self.file_store = "./file/" self.file_uri = "http://localhost/" # OpenID connect specific endpoints for endpoint in ENDPOINTS: setattr(self, endpoint, "") self.id_token = None self.log = None self.request2endpoint = REQUEST2ENDPOINT self.response2error = RESPONSE2ERROR self.grant_class = Grant self.token_class = Token self.provider_info = None self.registration_response = None self.client_prefs = client_prefs or {} self.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]} self.wf = WebFinger(OIC_ISSUER) self.wf.httpd = self self.allow = {} self.post_logout_redirect_uris = [] self.registration_expires = 0 self.registration_access_token = None # Default key by kid for different key types # For instance {"RSA":"abc"} self.kid = {"sig": {}, "enc": {}}
def test_query_device_host_forced(self): wf = WebFinger() query = wf.query(resource="device:p1.example.com", host='forced.com') assert query == 'https://forced.com/.well-known/webfinger' \ '?resource=device%3Ap1.example.com'
class Client(oauth2.Client): _endpoints = ENDPOINTS def __init__( self, client_id=None, ca_certs=None, client_prefs=None, client_authn_method=None, keyjar=None, verify_ssl=True ): oauth2.Client.__init__( self, client_id, ca_certs, client_authn_method=client_authn_method, keyjar=keyjar, verify_ssl=verify_ssl ) self.file_store = "./file/" self.file_uri = "http://localhost/" # OpenID connect specific endpoints for endpoint in ENDPOINTS: setattr(self, endpoint, "") self.id_token = None self.log = None self.request2endpoint = REQUEST2ENDPOINT self.response2error = RESPONSE2ERROR self.grant_class = Grant self.token_class = Token self.provider_info = None self.registration_response = None self.client_prefs = client_prefs or {} self.behaviour = {} self.wf = WebFinger(OIC_ISSUER) self.wf.httpd = self self.allow = {} self.post_logout_redirect_uris = [] self.registration_expires = 0 self.registration_access_token = None self.id_token_max_age = 0 # Default key by kid for different key types # For instance {"RSA":"abc"} self.kid = {"sig": {}, "enc": {}} def _get_id_token(self, **kwargs): try: return kwargs["id_token"] except KeyError: grant = self.get_grant(**kwargs) if grant: try: _scope = kwargs["scope"] except KeyError: _scope = None for token in grant.tokens: if token.scope and _scope: flag = True for item in _scope: try: assert item in token.scope except AssertionError: flag = False break if not flag: break if token.id_token: return token.id_token return None def request_object_encryption(self, msg, **kwargs): try: encalg = kwargs["request_object_encryption_alg"] except KeyError: try: encalg = self.behaviour["request_object_encryption_alg"] except KeyError: return msg try: encenc = kwargs["request_object_encryption_enc"] except KeyError: try: encenc = self.behaviour["request_object_encryption_enc"] except KeyError: raise MissingRequiredAttribute("No request_object_encryption_enc specified") _jwe = JWE(msg, alg=encalg, enc=encenc) _kty = jwe.alg2keytype(encalg) try: _kid = kwargs["enc_kid"] except KeyError: _kid = "" if "target" not in kwargs: raise MissingRequiredAttribute("No target specified") if _kid: _keys = self.keyjar.get_encrypt_key(_kty, owner=kwargs["target"], kid=_kid) _jwe["kid"] = _kid else: _keys = self.keyjar.get_encrypt_key(_kty, owner=kwargs["target"]) return _jwe.encrypt(_keys) 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" else: request_param = "request" del kwargs["request_method"] areq = oauth2.Client.construct_AuthorizationRequest(self, request, request_args, extra_args, **kwargs) if request_param: alg = None for arg in ["request_object_signing_alg", "algorithm"]: try: # Trumps everything alg = kwargs[arg] except KeyError: pass else: break if not alg: try: alg = self.behaviour["request_object_signing_alg"] except KeyError: alg = "none" kwargs["request_object_signing_alg"] = alg if "keys" not in kwargs and alg and alg != "none": _kty = jws.alg2keytype(alg) try: _kid = kwargs["sig_kid"] except KeyError: _kid = self.kid["sig"].get(_kty, None) kwargs["keys"] = self.keyjar.get_signing_key(_kty, kid=_kid) _req = make_openid_request(areq, **kwargs) # Should the request be encrypted _req = self.request_object_encryption(_req, **kwargs) if request_param == "request": areq["request"] = _req else: _filedir = kwargs["local_dir"] if not os.path.isdir(_filedir): os.makedirs(_filedir) _webpath = kwargs["base_path"] _name = rndstr(10) + ".jwt" 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 # noinspection PyUnusedLocal def construct_AccessTokenRequest(self, request=AccessTokenRequest, request_args=None, extra_args=None, **kwargs): return oauth2.Client.construct_AccessTokenRequest(self, request, request_args, extra_args, **kwargs) def construct_RefreshAccessTokenRequest( self, request=RefreshAccessTokenRequest, request_args=None, extra_args=None, **kwargs ): return oauth2.Client.construct_RefreshAccessTokenRequest(self, request, request_args, extra_args, **kwargs) def construct_UserInfoRequest(self, request=UserInfoRequest, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} if "access_token" in request_args: pass else: if "scope" not in kwargs: kwargs["scope"] = "openid" token = self.get_token(**kwargs) if token is None: raise PyoidcError("No valid token available") request_args["access_token"] = token.access_token return self.construct_request(request, request_args, extra_args) # noinspection PyUnusedLocal def construct_RegistrationRequest(self, request=RegistrationRequest, request_args=None, extra_args=None, **kwargs): return self.construct_request(request, request_args, extra_args) # noinspection PyUnusedLocal def construct_RefreshSessionRequest( self, request=RefreshSessionRequest, request_args=None, extra_args=None, **kwargs ): return self.construct_request(request, request_args, extra_args) def _id_token_based(self, request, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} try: _prop = kwargs["prop"] except KeyError: _prop = "id_token" if _prop in request_args: pass else: id_token = self._get_id_token(**kwargs) if id_token is None: raise PyoidcError("No valid id token available") request_args[_prop] = id_token return self.construct_request(request, request_args, extra_args) def construct_CheckSessionRequest(self, request=CheckSessionRequest, request_args=None, extra_args=None, **kwargs): return self._id_token_based(request, request_args, extra_args, **kwargs) def construct_CheckIDRequest(self, request=CheckIDRequest, request_args=None, extra_args=None, **kwargs): # access_token is where the id_token will be placed return self._id_token_based(request, request_args, extra_args, prop="access_token", **kwargs) def construct_EndSessionRequest(self, request=EndSessionRequest, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} if "state" in kwargs: request_args["state"] = kwargs["state"] elif "state" in request_args: kwargs["state"] = request_args["state"] # if "redirect_url" not in request_args: # request_args["redirect_url"] = self.redirect_url return self._id_token_based(request, request_args, extra_args, **kwargs) # ------------------------------------------------------------------------ def authorization_request_info(self, request_args=None, extra_args=None, **kwargs): return self.request_info(AuthorizationRequest, "GET", request_args, extra_args, **kwargs) # ------------------------------------------------------------------------ def do_authorization_request( self, request=AuthorizationRequest, state="", body_type="", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=AuthorizationResponse, ): algs = self.sign_enc_algs("id_token") return oauth2.Client.do_authorization_request( self, request, state, body_type, method, request_args, extra_args, http_args, response_cls, algs=algs ) def do_access_token_request( self, request=AccessTokenRequest, scope="", state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=AccessTokenResponse, authn_method="client_secret_basic", **kwargs ): return oauth2.Client.do_access_token_request( self, request, scope, state, body_type, method, request_args, extra_args, http_args, response_cls, authn_method, **kwargs ) def do_access_token_refresh( self, request=RefreshAccessTokenRequest, state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=AccessTokenResponse, **kwargs ): return oauth2.Client.do_access_token_refresh( self, request, state, body_type, method, request_args, extra_args, http_args, response_cls, **kwargs ) def do_registration_request( self, request=RegistrationRequest, scope="", state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=None, ): url, body, ht_args, csi = self.request_info( request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state ) if http_args is None: http_args = ht_args else: http_args.update(http_args) if response_cls is None: response_cls = RegistrationResponse response = self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) return response def do_check_session_request( self, request=CheckSessionRequest, scope="", state="", body_type="json", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=IdToken, ): url, body, ht_args, csi = self.request_info( request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state ) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def do_check_id_request( self, request=CheckIDRequest, scope="", state="", body_type="json", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=IdToken, ): url, body, ht_args, csi = self.request_info( request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state ) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def do_end_session_request( self, request=EndSessionRequest, scope="", state="", body_type="", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=None, ): url, body, ht_args, csi = self.request_info( request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state ) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def user_info_request(self, method="GET", state="", scope="", **kwargs): uir = UserInfoRequest() logger.debug("[user_info_request]: kwargs:%s" % (kwargs,)) if "token" in kwargs: if kwargs["token"]: uir["access_token"] = kwargs["token"] token = Token() token.token_type = "Bearer" token.access_token = kwargs["token"] kwargs["behavior"] = "use_authorization_header" else: # What to do ? Need a callback token = None elif "access_token" in kwargs and kwargs["access_token"]: uir["access_token"] = kwargs["access_token"] del kwargs["access_token"] token = None else: token = self.grant[state].get_token(scope) if token.is_valid(): uir["access_token"] = token.access_token if token.token_type == "Bearer" and method == "GET": kwargs["behavior"] = "use_authorization_header" else: # raise oauth2.OldAccessToken if self.log: self.log.info("do access token refresh") try: self.do_access_token_refresh(token=token) token = self.grant[state].get_token(scope) uir["access_token"] = token.access_token except Exception: raise uri = self._endpoint("userinfo_endpoint", **kwargs) # If access token is a bearer token it might be sent in the # authorization header # 3-ways of sending the access_token: # - POST with token in authorization header # - POST with token in message body # - GET with token in authorization header if "behavior" in kwargs: _behav = kwargs["behavior"] _token = uir["access_token"] try: _ttype = kwargs["token_type"] except KeyError: try: _ttype = token.token_type except AttributeError: raise MissingParameter("Unspecified token type") # use_authorization_header, token_in_message_body if "use_authorization_header" in _behav and _ttype == "Bearer": bh = "Bearer %s" % _token if "headers" in kwargs: kwargs["headers"].update({"Authorization": bh}) else: kwargs["headers"] = {"Authorization": bh} if "token_in_message_body" not in _behav: # remove the token from the request del uir["access_token"] path, body, kwargs = get_or_post(uri, method, uir, **kwargs) h_args = dict([(k, v) for k, v in kwargs.items() if k in HTTP_ARGS]) return path, body, method, h_args 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) _txt = resp.text if sformat == "json": res = _schema().from_json(txt=_txt) else: res = _schema().from_jwt(_txt, keyjar=self.keyjar, sender=self.provider_info["issuer"]) self.store_response(res, _txt) return res def get_userinfo_claims(self, access_token, endpoint, method="POST", schema_class=OpenIDSchema, **kwargs): uir = UserInfoRequest(access_token=access_token) h_args = dict([(k, v) for k, v in kwargs.items() if k in HTTP_ARGS]) if "authn_method" in kwargs: http_args = self.init_authentication_method(**kwargs) else: # If nothing defined this is the default http_args = self.init_authentication_method(uir, "bearer_header", **kwargs) h_args.update(http_args) path, body, kwargs = get_or_post(endpoint, method, uir, **kwargs) try: resp = self.http_request(path, method, data=body, **h_args) except oauth2.MissingRequiredAttribute: raise if resp.status_code == 200: assert "application/json" in resp.headers["content-type"] 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)) res = schema_class().from_json(txt=resp.text) self.store_response(res, resp.txt) return res def handle_provider_config(self, pcr, issuer, keys=True, endpoints=True): """ Deal with Provider Config Response :param pcr: The ProviderConfigResponse instance :param issuer: The one I thought should be the issuer of the config :param keys: Should I deal with keys :param endpoints: Should I deal with endpoints, that is store them as attributes in self. """ if "issuer" in pcr: _pcr_issuer = pcr["issuer"] if pcr["issuer"].endswith("/"): if issuer.endswith("/"): _issuer = issuer else: _issuer = issuer + "/" else: if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer try: _ = self.allow["issuer_mismatch"] except KeyError: try: assert _issuer == _pcr_issuer except AssertionError: raise IssuerMismatch("'%s' != '%s'" % (_issuer, _pcr_issuer), pcr) self.provider_info = pcr else: _pcr_issuer = issuer if endpoints: for key, val in pcr.items(): if key.endswith("_endpoint"): setattr(self, key, val) if keys: if self.keyjar is None: self.keyjar = KeyJar(verify_ssl=self.verify_ssl) self.keyjar.load_keys(pcr, _pcr_issuer) def provider_config( self, issuer, keys=True, endpoints=True, response_cls=ProviderConfigurationResponse, serv_pattern=OIDCONF_PATTERN, ): if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer url = serv_pattern % _issuer pcr = None r = self.http_request(url) if r.status_code == 200: try: pcr = response_cls().from_json(r.text) except: logger.error("Faulty provider config response: {}".format(r.text)) elif r.status_code == 302 or r.status_code == 301: while r.status_code == 302 or r.status_code == 301: r = self.http_request(r.headers["location"]) if r.status_code == 200: pcr = response_cls().from_json(r.text) break # logger.debug("Provider info: %s" % pcr) if pcr is None: raise PyoidcError("Trying '%s', status %s" % (url, r.status_code)) self.store_response(pcr, r.text) self.handle_provider_config(pcr, issuer, keys, endpoints) return pcr def unpack_aggregated_claims(self, userinfo): if userinfo["_claim_sources"]: for csrc, spec in userinfo["_claim_sources"].items(): if "JWT" in spec: aggregated_claims = Message().from_jwt(spec["JWT"].encode("utf-8"), keyjar=self.keyjar, sender=csrc) claims = [value for value, src in userinfo["_claim_names"].items() if src == csrc] assert claims == aggregated_claims.keys() for key, vals in aggregated_claims.items(): userinfo[key] = vals return userinfo def fetch_distributed_claims(self, userinfo, callback=None): for csrc, spec in userinfo["_claim_sources"].items(): if "endpoint" in spec: if "access_token" in spec: _uinfo = self.do_user_info_request(token=spec["access_token"], userinfo_endpoint=spec["endpoint"]) else: _uinfo = self.do_user_info_request(token=callback(csrc), userinfo_endpoint=spec["endpoint"]) claims = [value for value, src in userinfo["_claim_names"].items() if src == csrc] assert claims == _uinfo.keys() for key, vals in _uinfo.items(): userinfo[key] = vals return userinfo def verify_alg_support(self, alg, usage, other): """ Verifies that the algorithm to be used are supported by the other side. :param alg: The algorithm specification :param usage: In which context the 'alg' will be used. The following values are supported: - userinfo - id_token - request_object - token_endpoint_auth :param other: The identifier for the other side :return: True or False """ try: _pcr = self.provider_info supported = _pcr["%s_algs_supported" % usage] except KeyError: try: supported = getattr(self, "%s_algs_supported" % usage) except AttributeError: supported = None if supported is None: return True else: if alg in supported: return True else: return False def match_preferences(self, pcr=None, issuer=None): """ Match the clients preferences against what the provider can do. :param pcr: Provider configuration response if available :param issuer: The issuer identifier """ if not pcr: pcr = self.provider_info regreq = RegistrationRequest for _pref, _prov in PREFERENCE2PROVIDER.items(): try: vals = self.client_prefs[_pref] except KeyError: continue try: _pvals = pcr[_prov] except KeyError: try: self.behaviour[_pref] = PROVIDER_DEFAULT[_pref] except KeyError: # self.behaviour[_pref]= vals[0] if isinstance(pcr.c_param[_prov][0], list): self.behaviour[_pref] = [] else: self.behaviour[_pref] = None continue if isinstance(vals, basestring): if vals in _pvals: self.behaviour[_pref] = vals else: vtyp = regreq.c_param[_pref] if isinstance(vtyp[0], list): self.behaviour[_pref] = [] for val in vals: if val in _pvals: self.behaviour[_pref].append(val) else: for val in vals: if val in _pvals: self.behaviour[_pref] = val break if _pref not in self.behaviour: raise ConfigurationError("OP couldn't match preference:%s" % _pref, pcr) for key, val in self.client_prefs.items(): if key in self.behaviour: continue try: vtyp = regreq.c_param[key] if isinstance(vtyp[0], list): pass elif isinstance(val, list) and not isinstance(val, basestring): val = val[0] except KeyError: pass if key not in PREFERENCE2PROVIDER: self.behaviour[key] = val def store_registration_info(self, reginfo): self.registration_response = reginfo if "token_endpoint_auth_method" not in self.registration_response: self.registration_response["token_endpoint_auth_method"] = "client_secret_post" self.client_id = reginfo["client_id"] try: self.client_secret = reginfo["client_secret"] except KeyError: # Not required pass else: try: self.registration_expires = reginfo["client_secret_expires_at"] except KeyError: pass try: self.registration_access_token = reginfo["registration_access_token"] except KeyError: pass def handle_registration_info(self, response): if response.status_code == 200: resp = RegistrationResponse().deserialize(response.text, "json") self.store_response(resp, response.text) self.store_registration_info(resp) else: err = ErrorResponse().deserialize(response.text, "json") raise PyoidcError("Registration failed: %s" % err.to_json()) return resp def registration_read(self, url="", registration_access_token=None): if not url: url = self.registration_response["registration_client_uri"] if not registration_access_token: registration_access_token = self.registration_access_token headers = [("Authorization", "Bearer %s" % registration_access_token)] rsp = self.http_request(url, "GET", headers=headers) return self.handle_registration_info(rsp) def create_registration_request(self, **kwargs): """ Create a registration request :param kwargs: parameters to the registration request :return: """ req = RegistrationRequest() for prop in req.parameters(): try: req[prop] = kwargs[prop] except KeyError: try: req[prop] = self.behaviour[prop] except KeyError: pass if "post_logout_redirect_uris" not in req: try: req["post_logout_redirect_uris"] = self.post_logout_redirect_uris except AttributeError: pass if "redirect_uris" not in req: try: req["redirect_uris"] = self.redirect_uris except AttributeError: raise MissingRequiredAttribute("redirect_uris", req) return req def register(self, url, **kwargs): """ Register the client at an OP :param url: The OPs registration endpoint :param kwargs: parameters to the registration request :return: """ req = self.create_registration_request(**kwargs) headers = {"content-type": "application/json"} rsp = self.http_request(url, "POST", data=req.to_json(), headers=headers) return self.handle_registration_info(rsp) def normalization(self, principal, idtype="mail"): if idtype == "mail": (local, domain) = principal.split("@") subject = "acct:%s" % principal elif idtype == "url": p = urlparse(principal) domain = p.netloc subject = principal else: domain = "" subject = principal return subject, domain def discover(self, principal): # subject, host = self.normalization(principal) return self.wf.discovery_query(principal) def sign_enc_algs(self, typ): resp = {} for key, val in PARAMMAP.items(): try: resp[key] = self.registration_response[val % typ] except (TypeError, KeyError): if key == "sign": resp[key] = DEF_SIGN_ALG["id_token"] return resp def _verify_id_token(self, id_token, nonce="", acr_values=None, auth_time=0, max_age=0): """ If the JWT alg Header Parameter uses a MAC based algorithm s uch as HS256, HS384, or HS512, the octets of the UTF-8 representation of the client_secret corresponding to the client_id contained in the aud (audience) Claim are used as the key to validate the signature. For MAC based algorithms, the behavior is unspecified if the aud is multi-valued or if an azp value is present that is different than the aud value. :param id_token: The ID Token tp check :param nonce: The nonce specified in the authorization request :param acr_values: Asked for acr values :param auth_time: An auth_time claim :param max_age: Max age of authentication """ try: assert self.provider_info["issuer"] == id_token["iss"] except AssertionError: raise OtherError("issuer != iss") _now = time_util.utc_time_sans_frac() try: assert _now < id_token["exp"] except AssertionError: raise OtherError("Passed best before date") if self.id_token_max_age: try: assert _now < int(id_token["iat"]) + self.id_token_max_age except AssertionError: raise OtherError("I think this ID token is to old") if nonce: try: assert nonce == id_token["nonce"] except AssertionError: raise OtherError("nonce mismatch") if acr_values: try: assert id_token["acr"] in acr_values except AssertionError: raise OtherError("acr mismatch") if max_age: try: assert _now < int(id_token["auth_time"]) + max_age except AssertionError: raise AuthnToOld("To old authentication") if auth_time: if not claims_match(id_token["auth_time"], {"auth_time": auth_time}): raise AuthnToOld("To old authentication") def verify_id_token(self, id_token, authn_req): kwa = {} try: kwa["nonce"] = authn_req["nonce"] except KeyError: pass for param in ["acr_values", "max_age"]: try: kwa[param] = authn_req[param] except KeyError: pass self._verify_id_token(id_token, **kwa)
class Client(oauth2.Client): _endpoints = ENDPOINTS def __init__(self, client_id=None, ca_certs=None, client_prefs=None, client_authn_method=None, keyjar=None, verify_ssl=True): oauth2.Client.__init__(self, client_id, ca_certs, client_authn_method=client_authn_method, keyjar=keyjar, verify_ssl=verify_ssl) self.file_store = "./file/" self.file_uri = "http://localhost/" # OpenID connect specific endpoints for endpoint in ENDPOINTS: setattr(self, endpoint, "") self.id_token = None self.log = None self.request2endpoint = REQUEST2ENDPOINT self.response2error = RESPONSE2ERROR self.grant_class = Grant self.token_class = Token self.provider_info = None self.registration_response = None self.client_prefs = client_prefs or {} self.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.wf = WebFinger(OIC_ISSUER) self.wf.httpd = self self.allow = {} self.post_logout_redirect_uris = [] self.registration_expires = 0 self.registration_access_token = None # Default key by kid for different key types # For instance {"RSA":"abc"} self.kid = {"sig": {}, "enc": {}} def _get_id_token(self, **kwargs): try: return kwargs["id_token"] except KeyError: grant = self.get_grant(**kwargs) if grant: try: _scope = kwargs["scope"] except KeyError: _scope = None for token in grant.tokens: if token.scope and _scope: flag = True for item in _scope: try: assert item in token.scope except AssertionError: flag = False break if not flag: break if token.id_token: return token.id_token return None 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 #noinspection PyUnusedLocal def construct_AccessTokenRequest(self, request=AccessTokenRequest, request_args=None, extra_args=None, **kwargs): return oauth2.Client.construct_AccessTokenRequest( self, request, request_args, extra_args, **kwargs) def construct_RefreshAccessTokenRequest(self, request=RefreshAccessTokenRequest, request_args=None, extra_args=None, **kwargs): return oauth2.Client.construct_RefreshAccessTokenRequest( self, request, request_args, extra_args, **kwargs) def construct_UserInfoRequest(self, request=UserInfoRequest, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} if "access_token" in request_args: pass else: if "scope" not in kwargs: kwargs["scope"] = "openid" token = self.get_token(**kwargs) if token is None: raise PyoidcError("No valid token available") request_args["access_token"] = token.access_token return self.construct_request(request, request_args, extra_args) #noinspection PyUnusedLocal def construct_RegistrationRequest(self, request=RegistrationRequest, request_args=None, extra_args=None, **kwargs): return self.construct_request(request, request_args, extra_args) #noinspection PyUnusedLocal def construct_RefreshSessionRequest(self, request=RefreshSessionRequest, request_args=None, extra_args=None, **kwargs): return self.construct_request(request, request_args, extra_args) def _id_token_based(self, request, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} try: _prop = kwargs["prop"] except KeyError: _prop = "id_token" if _prop in request_args: pass else: id_token = self._get_id_token(**kwargs) if id_token is None: raise PyoidcError("No valid id token available") request_args[_prop] = id_token return self.construct_request(request, request_args, extra_args) def construct_CheckSessionRequest(self, request=CheckSessionRequest, request_args=None, extra_args=None, **kwargs): return self._id_token_based(request, request_args, extra_args, **kwargs) def construct_CheckIDRequest(self, request=CheckIDRequest, request_args=None, extra_args=None, **kwargs): # access_token is where the id_token will be placed return self._id_token_based(request, request_args, extra_args, prop="access_token", **kwargs) def construct_EndSessionRequest(self, request=EndSessionRequest, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} if "state" in kwargs: request_args["state"] = kwargs["state"] elif "state" in request_args: kwargs["state"] = request_args["state"] # if "redirect_url" not in request_args: # request_args["redirect_url"] = self.redirect_url return self._id_token_based(request, request_args, extra_args, **kwargs) # ------------------------------------------------------------------------ def authorization_request_info(self, request_args=None, extra_args=None, **kwargs): return self.request_info(AuthorizationRequest, "GET", request_args, extra_args, **kwargs) # ------------------------------------------------------------------------ def do_authorization_request(self, request=AuthorizationRequest, state="", body_type="", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=AuthorizationResponse): return oauth2.Client.do_authorization_request(self, request, state, body_type, method, request_args, extra_args, http_args, response_cls) def do_access_token_request(self, request=AccessTokenRequest, scope="", state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=AccessTokenResponse, authn_method="", **kwargs): return oauth2.Client.do_access_token_request(self, request, scope, state, body_type, method, request_args, extra_args, http_args, response_cls, authn_method, **kwargs) def do_access_token_refresh(self, request=RefreshAccessTokenRequest, state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=AccessTokenResponse, **kwargs): return oauth2.Client.do_access_token_refresh(self, request, state, body_type, method, request_args, extra_args, http_args, response_cls, **kwargs) def do_registration_request(self, request=RegistrationRequest, scope="", state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=None): url, body, ht_args, csi = self.request_info(request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state) if http_args is None: http_args = ht_args else: http_args.update(http_args) if response_cls is None: response_cls = RegistrationResponse response = self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) return response def do_check_session_request(self, request=CheckSessionRequest, scope="", state="", body_type="json", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=IdToken): url, body, ht_args, csi = self.request_info(request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def do_check_id_request(self, request=CheckIDRequest, scope="", state="", body_type="json", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=IdToken): url, body, ht_args, csi = self.request_info(request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def do_end_session_request(self, request=EndSessionRequest, scope="", state="", body_type="", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=None): url, body, ht_args, csi = self.request_info(request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def user_info_request(self, method="GET", state="", scope="", **kwargs): uir = UserInfoRequest() logger.debug("[user_info_request]: kwargs:%s" % (kwargs, )) if "token" in kwargs: if kwargs["token"]: uir["access_token"] = kwargs["token"] token = Token() token.token_type = "Bearer" token.access_token = kwargs["token"] kwargs["behavior"] = "use_authorization_header" else: # What to do ? Need a callback token = None elif "access_token" in kwargs and kwargs["access_token"]: uir["access_token"] = kwargs["access_token"] del kwargs["access_token"] token = None else: token = self.grant[state].get_token(scope) if token.is_valid(): uir["access_token"] = token.access_token if token.token_type == "Bearer" and method == "GET": kwargs["behavior"] = "use_authorization_header" else: # raise oauth2.OldAccessToken if self.log: self.log.info("do access token refresh") try: self.do_access_token_refresh(token=token) token = self.grant[state].get_token(scope) uir["access_token"] = token.access_token except Exception: raise uri = self._endpoint("userinfo_endpoint", **kwargs) # If access token is a bearer token it might be sent in the # authorization header # 3-ways of sending the access_token: # - POST with token in authorization header # - POST with token in message body # - GET with token in authorization header if "behavior" in kwargs: _behav = kwargs["behavior"] _token = uir["access_token"] try: _ttype = kwargs["token_type"] except KeyError: try: _ttype = token.token_type except AttributeError: raise MissingParameter("Unspecified token type") # use_authorization_header, token_in_message_body if "use_authorization_header" in _behav and _ttype == "Bearer": bh = "Bearer %s" % _token if "headers" in kwargs: kwargs["headers"].update({"Authorization": bh}) else: kwargs["headers"] = {"Authorization": bh} if not "token_in_message_body" in _behav: # remove the token from the request del uir["access_token"] path, body, kwargs = self.get_or_post(uri, method, uir, **kwargs) h_args = dict([(k, v) for k, v in kwargs.items() if k in HTTP_ARGS]) return path, body, method, h_args 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 get_userinfo_claims(self, access_token, endpoint, method="POST", schema_class=OpenIDSchema, **kwargs): uir = UserInfoRequest(access_token=access_token) h_args = dict([(k, v) for k, v in kwargs.items() if k in HTTP_ARGS]) if "authn_method" in kwargs: http_args = self.init_authentication_method(**kwargs) else: # If nothing defined this is the default http_args = self.init_authentication_method( uir, "bearer_header", **kwargs) h_args.update(http_args) path, body, kwargs = self.get_or_post(endpoint, method, uir, **kwargs) try: resp = self.http_request(path, method, data=body, **h_args) except oauth2.MissingRequiredAttribute: raise if resp.status_code == 200: assert "application/json" in resp.headers["content-type"] elif resp.status_code == 500: raise PyoidcError("ERROR: Something went wrong: %s" % resp.text) else: raise PyoidcError("ERROR: Something went wrong [%s]" % resp.status_code) return schema_class().from_json(txt=resp.text) def handle_provider_config(self, pcr, issuer, keys=True, endpoints=True): """ Deal with Provider Config Response :param pcr: The ProviderConfigResponse instance :param issuer: The one I thought should be the issuer of the config :param keys: Should I deal with keys :param endpoints: Should I deal with endpoints, that is store them as attributes in self. """ if "issuer" in pcr: _pcr_issuer = pcr["issuer"] if pcr["issuer"].endswith("/"): if issuer.endswith("/"): _issuer = issuer else: _issuer = issuer + "/" else: if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer try: _ = self.allow["issuer_mismatch"] except KeyError: try: assert _issuer == _pcr_issuer except AssertionError: raise IssuerMismatch( "'%s' != '%s'" % (_issuer, _pcr_issuer), pcr) self.provider_info = pcr else: _pcr_issuer = issuer if endpoints: for key, val in pcr.items(): if key.endswith("_endpoint"): setattr(self, key, val) if keys: if self.keyjar is None: self.keyjar = KeyJar(verify_ssl=self.verify_ssl) self.keyjar.load_keys(pcr, _pcr_issuer) def provider_config(self, issuer, keys=True, endpoints=True, response_cls=ProviderConfigurationResponse, serv_pattern=OIDCONF_PATTERN): if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer url = serv_pattern % _issuer pcr = None r = self.http_request(url) if r.status_code == 200: pcr = response_cls().from_json(r.text) elif r.status_code == 302: while r.status_code == 302: r = self.http_request(r.headers["location"]) if r.status_code == 200: pcr = response_cls().from_json(r.text) break #logger.debug("Provider info: %s" % pcr) if pcr is None: raise PyoidcError("Trying '%s', status %s" % (url, r.status_code)) self.handle_provider_config(pcr, issuer, keys, endpoints) return pcr def unpack_aggregated_claims(self, userinfo): if userinfo._claim_sources: for csrc, spec in userinfo._claim_sources.items(): if "JWT" in spec: if not csrc in self.keyjar: self.provider_config(csrc, endpoints=False) keycol = self.keyjar.get_verify_key(owner=csrc) for typ, keyl in self.keyjar.get_verify_key().items(): try: keycol[typ].extend(keyl) except KeyError: keycol[typ] = keyl info = json.loads(JWS().verify(str(spec["JWT"]), keycol)) attr = [ n for n, s in userinfo._claim_names.items() if s == csrc ] assert attr == info.keys() for key, vals in info.items(): userinfo[key] = vals return userinfo def fetch_distributed_claims(self, userinfo, callback=None): for csrc, spec in userinfo._claim_sources.items(): if "endpoint" in spec: #pcr = self.provider_config(csrc, keys=False, endpoints=False) if "access_token" in spec: _uinfo = self.do_user_info_request( token=spec["access_token"], userinfo_endpoint=spec["endpoint"]) else: _uinfo = self.do_user_info_request( token=callback(csrc), userinfo_endpoint=spec["endpoint"]) attr = [ n for n, s in userinfo._claim_names.items() if s == csrc ] assert attr == _uinfo.keys() for key, vals in _uinfo.items(): userinfo[key] = vals return userinfo def verify_alg_support(self, alg, usage, other): """ Verifies that the algorithm to be used are supported by the other side. :param alg: The algorithm specification :param usage: In which context the 'alg' will be used. The following values are supported: - userinfo - id_token - request_object - token_endpoint_auth :param other: The identifier for the other side :return: True or False """ try: _pcr = self.provider_info supported = _pcr["%s_algs_supported" % usage] except KeyError: try: supported = getattr(self, "%s_algs_supported" % usage) except AttributeError: supported = None if supported is None: return True else: if alg in supported: return True else: return False def match_preferences(self, pcr=None, issuer=None): """ Match the clients preferences against what the provider can do. :param pcr: Provider configuration response if available :param issuer: The issuer identifier """ if not pcr: pcr = self.provider_info regreq = RegistrationRequest for _pref, _prov in PREFERENCE2PROVIDER.items(): try: vals = self.client_prefs[_pref] except KeyError: continue try: _pvals = pcr[_prov] except KeyError: try: self.behaviour[_pref] = PROVIDER_DEFAULT[_pref] except KeyError: #self.behaviour[_pref]= vals[0] if isinstance(pcr.c_param[_prov][0], list): self.behaviour[_pref] = [] else: self.behaviour[_pref] = None continue if isinstance(vals, basestring): if vals in _pvals: self.behaviour[_pref] = vals else: vtyp = regreq.c_param[_pref] if isinstance(vtyp[0], list): _list = True else: _list = False for val in vals: if val in _pvals: if not _list: self.behaviour[_pref] = val break else: try: self.behaviour[_pref].append(val) except KeyError: self.behaviour[_pref] = [val] if _pref not in self.behaviour: raise ConfigurationError( "OP couldn't match preference:%s" % _pref, pcr) for key, val in self.client_prefs.items(): if key in self.behaviour: continue try: vtyp = regreq.c_param[key] if isinstance(vtyp[0], list): pass elif isinstance(val, list) and not isinstance(val, basestring): val = val[0] except KeyError: pass if key not in PREFERENCE2PROVIDER: self.behaviour[key] = val def store_registration_info(self, reginfo): self.registration_response = reginfo if "token_endpoint_auth_method" not in self.registration_response: self.registration_response[ "token_endpoint_auth_method"] = "client_secret_post" self.client_secret = reginfo["client_secret"] self.client_id = reginfo["client_id"] try: self.registration_expires = reginfo["client_secret_expires_at"] except KeyError: pass try: self.registration_access_token = reginfo[ "registration_access_token"] except KeyError: pass def handle_registration_info(self, response): if response.status_code == 200: resp = RegistrationResponse().deserialize(response.text, "json") self.store_registration_info(resp) else: err = ErrorResponse().deserialize(response.text, "json") raise PyoidcError("Registration failed: %s" % err.get_json()) return resp def registration_read(self, url="", registration_access_token=None): if not url: url = self.registration_response["registration_client_uri"] if not registration_access_token: registration_access_token = self.registration_access_token headers = [("Authorization", "Bearer %s" % registration_access_token)] rsp = self.http_request(url, "GET", headers=headers) return self.handle_registration_info(rsp) def create_registration_request(self, **kwargs): """ Create a registration request :param kwargs: parameters to the registration request :return: """ req = RegistrationRequest() for prop in req.parameters(): try: req[prop] = kwargs[prop] except KeyError: try: req[prop] = self.behaviour[prop] except KeyError: pass if "post_logout_redirect_uris" not in req: try: req["post_logout_redirect_uris"] = self.post_logout_redirect_uris except AttributeError: pass if "redirect_uris" not in req: try: req["redirect_uris"] = self.redirect_uris except AttributeError: raise MissingRequiredAttribute("redirect_uris", req) return req def register(self, url, **kwargs): """ Register the client at an OP :param url: The OPs registration endpoint :param kwargs: parameters to the registration request :return: """ req = self.create_registration_request(**kwargs) headers = {"content-type": "application/json"} rsp = self.http_request(url, "POST", data=req.to_json(), headers=headers) return self.handle_registration_info(rsp) def normalization(self, principal, idtype="mail"): if idtype == "mail": (local, domain) = principal.split("@") subject = "acct:%s" % principal elif idtype == "url": p = urlparse.urlparse(principal) domain = p.netloc subject = principal else: domain = "" subject = principal return subject, domain def discover(self, principal): #subject, host = self.normalization(principal) return self.wf.discovery_query(principal)
class MyFakeOICServer(Server): def __init__(self, name=""): Server.__init__(self) self.sdb = SessionDB(name) self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" self.webfinger = WebFinger() self.userinfo_signed_response_alg = "" def http_request(self, path, method="GET", **kwargs): part = urlparse(path) path = part[2] query = part[4] self.host = "%s://%s" % (part.scheme, part.netloc) response = Response response.status_code = 500 response.text = "" if path == ENDPOINT["authorization_endpoint"]: assert method == "GET" response = self.authorization_endpoint(query) elif path == ENDPOINT["token_endpoint"]: assert method == "POST" response = self.token_endpoint(kwargs["data"]) elif path == ENDPOINT["user_info_endpoint"]: assert method == "POST" response = self.userinfo_endpoint(kwargs["data"]) elif path == ENDPOINT["refresh_session_endpoint"]: assert method == "GET" response = self.refresh_session_endpoint(query) elif path == ENDPOINT["check_session_endpoint"]: assert method == "GET" response = self.check_session_endpoint(query) elif path == ENDPOINT["end_session_endpoint"]: assert method == "GET" response = self.end_session_endpoint(query) elif path == ENDPOINT["registration_endpoint"]: if method == "POST": response = self.registration_endpoint(kwargs["data"]) elif path == "/.well-known/webfinger": assert method == "GET" qdict = parse_qs(query) response.status_code = 200 response.text = self.webfinger.response(qdict["resource"][0], "%s/" % self.name) elif path == "/.well-known/openid-configuration": assert method == "GET" response = self.openid_conf() return response 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"] if 'offline_access' in _info['scope']: _dict = self.sdb.upgrade_to_token(grant, issue_refresh=True) else: _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() _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 token_endpoint(self, data): if "grant_type=refresh_token" in data: req = self.parse_refresh_token_request(body=data) _info = self.sdb.refresh_token(req["refresh_token"], req['client_id']) elif "grant_type=authorization_code" in data: req = self.parse_token_request(body=data) if 'offline_access' in self.sdb[req['code']]['scope']: _info = self.sdb.upgrade_to_token(req["code"], issue_refresh=True) else: _info = self.sdb.upgrade_to_token(req["code"]) else: response = TokenErrorResponse(error="unsupported_grant_type") return response, "" resp = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) response = Response() 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 registration_endpoint(self, data): try: req = self.parse_registration_request(data, "json") except ValueError: req = self.parse_registration_request(data) client_secret = rndstr() expires = utc_time_sans_frac() + self.registration_expires_in kwargs = {} if "client_id" not in req: client_id = rndstr(10) registration_access_token = rndstr(20) _client_info = req.to_dict() kwargs.update(_client_info) _client_info.update({ "client_secret": client_secret, "info": req.to_dict(), "expires": expires, "registration_access_token": registration_access_token, "registration_client_uri": "register_endpoint" }) self.client[client_id] = _client_info kwargs["registration_access_token"] = registration_access_token kwargs["registration_client_uri"] = "register_endpoint" try: del kwargs["operation"] except KeyError: pass else: client_id = req.client_id _cinfo = self.client[req.client_id] _cinfo["info"].update(req.to_dict()) _cinfo["client_secret"] = client_secret _cinfo["expires"] = expires resp = RegistrationResponse(client_id=client_id, client_secret=client_secret, client_secret_expires_at=expires, **kwargs) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def check_session_endpoint(self, query): try: idtoken = self.parse_check_session_request(query=query) except Exception: raise response = Response() response.text = idtoken.to_json() response.headers = {"content-type": "application/json"} return response def refresh_session_endpoint(self, query): self.parse_refresh_session_request(query=query) resp = RegistrationResponse(client_id="anonymous", client_secret="hemligt") response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def end_session_endpoint(self, query): try: req = self.parse_end_session_request(query=query) except Exception: raise # redirect back resp = EndSessionResponse(state=req["state"]) url = resp.request(req["redirect_url"]) response = Response() response.headers = {"location": url} response.status_code = 302 # redirect response.text = "" return response @staticmethod def add_credentials(user, passwd): pass def openid_conf(self): endpoint = {} for point, path in ENDPOINT.items(): endpoint[point] = "%s%s" % (self.host, path) signing_algs = list(jws.SIGNER_ALGS.keys()) resp = ProviderConfigurationResponse( issuer=self.name, scopes_supported=["openid", "profile", "email", "address"], identifiers_supported=["public", "PPID"], flows_supported=[ "code", "token", "code token", "id_token", "code id_token", "token id_token" ], subject_types_supported=["pairwise", "public"], response_types_supported=[ "code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token" ], jwks_uri="http://example.com/oidc/jwks", id_token_signing_alg_values_supported=signing_algs, grant_types_supported=["authorization_code", "implicit"], **endpoint) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response
def test_query_rel(self): wf = WebFinger() query = wf.query("acct:[email protected]", ["http://webfinger.net/rel/profile-page", "vcard"]) assert query == "https://example.com/.well-known/webfinger?resource=acct%3Abob%40example.com&rel=http%3A%2F%2Fwebfinger.net%2Frel%2Fprofile-page&rel=vcard"
def test_query_acct(self): wf = WebFinger(OIC_ISSUER) query = wf.query("acct:[email protected]") assert query == "https://example.com/.well-known/webfinger?resource=acct%3Acarol%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer"
import sys from oic.oauth2 import PBase from oic.utils.webfinger import WebFinger, OIC_ISSUER __author__ = 'roland' wf = WebFinger(OIC_ISSUER) wf.httpd = PBase() print(wf.discovery_query(sys.argv[1]))
# # HTTP/1.1 200 OK # Access-Control-Allow-Origin * # Content-Type: application/jrd+json # # { # "subject" : "acct:[email protected]", # "links": # [{ # "rel": "http://openid.net/specs/connect/1.0/issuer", # "href": "https://openid.example.com" # }] # } userid = "*****@*****.**" wf = WebFinger() query = wf.query("acct:%s" % userid, rel=OIC_ISSUER) print query r = requests.request("GET", query, verify=False) jwt = json.loads(r.text) print json.dumps(jwt, sort_keys=True, indent=4, separators=(',', ': ')) print ######################################################### wf = WebFinger() wf.httpd = PBase(verify_ssl=False) url = wf.discovery_query("acct:%s" % userid) print "The user should be redirected here for login", url
#!/usr/bin/env python from oic.oauth2 import PBase from oic.oic import Client from oic.utils.webfinger import WebFinger __author__ = 'roland' import argparse parser = argparse.ArgumentParser() parser.add_argument('-w', dest='webfinger') parser.add_argument('-p', dest='providerinfo') cargs = parser.parse_args() issuer = "" if cargs.webfinger: _httpd = PBase(verify_ssl=False) w = WebFinger(httpd=_httpd) issuer = w.discovery_query(cargs.webfinger) print issuer if cargs.providerinfo: cli = Client(verify_ssl=False) if cargs.providerinfo != "-": issuer = cargs.providerinfo cli.provider_config(issuer) print cli.provider_info
""" Shows off how you can do OpenID Connect dynamic configuration discovery """ import json import requests from oic.oic import Client from oic.oic import OIDCONF_PATTERN from oic.utils.webfinger import WebFinger from oic.utils.webfinger import OIC_ISSUER __author__ = 'roland' # ============================================================================= wf = WebFinger() query = wf.query("acct:[email protected]:8060", rel=OIC_ISSUER) r = requests.request("GET", query, verify=False) jwt = json.loads(r.text) url = jwt["links"][0]["href"] print("Provider:", url) # Construct the URL used to get the provider configuration url = OIDCONF_PATTERN % url[:-1] print("Provider info url:", url) r = requests.request("GET", url, verify=False)
class Client(oauth2.Client): _endpoints = ENDPOINTS def __init__(self, client_id=None, ca_certs=None, grant_expire_in=600, jwt_keys=None, client_timeout=0, client_prefs=None): oauth2.Client.__init__(self, client_id, ca_certs, grant_expire_in, client_timeout=client_timeout, jwt_keys=jwt_keys) self.file_store = "./file/" self.file_uri = "http://localhost/" # OpenID connect specific endpoints for endpoint in ENDPOINTS: setattr(self, endpoint, "") self.id_token = None self.log = None self.request2endpoint = REQUEST2ENDPOINT self.response2error = RESPONSE2ERROR self.grant_class = Grant self.token_class = Token self.authn_method = AUTHN_METHOD self.provider_info = {} self.client_prefs = client_prefs or {} self.behaviour = {"require_signed_request_object": DEF_SIGN_ALG["openid_request_object"]} self.wf = WebFinger(OIC_ISSUER) self.wf.httpd = self def _get_id_token(self, **kwargs): try: return kwargs["id_token"] except KeyError: grant = self.get_grant(**kwargs) if grant: try: _scope = kwargs["scope"] except KeyError: _scope = None for token in grant.tokens: if token.scope and _scope: flag = True for item in _scope: try: assert item in token.scope except AssertionError: flag = False break if not flag: break if token.id_token: return token.id_token return None def construct_AuthorizationRequest(self, request=AuthorizationRequest, request_args=None, extra_args=None, **kwargs): if request_args is not None: for arg in ["idtoken_claims", "userinfo_claims"]: if arg in request_args: kwargs[arg] = request_args[arg] del request_args[arg] 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 "idtoken_claims" in kwargs or "userinfo_claims" in kwargs: request_param = "request" else: request_param = None 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 #noinspection PyUnusedLocal def construct_AccessTokenRequest(self, request=AccessTokenRequest, request_args=None, extra_args=None, **kwargs): return oauth2.Client.construct_AccessTokenRequest(self, request, request_args, extra_args, **kwargs) def construct_RefreshAccessTokenRequest(self, request=RefreshAccessTokenRequest, request_args=None, extra_args=None, **kwargs): return oauth2.Client.construct_RefreshAccessTokenRequest(self, request, request_args, extra_args, **kwargs) def construct_UserInfoRequest(self, request=UserInfoRequest, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} if "access_token" in request_args: pass else: if "scope" not in kwargs: kwargs["scope"] = "openid" token = self.get_token(**kwargs) if token is None: raise Exception("No valid token available") request_args["access_token"] = token.access_token return self.construct_request(request, request_args, extra_args) #noinspection PyUnusedLocal def construct_RegistrationRequest(self, request=RegistrationRequest, request_args=None, extra_args=None, **kwargs): return self.construct_request(request, request_args, extra_args) #noinspection PyUnusedLocal def construct_RefreshSessionRequest(self, request=RefreshSessionRequest, request_args=None, extra_args=None, **kwargs): return self.construct_request(request, request_args, extra_args) def _id_token_based(self, request, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} try: _prop = kwargs["prop"] except KeyError: _prop = "id_token" if _prop in request_args: pass else: id_token = self._get_id_token(**kwargs) if id_token is None: raise Exception("No valid id token available") request_args[_prop] = id_token return self.construct_request(request, request_args, extra_args) def construct_CheckSessionRequest(self, request=CheckSessionRequest, request_args=None, extra_args=None, **kwargs): return self._id_token_based(request, request_args, extra_args, **kwargs) def construct_CheckIDRequest(self, request=CheckIDRequest, request_args=None, extra_args=None, **kwargs): # access_token is where the id_token will be placed return self._id_token_based(request, request_args, extra_args, prop="access_token", **kwargs) def construct_EndSessionRequest(self, request=EndSessionRequest, request_args=None, extra_args=None, **kwargs): if request_args is None: request_args = {} if "state" in kwargs: request_args["state"] = kwargs["state"] elif "state" in request_args: kwargs["state"] = request_args["state"] # if "redirect_url" not in request_args: # request_args["redirect_url"] = self.redirect_url return self._id_token_based(request, request_args, extra_args, **kwargs) # ------------------------------------------------------------------------ def authorization_request_info(self, request_args=None, extra_args=None, **kwargs): return self.request_info(AuthorizationRequest, "GET", request_args, extra_args, **kwargs) # ------------------------------------------------------------------------ def do_authorization_request(self, request=AuthorizationRequest, state="", body_type="", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=AuthorizationResponse): return oauth2.Client.do_authorization_request(self, request, state, body_type, method, request_args, extra_args, http_args, response_cls) def do_access_token_request(self, request=AccessTokenRequest, scope="", state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=AccessTokenResponse, authn_method="", **kwargs): return oauth2.Client.do_access_token_request(self, request, scope, state, body_type, method, request_args, extra_args, http_args, response_cls, authn_method, **kwargs) def do_access_token_refresh(self, request=RefreshAccessTokenRequest, state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=AccessTokenResponse, **kwargs): return oauth2.Client.do_access_token_refresh(self, request, state, body_type, method, request_args, extra_args, http_args, response_cls, **kwargs) def do_registration_request(self, request=RegistrationRequest, scope="", state="", body_type="json", method="POST", request_args=None, extra_args=None, http_args=None, response_cls=None): url, body, ht_args, csi = self.request_info(request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state) if http_args is None: http_args = ht_args else: http_args.update(http_args) if response_cls is None: response_cls = RegistrationResponse response = self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) # if isinstance(response, Message): # if "token_endpoint_auth_type" not in response: # response["token_endpoint_auth_type"] = "client_secret_basic" return response def do_check_session_request(self, request=CheckSessionRequest, scope="", state="", body_type="json", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=IdToken): url, body, ht_args, csi = self.request_info(request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def do_check_id_request(self, request=CheckIDRequest, scope="", state="", body_type="json", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=IdToken): url, body, ht_args, csi = self.request_info(request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def do_end_session_request(self, request=EndSessionRequest, scope="", state="", body_type="", method="GET", request_args=None, extra_args=None, http_args=None, response_cls=None): url, body, ht_args, csi = self.request_info(request, method=method, request_args=request_args, extra_args=extra_args, scope=scope, state=state) if http_args is None: http_args = ht_args else: http_args.update(http_args) return self.request_and_return(url, response_cls, method, body, body_type, state=state, http_args=http_args) def user_info_request(self, method="GET", state="", scope="", **kwargs): uir = UserInfoRequest() logger.debug("[user_info_request]: kwargs:%s" % (kwargs,)) if "token" in kwargs: if kwargs["token"]: uir["access_token"] = kwargs["token"] token = Token() token.type = "Bearer" token.access_token = kwargs["token"] kwargs["behavior"] = "use_authorization_header" else: # What to do ? Need a callback token = None elif "access_token" in kwargs and kwargs["access_token"]: uir["access_token"] = kwargs["access_token"] del kwargs["access_token"] token = None else: token = self.grant[state].get_token(scope) if token.is_valid(): uir["access_token"] = token.access_token else: # raise oauth2.OldAccessToken if self.log: self.log.info("do access token refresh") try: self.do_access_token_refresh(token=token) token = self.grant[state].get_token(scope) uir["access_token"] = token.access_token except Exception: raise try: uir["schema"] = kwargs["schema"] except KeyError: pass uri = self._endpoint("userinfo_endpoint", **kwargs) # If access token is a bearer token it might be sent in the # authorization header # 3-ways of sending the access_token: # - POST with token in authorization header # - POST with token in message body # - GET with token in authorization header if "behavior" in kwargs: _behav = kwargs["behavior"] # use_authorization_header, token_in_message_body if "use_authorization_header" in _behav and token.type == "Bearer": bh = "Bearer %s" % token.access_token if "headers" in kwargs: kwargs["headers"]= {"Authorization": bh} else: kwargs["headers"] = {"Authorization": bh} if not "token_in_message_body" in _behav: # remove the token from the request del uir["access_token"] path, body, kwargs = self.get_or_post(uri, method, uir, **kwargs) h_args = dict([(k, v) for k,v in kwargs.items() if k in HTTP_ARGS]) return path, body, method, h_args 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"] format = "json" except AssertionError: assert "application/jwt" in resp.headers["content-type"] format = "jwt" elif resp.status_code == 500: raise Exception("ERROR: Something went wrong: %s" % resp.text) else: raise Exception("ERROR: Something went wrong [%s]: %s" % ( resp.status_code, resp.text)) if format == "json": return OpenIDSchema().from_json(txt=resp.text) else: algo = self.client_prefs["userinfo_signed_response_alg"] # Keys of the OP ? keys = self.keyjar.get_signing_key(alg2keytype(algo)) return OpenIDSchema().from_jwt(resp.text, keys) def get_userinfo_claims(self, access_token, endpoint, method="POST", schema_class=OpenIDSchema, **kwargs): uir = UserInfoRequest(access_token=access_token) try: uir["schema"] = kwargs["schema"] except KeyError: pass h_args = dict([(k, v) for k,v in kwargs.items() if k in HTTP_ARGS]) if "authn_method" in kwargs: http_args = self.init_authentication_method(**kwargs) else: # If nothing defined this is the default http_args = self.init_authentication_method(uir, "bearer_header", **kwargs) h_args.update(http_args) path, body, kwargs = self.get_or_post(endpoint, method, uir, **kwargs) try: resp = self.http_request(path, method, data=body, **h_args) except oauth2.MissingRequiredAttribute: raise if resp.status_code == 200: assert "application/json" in resp.headers["content-type"] elif resp.status_code == 500: raise Exception("ERROR: Something went wrong: %s" % resp.text) else: raise Exception("ERROR: Something went wrong [%s]" % resp.status_code) return schema_class().from_json(txt=resp.text) def provider_config(self, issuer, keys=True, endpoints=True): if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer url = OIDCONF_PATTERN % _issuer pcr = None r = self.http_request(url) if r.status_code == 200: pcr = ProviderConfigurationResponse().from_json(r.text) elif r.status_code == 302: while r.status_code == 302: r = self.http_request(r.headers["location"]) if r.status_code == 200: pcr = ProviderConfigurationResponse().from_json(r.text) break if pcr is None: raise Exception("Trying '%s', status %s" % (url, r.status_code)) if "issuer" in pcr: _pcr_issuer = pcr["issuer"] if pcr["issuer"].endswith("/"): if issuer.endswith("/"): _issuer = issuer else: _issuer = issuer + "/" else: if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer try: assert _issuer == _pcr_issuer except AssertionError: raise Exception("provider info issuer mismatch '%s' != '%s'" % ( _issuer, _pcr_issuer)) self.provider_info[_pcr_issuer] = pcr else: _pcr_issuer = issuer if endpoints: for key, val in pcr.items(): if key.endswith("_endpoint"): setattr(self, key, val) if keys: self.keyjar.load_keys(pcr, _pcr_issuer) return pcr def unpack_aggregated_claims(self, userinfo): if userinfo._claim_sources: for csrc, spec in userinfo._claim_sources.items(): if "JWT" in spec: if not csrc in self.keyjar: self.provider_config(csrc, endpoints=False) keycol = self.keyjar.get_verify_key(owner=csrc) for typ, keyl in self.keyjar.get_verify_key().items(): try: keycol[typ].extend(keyl) except KeyError: keycol[typ] = keyl info = json.loads(jws.verify(str(spec["JWT"]), keycol)) attr = [n for n, s in userinfo._claim_names.items() if s == csrc] assert attr == info.keys() for key, vals in info.items(): userinfo[key] = vals return userinfo def fetch_distributed_claims(self, userinfo, callback=None): for csrc, spec in userinfo._claim_sources.items(): if "endpoint" in spec: #pcr = self.provider_config(csrc, keys=False, endpoints=False) if "access_token" in spec: _uinfo = self.do_user_info_request( token=spec["access_token"], userinfo_endpoint=spec["endpoint"]) else: _uinfo = self.do_user_info_request(token=callback(csrc), userinfo_endpoint=spec["endpoint"]) attr = [n for n, s in userinfo._claim_names.items() if s == csrc] assert attr == _uinfo.keys() for key, vals in _uinfo.items(): userinfo[key] = vals return userinfo def verify_alg_support(self, alg, usage, other): """ Verifies that the algorithm to be used are supported by the other side. :param alg: The algorithm specification :param usage: In which context the 'alg' will be used. The following values are supported: - userinfo - id_token - request_object - token_endpoint_auth :param other: The identifier for the other side :return: True or False """ try: _pcr = self.provider_info[other] supported = _pcr["%s_algs_supported" % usage] except KeyError: try: supported = getattr(self, "%s_algs_supported" % usage) except AttributeError: supported = None if supported is None: return True else: if alg in supported: return True else: return False def match_preferences(self, pcr=None, issuer=None): """ Match the clients preferences against what the provider can do. :param pcr: Provider configuration response if available :param issuer: The issuer identifier """ if not pcr: pcr = self.provider_info[issuer] for _pref, _prov in PREFERENCE2PROVIDER.items(): try: vals = self.client_prefs[_pref] except KeyError: continue try: _pvals = pcr[_prov] except KeyError: try: self.behaviour[_pref] = PROVIDER_DEFAULT[_pref] except KeyError: #self.behaviour[_pref]= vals[0] self.behaviour[_pref] = None continue for val in vals: if val in _pvals: self.behaviour[_pref]= val break if _pref not in self.behaviour: raise ConfigurationError( "OP couldn't match preferences", "%s" % _pref) for key, val in self.client_prefs.items(): if key not in PREFERENCE2PROVIDER: self.behaviour[key] = val def register(self, url, operation="register", application_type="web", **kwargs): req = RegistrationRequest(operation=operation, application_type=application_type) if operation == "update": req["client_id"] = self.client_id req["client_secret"] = self.client_secret for prop in req.parameters(): if prop in ["operation", "client_id", "client_secret"]: continue try: req[prop] = kwargs[prop] except KeyError: try: req[prop] = self.behaviour[prop] except KeyError: pass if "redirect_uris" not in req: try: req["redirect_uris"] = self.redirect_uris except AttributeError: raise MissingRequiredAttribute("redirect_uris") headers = {"content-type": "application/x-www-form-urlencoded"} if operation == "client_update": headers["Authorization"] = "Bearer %s" % self.registration_access_token rsp = self.http_request(url, "POST", data=req.to_urlencoded(), headers=headers) if rsp.status_code == 200: resp = RegistrationResponse().deserialize(rsp.text, "json") self.client_secret = resp["client_secret"] self.client_id = resp["client_id"] self.registration_expires = resp["expires_at"] self.registration_access_token = resp["registration_access_token"] else: err = ErrorResponse().deserialize(rsp.text, "json") raise Exception("Registration failed: %s" % err.get_json()) return resp def normalization(self, principal, idtype="mail"): if idtype == "mail": (local, domain) = principal.split("@") subject = "acct:%s" % principal elif idtype == "url": p = urlparse.urlparse(principal) domain = p.netloc subject = principal else: domain = "" subject = principal return subject, domain def discover(self, principal): subject, host = self.normalization(principal) return self.wf.discovery_query(host, subject)
import sys from oic.oauth2 import PBase from oic.utils.webfinger import OIC_ISSUER from oic.utils.webfinger import WebFinger __author__ = 'roland' wf = WebFinger(OIC_ISSUER) wf.httpd = PBase() print (wf.discovery_query(sys.argv[1]))
""" Code to show how you can use WebFinger to aquire the issuer ID of an OpenID Connect provider using an account id """ import json from oic.oauth2 import PBase import requests from oic.utils.webfinger import WebFinger, OIC_ISSUER __author__ = 'roland' # ===================================================================== # Using only very basic functions and methods # Initiate the WebFinger class wf = WebFinger() # contruct the webfinger query URL query = wf.query("acct:[email protected]", rel=OIC_ISSUER) print(query) r = requests.request("GET", query, verify=False) # parse the JSON returned by the website and dump the content to # standard out jwt = json.loads(r.text) print(json.dumps(jwt, sort_keys=True, indent=4, separators=(',', ': '))) # ===================================================================== # A bit more high level
def test_query_device(self): wf = WebFinger() query = wf.query(resource="device:p1.example.com") assert query == 'https://p1.example.com/.well-known/webfinger?resource=device%3Ap1.example.com'
class MyFakeOICServer(Server): def __init__(self, name=""): Server.__init__(self) self.sdb = SessionDB() self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" self.webfinger = WebFinger() #noinspection PyUnusedLocal def http_request(self, path, method="GET", **kwargs): part = urlparse(path) path = part[2] query = part[4] self.host = "%s://%s" % (part.scheme, part.netloc) response = Response response.status_code = 500 response.text = "" if path == ENDPOINT["authorization_endpoint"]: assert method == "GET" response = self.authorization_endpoint(query) elif path == ENDPOINT["token_endpoint"]: assert method == "POST" response = self.token_endpoint(kwargs["data"]) elif path == ENDPOINT["user_info_endpoint"]: assert method == "POST" response = self.userinfo_endpoint(kwargs["data"]) elif path == ENDPOINT["refresh_session_endpoint"]: assert method == "GET" response = self.refresh_session_endpoint(query) elif path == ENDPOINT["check_session_endpoint"]: assert method == "GET" response = self.check_session_endpoint(query) elif path == ENDPOINT["end_session_endpoint"]: assert method == "GET" response = self.end_session_endpoint(query) elif path == ENDPOINT["registration_endpoint"]: if method == "POST": response = self.registration_endpoint(kwargs["data"]) elif path == "/.well-known/webfinger": assert method == "GET" qdict = parse_qs(query) response.status_code = 200 response.text = self.webfinger.response(qdict["resource"][0], "%s/" % self.name) elif path == "/.well-known/openid-configuration": assert method == "GET" response = self.openid_conf() return response 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 token_endpoint(self, data): if "grant_type=refresh_token" in data: req = self.parse_refresh_token_request(body=data) _info = self.sdb.refresh_token(req["refresh_token"]) elif "grant_type=authorization_code": req = self.parse_token_request(body=data) _info = self.sdb.upgrade_to_token(req["code"]) else: response = TokenErrorResponse(error="unsupported_grant_type") return response, "" resp = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) response = Response() 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() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def registration_endpoint(self, data): try: req = self.parse_registration_request(data, "json") except ValueError: req = self.parse_registration_request(data) client_secret = rndstr() expires = utc_time_sans_frac() + self.registration_expires_in kwargs = {} if "client_id" not in req: client_id = rndstr(10) registration_access_token = rndstr(20) _client_info = req.to_dict() kwargs.update(_client_info) _client_info.update({ "client_secret": client_secret, "info": req.to_dict(), "expires": expires, "registration_access_token": registration_access_token, "registration_client_uri": "register_endpoint" }) self.client[client_id] = _client_info kwargs["registration_access_token"] = registration_access_token kwargs["registration_client_uri"] = "register_endpoint" try: del kwargs["operation"] except KeyError: pass else: client_id = req.client_id _cinfo = self.client[req.client_id] _cinfo["info"].update(req.to_dict()) _cinfo["client_secret"] = client_secret _cinfo["expires"] = expires resp = RegistrationResponse(client_id=client_id, client_secret=client_secret, client_secret_expires_at=expires, **kwargs) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def check_session_endpoint(self, query): try: idtoken = self.parse_check_session_request(query=query) except Exception: raise response = Response() response.text = idtoken.to_json() response.headers = {"content-type": "application/json"} return response #noinspection PyUnusedLocal def refresh_session_endpoint(self, query): try: req = self.parse_refresh_session_request(query=query) except Exception: raise resp = RegistrationResponse(client_id="anonymous", client_secret="hemligt") response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def end_session_endpoint(self, query): try: req = self.parse_end_session_request(query=query) except Exception: raise # redirect back resp = EndSessionResponse(state=req["state"]) url = resp.request(req["redirect_url"]) response = Response() response.headers = {"location": url} response.status_code = 302 # redirect response.text = "" return response #noinspection PyUnusedLocal def add_credentials(self, user, passwd): return def openid_conf(self): endpoint = {} for point, path in ENDPOINT.items(): endpoint[point] = "%s%s" % (self.host, path) signing_algs = jws.SIGNER_ALGS.keys() resp = ProviderConfigurationResponse( issuer=self.name, scopes_supported=["openid", "profile", "email", "address"], identifiers_supported=["public", "PPID"], flows_supported=["code", "token", "code token", "id_token", "code id_token", "token id_token"], subject_types_supported=["pairwise", "public"], response_types_supported=["code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"], jwks_uri="http://example.com/oidc/jwks", id_token_signing_alg_values_supported=signing_algs, grant_types_supported=["authorization_code", "implicit"], **endpoint) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response
def application(environ, start_response): session = environ['beaker.session'] rpSession = RpSession(session) path = environ.get('PATH_INFO', '').lstrip('/') if path == "robots.txt": return static(environ, start_response, LOGGER, "static/robots.txt") if path.startswith("static/"): return static(environ, start_response, LOGGER, path) query = parse_qs(environ["QUERY_STRING"]) if path == "logout": try: logoutUrl = rpSession.getClient().endsession_endpoint logoutUrl += "?" + urllib.urlencode({"post_logout_redirect_uri": SERVER_ENV["base_url"]}) try: logoutUrl += "&" + urllib.urlencode({"id_token_hint": id_token_as_signed_jwt(rpSession.getClient(), "HS256")}) except: pass rpSession.clearSession() resp = Redirect(str(logoutUrl)) return resp(environ, start_response) except: pass if rpSession.getCallback(): for key, _dict in rp_conf.SERVICE.items(): if "opKey" in _dict and _dict["opKey"] == path: rpSession.setCallback(False) func = getattr(rp_conf.SERVICE[key]["instance"], "callback") return func(environ, SERVER_ENV, start_response, query, rpSession) if path == "rpAcr" and "key" in query and query["key"][0] in rp_conf.SERVICE: return chooseAcrValue(environ, start_response, rpSession, query["key"][0]) if path == "rpAuth": #Only called if multiple arc_values (that is authentications) exists. if "acr" in query and query["acr"][0] in rpSession.getAcrvalues() and \ "key" in query and query["key"][0] in rp_conf.SERVICE: func = getattr(rp_conf.SERVICE[query["key"][0]]["instance"], "create_authnrequest") return func(environ, SERVER_ENV, start_response, rpSession, query["acr"][0]) if rpSession.getClient() is not None: rpSession.setCallback(True) func = getattr(rp_conf.SERVICE[rpSession.getService()]["instance"], "begin") return func(environ, SERVER_ENV, start_response, rpSession) if path == "rp": if "key" in query: print "key" key = query["key"][0] if key in rp_conf.SERVICE: rpSession.setCallback(True) func = getattr(rp_conf.SERVICE[key]["instance"], "begin") return func(environ, SERVER_ENV, start_response, rpSession) if "uid" in query: print "uid" _val = URINormalizer().normalize(query["uid"][0]) wf = WebFinger(httpd=Httpd()) link = wf.discovery_query(resource=_val) #requests.get(url, verify=True) md5 = hashlib.md5() md5.update(link) opkey = base64.b16encode(md5.digest()) kwargs = {'opKey': opkey, 'description': 'OIDC server with discovery url: ' + link, 'class': pyoidcOIC, 'srv_discovery_url': link, 'scope': ["openid", "profile", "email", "address", "phone"], 'name': link} rp_conf.SERVICE[opkey] = kwargs rp_conf.SERVICE[opkey]["instance"] = pyoidcOIC(None, None, **kwargs) rpSession.setCallback(True) func = getattr(rp_conf.SERVICE[opkey]["instance"], "begin") return func(environ, SERVER_ENV, start_response, rpSession) if path == "opbyuid": return opbyuid(environ, start_response) if path == "oplist": return oplist(environ, start_response) if path == "about": return about(environ, start_response) return start(environ, start_response)
#!/usr/bin/env python import json import requests from oic.oauth2 import PBase from oic.oic import OIDCONF_PATTERN from oic.utils.webfinger import WebFinger # This is a complete discovery example with OpenID Connect. After having retrieved the provide URL, its information # is retrieved and printed. The standard URL format for obtaining OP information is: # https://<op.servername>/.well-known/openid-configuration userid = "[email protected]:8060" wf = WebFinger() wf.httpd = PBase(verify_ssl=False) url = wf.discovery_query("acct:%s" % userid) print "Provider:", url if url[-1] == '/': url = url[:-1] url = OIDCONF_PATTERN % url print "Provider info url:", url r = requests.request("GET", url, verify=False) jwt = json.loads(r.text) print "---- provider configuration info ----" print json.dumps(jwt, sort_keys=True, indent=4, separators=(',', ': '))