def backchannel_logout(client, request='', request_args=None): """ :param request: URL encoded logout request :return: """ if request: req = BackChannelLogoutRequest().from_urlencoded(as_unicode(request)) elif request_args: req = BackChannelLogoutRequest(**request_args) else: raise MissingRequiredAttribute('logout_token') _context = client.client_get("service_context") kwargs = { 'aud': client.get_client_id(), 'iss': _context.get('issuer'), 'keyjar': _context.keyjar, 'allowed_sign_alg': _context.get('registration_response').get( "id_token_signed_response_alg", "RS256") } logger.debug(f"(backchannel_logout) Verifying request using: {kwargs}") try: req.verify(**kwargs) except (MessageException, ValueError, NotForMe) as err: raise MessageException('Bogus logout request: {}'.format(err)) else: logger.debug("Request verified OK") # Find the subject through 'sid' or 'sub' sub = req[verified_claim_name('logout_token')].get('sub') sid = None if not sub: sid = req[verified_claim_name('logout_token')].get('sid') if not sub and not sid: raise MessageException('Neither "sid" nor "sub"') elif sub: _state = _context.state.get_state_by_sub(sub) elif sid: _state = _context.state.get_state_by_sid(sid) return _state
def clear_verified_claims(msg): for claim in CLAIMS_WITH_VERIFIED: _vc_name = verified_claim_name(claim) try: del msg[_vc_name] except KeyError: pass return msg
def verify_id_token(msg, check_hash=False, claim='id_token', **kwargs): # Try to decode the JWT, checks the signature args = {} for arg in ID_TOKEN_VERIFY_ARGS: try: args[arg] = kwargs[arg] except KeyError: pass _jws = jws_factory(msg[claim]) if not _jws: raise ValueError('{} not a signed JWT'.format(claim)) if _jws.jwt.headers['alg'] == 'none': try: _allow_none = kwargs['allow_sign_alg_none'] except KeyError: logger.info('Signing algorithm None not allowed') return False else: if not _allow_none: logger.info('Signing algorithm None not allowed') return False _body = _jws.jwt.payload() if 'keyjar' in kwargs: try: if _body['iss'] not in kwargs['keyjar']: raise ValueError('Unknown issuer') except KeyError: raise MissingRequiredAttribute('iss') idt = IdToken().from_jwt(str(msg[claim]), **args) if not idt.verify(**kwargs): return False if check_hash: _alg = idt.jws_header["alg"] hfunc = "HS" + _alg[-3:] if "access_token" in msg: if "at_hash" not in idt: raise MissingRequiredAttribute("Missing at_hash property", idt) if idt["at_hash"] != left_hash(msg["access_token"], hfunc): raise AtHashError("Failed to verify access_token hash", idt) if "code" in msg: if "c_hash" not in idt: raise MissingRequiredAttribute("Missing c_hash property", idt) if idt["c_hash"] != left_hash(msg["code"], hfunc): raise CHashError("Failed to verify code hash", idt) msg[verified_claim_name(claim)] = idt logger.info('Verified {}: {}'.format(claim, idt.to_dict())) return True
def verify(self, **kwargs): if "request" in self: _vc_name = verified_claim_name("request") if _vc_name in self: del self[_vc_name] args = {} for arg in ["keyjar", "opponent_id", "sender", "alg", "encalg", "encenc"]: try: args[arg] = kwargs[arg] except KeyError: pass _req = AuthorizationRequest().from_jwt(str(self["request"]), **args) self.merge(_req, "lax") self[_vc_name] = _req return True
def verify(self, **kwargs): if "request" in self: _vc_name = verified_claim_name("request") if _vc_name in self: del self[_vc_name] args = {} for arg in ["keyjar", "opponent_id", "sender", "alg", "encalg", "encenc"]: try: args[arg] = kwargs[arg] except KeyError: pass _req = AuthorizationRequest().from_jwt(str(self["request"]), **args) self.merge(_req, "strict") self[_vc_name] = _req elif "request_uri" not in self: raise MissingAttribute("One of request or request_uri must be present") return True
def verify(self, **kwargs): """Authorization Request parameters that are OPTIONAL in the OAuth 2.0 specification MAY be included in the OpenID Request Object without also passing them as OAuth 2.0 Authorization Request parameters, with one exception: The scope parameter MUST always be present in OAuth 2.0 Authorization Request parameters. All parameter values that are present both in the OAuth 2.0 Authorization Request and in the OpenID Request Object MUST exactly match.""" super(AuthorizationRequest, self).verify(**kwargs) clear_verified_claims(self) args = {} for arg in ["keyjar", "opponent_id", "sender", "alg", "encalg", "encenc"]: try: args[arg] = kwargs[arg] except KeyError: pass if "opponent_id" not in kwargs: args["opponent_id"] = self["client_id"] if "request" in self: if isinstance(self["request"], str): # Try to decode the JWT, checks the signature oidr = OpenIDRequest().from_jwt(str(self["request"]), **args) # check if something is change in the original message for key, val in oidr.items(): if key in self: if self[key] != val: # log but otherwise ignore logger.warning("{} != {}".format(self[key], val)) # remove all claims _keys = list(self.keys()) for key in _keys: if key not in oidr: del self[key] self.update(oidr) # replace the JWT with the parsed and verified instance self[verified_claim_name("request")] = oidr if "id_token_hint" in self: if isinstance(self["id_token_hint"], str): idt = IdToken().from_jwt(str(self["id_token_hint"]), **args) self["verified_id_token_hint"] = idt if "response_type" not in self: raise MissingRequiredAttribute("response_type missing", self) _rt = self["response_type"] if "id_token" in _rt: if "nonce" not in self: raise MissingRequiredAttribute("Nonce missing", self) else: try: if self["nonce"] != kwargs["nonce"]: raise ValueError("Nonce in id_token not matching nonce in authz " "request") except KeyError: pass if "openid" not in self.get("scope", []): raise MissingRequiredValue("openid not in scope", self) if "offline_access" in self.get("scope", []): if "prompt" not in self or "consent" not in self["prompt"]: raise MissingRequiredValue("consent in prompt", self) if "prompt" in self: if "none" in self["prompt"] and len(self["prompt"]) > 1: raise InvalidRequest("prompt none combined with other value", self) return True
def verify_id_token(msg, check_hash=False, claim="id_token", **kwargs): # Try to decode the JWT, checks the signature args = {} for arg in ID_TOKEN_VERIFY_ARGS: try: args[arg] = kwargs[arg] except KeyError: pass _jws = jws_factory(msg[claim]) if not _jws: raise ValueError("{} not a signed JWT".format(claim)) if _jws.jwt.headers["alg"] == "none": _signed = False _sign_alg = kwargs.get("sigalg") if _sign_alg == "none": _allowed = True else: # There might or might not be a specified signing alg if kwargs.get("allow_sign_alg_none", False) is False: logger.info("Signing algorithm None not allowed") raise UnsupportedAlgorithm("Signing algorithm None not allowed") else: _signed = True if "allowed_sign_alg" in kwargs: if _jws.jwt.headers["alg"] != kwargs["allowed_sign_alg"]: _msg = "Wrong token signing algorithm, {} != {}".format( _jws.jwt.headers["alg"], kwargs["allowed_sign_alg"] ) logger.error(_msg) raise UnsupportedAlgorithm(_msg) _body = _jws.jwt.payload() if _signed and "keyjar" in kwargs: try: if _body["iss"] not in kwargs["keyjar"]: logger.info("KeyJar issuers: {}".format(kwargs["keyjar"])) raise ValueError('Unknown issuer: "{}"'.format(_body["iss"])) except KeyError: raise MissingRequiredAttribute("iss") idt = IdToken().from_jwt(str(msg[claim]), **args) if not idt.verify(**kwargs): return False if _signed and check_hash: _alg = idt.jws_header["alg"] hfunc = "HS" + _alg[-3:] if "access_token" in msg: if "at_hash" not in idt: raise MissingRequiredAttribute("Missing at_hash property", idt) if idt["at_hash"] != left_hash(msg["access_token"], hfunc): raise AtHashError("Failed to verify access_token hash", idt) if "code" in msg: if "c_hash" not in idt: raise MissingRequiredAttribute("Missing c_hash property", idt) if idt["c_hash"] != left_hash(msg["code"], hfunc): raise CHashError("Failed to verify code hash", idt) msg[verified_claim_name(claim)] = idt logger.info("Verified {}: {}".format(claim, idt.to_dict())) return True