def parse_response(self, response: Type[Message], info: str = "", sformat: ENCODINGS = "json", state: str = "", **kwargs) -> Message: """ Parse a response. :param response: Response type :param info: The response, can be either in a JSON or an urlencoded format :param sformat: Which serialization that was used :param state: The state :param kwargs: Extra key word arguments :return: The parsed and to some extend verified response """ _r2e = self.response2error if sformat == "urlencoded": info = self.get_urlinfo(info) resp = response().deserialize(info, sformat, **kwargs) msg = 'Initial response parsing => "{}"' logger.debug(msg.format(sanitize(resp.to_dict()))) if self.events: self.events.store("Response", resp.to_dict()) if "error" in resp and not isinstance(resp, ErrorResponse): resp = None errmsgs = [] # type: List[Any] try: errmsgs = _r2e[response.__name__] except KeyError: errmsgs = [ErrorResponse] try: for errmsg in errmsgs: try: resp = errmsg().deserialize(info, sformat) resp.verify() break except Exception: resp = None except KeyError: pass elif resp.only_extras(): resp = None else: kwargs["client_id"] = self.client_id try: kwargs["iss"] = self.provider_info["issuer"] except (KeyError, AttributeError): if self.issuer: kwargs["iss"] = self.issuer if "key" not in kwargs and "keyjar" not in kwargs: kwargs["keyjar"] = self.keyjar logger.debug("Verify response with {}".format(sanitize(kwargs))) verf = resp.verify(**kwargs) if not verf: logger.error("Verification of the response failed") raise PyoidcError("Verification of the response failed") if resp.type() == "AuthorizationResponse" and "scope" not in resp: try: resp["scope"] = kwargs["scope"] except KeyError: pass if not resp: logger.error("Missing or faulty response") raise ResponseError("Missing or faulty response") self.store_response(resp, info) if isinstance(resp, (AuthorizationResponse, AccessTokenResponse)): try: _state = resp["state"] except (AttributeError, KeyError): _state = "" if not _state: _state = state try: self.grant[_state].update(resp) except KeyError: self.grant[_state] = self.grant_class(resp=resp) if "id_token" in resp and self.sso_db is not None: session_update(self.sso_db, _state, "sub", resp["id_token"]["sub"]) session_update(self.sso_db, _state, "issuer", resp["id_token"]["iss"]) if "sid" in resp["id_token"]: session_update(self.sso_db, _state, "smid", resp["id_token"]["sid"]) return resp
def parse_authz( self, query="", **kwargs ) -> Union[http_util.BadRequest, Tuple[Optional[AuthorizationResponse], Optional[AccessTokenResponse], Optional[IdToken], ], ]: """ Parse authorization response from server. Couple of cases ["code"] ["code", "token"] ["code", "id_token", "token"] ["id_token"] ["id_token", "token"] ["token"] """ _log_info = logger.info logger.debug("- authorization -") # FIXME: This shouldn't be here... We should rather raise a sepcific Client error # That would simplify the return value of this function # and drop bunch of assertions from tests added in this commit. if not query: return http_util.BadRequest("Missing query") _log_info("response: %s" % sanitize(query)) if "algs" not in kwargs: kwargs["algs"] = self.sign_enc_algs("id_token") if "code" in self.consumer_config["response_type"]: aresp, _state = self._parse_authz(query, **kwargs) # 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 else: try: session_update(self.sdb, idt["sid"], "smid", _state) except KeyError: pass elif "token" in self.consumer_config["response_type"]: # implicit flow _log_info("Expect Access Token Response") aresp = None _state = None atr = self.parse_response(AccessTokenResponse, info=query, sformat="urlencoded", keyjar=self.keyjar, **kwargs) if isinstance(atr, ErrorResponse): raise TokenError(atr.get("error"), atr) idt = atr.get("id_token") else: # only id_token aresp, _state = self._parse_authz(query, **kwargs) try: idt = aresp["id_token"] except KeyError: idt = None else: try: session_update(self.sso_db, _state, "smid", idt["sid"]) except KeyError: pass # Null the aresp as only id_token should be returned aresp = atr = None # Verify the IdToken if it was present if idt is not None: self.verify_id_token(idt, self.authz_req.get(_state or atr["state"])) return aresp, atr, idt
def parse_authz(self, query="", **kwargs): """ Parse authorization response from server. Couple of cases ["code"] ["code", "token"] ["code", "id_token", "token"] ["id_token"] ["id_token", "token"] ["token"] :return: A AccessTokenResponse instance """ _log_info = logger.info logger.debug("- authorization -") if not query: return http_util.BadRequest("Missing query") _log_info("response: %s" % sanitize(query)) if "code" in self.consumer_config["response_type"]: aresp, _state = self._parse_authz(query, **kwargs) # 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 else: try: session_update(self.sdb, idt["sid"], "smid", _state) except KeyError: pass return aresp, atr, idt elif "token" in self.consumer_config["response_type"]: # implicit flow _log_info("Expect Access Token Response") atr = self.parse_response( AccessTokenResponse, info=query, sformat="urlencoded", keyjar=self.keyjar, **kwargs, ) if isinstance(atr, ErrorResponse): raise TokenError(atr.get("error"), atr) idt = None return None, atr, idt else: # only id_token aresp, _state = self._parse_authz(query, **kwargs) try: idt = aresp["id_token"] except KeyError: idt = None else: try: session_update(self.sso_db, _state, "smid", idt["sid"]) except KeyError: pass return None, None, idt
def test_session_update(): with pytest.raises(KeyError): session_update({}, "session_id", "attr", "val")