def begin(self, environ, start_response): """ Begin the OAuth2 flow :param environ: The WSGI environment :param start_response: The function to start the response process :return: A URL to which the user should be redirected """ LOG_DEBUG("- begin -") # Store the request and the redirect uri used _path = http_util.geturl(environ, False, False) self.redirect_uris = ["%s%s" % (_path, self.authz_page)] self._request = http_util.geturl(environ) # Put myself in the dictionary of sessions, keyed on session-id if not self.seed: self.seed = rndstr() sid = stateID(_path, self.seed) self.state = sid self.grant[sid] = Grant(seed=self.seed) self._backup(sid) self.sdb["seed:%s" % self.seed] = sid location = self.request_info(AuthorizationRequest, method="GET", scope=self.scope, request_args={"state": sid})[0] LOG_DEBUG("Redirecting to: %s" % (location,)) return location
def test_geturl(): environ = { "wsgi.url_scheme": "http", "SERVER_NAME": "example.com", "SERVER_PORT": "80", "SCRIPT_NAME": "/foo", "PATH_INFO": "/bar", "QUERY_STRING": "baz=xyz" } assert geturl(environ) == "http://example.com/foo/bar?baz=xyz"
def verify_client(self, environ, areq): try: _token = self._bearer_auth(environ) if _token in self.cdb: return True except AuthnFailure: pass if areq["client_id"] not in self.cdb: return False if "client_secret" in areq: # client_secret_post identity = areq["client_id"] if self.cdb[identity]["client_secret"] == areq["client_secret"]: return True elif "client_assertion" in areq: # client_secret_jwt or public_key_jwt if areq["client_assertion_type"] != JWT_BEARER: return False key_col = {areq["client_id"]: self.keystore.get_verify_key(owner=areq["client_id"])} key_col.update({".": self.keystore.get_verify_key()}) # On a busy system this is lots of data # if log_info: # log_info("key_col: %s" % (key_col,)) bjwt = AuthnToken().from_jwt(areq["client_assertion"], key_col) try: assert bjwt["iss"] == areq["client_id"] # Issuer = the client # Is this true bjwt.iss == areq.client_id assert str(bjwt["iss"]) in self.cdb # It's a client I know assert str(bjwt["aud"]) == geturl(environ, query=False) return True except AssertionError: pass return False
def handle_authorization_response(self, environ, start_response): """ This is where we get redirect back to after authorization at the authorization server has happened. :param environ: The WSGI environment :param start_response: The function to start the response process :return: A AccessTokenResponse instance """ LOG_DEBUG("- authorization - %s flow -" % self.flow_type) _query = environ.get("QUERY_STRING") LOG_DEBUG("QUERY: %s" % _query) _path = http_util.geturl(environ, False, False) if "code" in self.response_type: # Might be an error response try: aresp = self.parse_response(AuthorizationResponse, info=_query, sformat="urlencoded") except Exception, err: logger.error("%s" % err) raise if isinstance(aresp, Message): if aresp.type().endswith("ErrorResponse"): raise AuthzError(aresp["error"]) try: self.update(aresp["state"]) except KeyError: raise UnknownState(aresp["state"]) self._backup(aresp["state"]) return aresp
def parse_authz(self, environ, start_response): """ This is where we get redirect back to after authorization at the authorization server has happened. :param environ: The WSGI environment :param start_response: The function to start the response process :return: A AccessTokenResponse instance """ _log_info = logger.info if self.debug: _log_info("- authorization -") _log_info("environ: %s" % environ) if environ.get("REQUEST_METHOD") == "GET": _query = environ.get("QUERY_STRING") # elif environ.get("REQUEST_METHOD") == "POST": # _query = http_util.get_post(environ) else: resp = http_util.BadRequest("Unsupported method") return resp(environ, start_response) _log_info("response: %s" % _query) _path = http_util.geturl(environ, False, False) vkeys = self.keystore.get_verify_key(owner=None) if "code" in self.config["response_type"]: # Might be an error response _log_info("Expect Authorization Response") aresp = self.parse_response(AuthorizationResponse, info=_query, format="urlencoded", key=vkeys) if aresp.type() == "ErrorResponse": _log_info("ErrorResponse: %s" % aresp) raise AuthzError(aresp.error) _log_info("Aresp: %s" % aresp) _state = aresp["state"] try: self.update(_state) except KeyError: raise UnknownState(_state) self.redirect_uris = [self.sdb[_state]["redirect_uris"]] # May have token and id_token information too if "access_token" in aresp: atr = clean_response(aresp) self.access_token = atr # update the grant object self.get_grant(state=_state).add_token(atr) else: atr = None self._backup(_state) try: idt = aresp["id_token"] except KeyError: idt = None return aresp, atr, idt else: # implicit flow _log_info("Expect Access Token Response") atr = self.parse_response(AccessTokenResponse, info=_query, format="urlencoded", key=vkeys) if atr.type() == "ErrorResponse": raise TokenError(atr["error"]) idt = None return None, atr, idt
def begin(self, environ, start_response, scope="", response_type="", use_nonce=False): """ Begin the OAuth2 flow :param environ: The WSGI environment :param start_response: The function to start the response process :param scope: Defines which user info claims is wanted :param response_type: Controls the parameters returned in the response from the Authorization Endpoint :param use_nonce: If not implicit flow nonce is optional. This defines if it should be used anyway. :return: A URL to which the user should be redirected """ _log_info = logger.info if self.debug: _log_info("- begin -") _path = http_util.geturl(environ, False, False) _page = self.config["authz_page"] if not _path.endswith("/"): if _page.startswith("/"): self.redirect_uris = [_path + _page] else: self.redirect_uris = ["%s/%s" % (_path, _page)] else: if _page.startswith("/"): self.redirect_uris = [_path + _page[1:]] else: self.redirect_uris = ["%s/%s" % (_path, _page)] # Put myself in the dictionary of sessions, keyed on session-id if not self.seed: self.seed = rndstr() if not scope: scope = self.config["scope"] if not response_type: response_type = self.config["response_type"] sid = stateID(_path, self.seed) self.state = sid self.grant[sid] = Grant(seed=self.seed) self._backup(sid) self.sdb["seed:%s" % self.seed] = sid # Store the request and the redirect uri used self._request = http_util.geturl(environ) args = { "client_id": self.client_id, "state":sid, "response_type":response_type, "scope": scope, } # nonce is REQUIRED in implicit flow, # OPTIONAL on code flow. if "token" in response_type or use_nonce: self.nonce = rndstr(12) args["nonce"] = self.nonce if "max_age" in self.config: args["idtoken_claims"] = {"max_age": self.config["max_age"]} if "user_info" in self.config: args["userinfo_claims"] = self.config["user_info"] if "request_method" in self.config: areq = self.construct_OpenIDRequest(request_args=args, extra_args=None) if self.config["request_method"] == "file": id_request = areq["request"] del areq["request"] _filedir = self.config["temp_dir"] _webpath = self.config["temp_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(id_request) fid.close() _webname = "%s%s%s" % (_path,_webpath,_name) areq["request_uri"] = _webname self.request_uri = _webname self._backup(sid) else: if "userinfo_claims" in args: # can only be carried in an IDRequest raise Exception("Need a request method") areq = self.construct_AuthorizationRequest(AuthorizationRequest, request_args=args) location = areq.request(self.authorization_endpoint) if self.debug: _log_info("Redirecting to: %s" % location) return location