def verify(self, request, **kwargs): _jwt = JWT(self.endpoint_context.keyjar) try: ca_jwt = _jwt.unpack(request["client_assertion"]) except (Invalid, MissingKey, BadSignature) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") try: logger.debug("authntoken: %s" % sanitize(ca_jwt.to_dict())) except AttributeError: logger.debug("authntoken: %s" % sanitize(ca_jwt)) request[verified_claim_name("client_assertion")] = ca_jwt try: client_id = kwargs["client_id"] except KeyError: client_id = ca_jwt["iss"] # I should be among the audience # could be either my issuer id or the token endpoint if self.endpoint_context.issuer in ca_jwt["aud"]: pass elif self.endpoint_context.endpoint["token"].full_path in ca_jwt[ "aud"]: pass else: raise NotForMe("Not for me!") return {"client_id": client_id, "jwt": ca_jwt}
def verify(self, request, **kwargs): """ Verifies that the given username and password was correct :param request: Either the query part of a URL a urlencoded body of a HTTP message or a parse such. :param kwargs: Catch whatever else is sent. :return: redirect back to where ever the base applications wants the user after authentication. """ logger.debug("verify(%s)" % sanitize(request)) _dict = request logger.debug("dict: %s" % sanitize(_dict)) # verify username and password try: self._verify(_dict["password"], _dict["login"]) # dict origin except TypeError: try: self._verify(_dict["password"][0], _dict["login"][0]) except (AssertionError, KeyError) as err: logger.debug("Password verification failed: {}".format(err)) return {'response', b"Unknown user or wrong password"} else: try: _qp = _dict["query"] except KeyError: _qp = self.get_multi_auth_cookie(kwargs['cookie']) except (AssertionError, KeyError) as err: logger.debug("Password verification failed: {}".format(err)) return {'response': b"Unknown user or wrong password"} else: try: _qp = _dict["query"] except KeyError: _qp = self.get_multi_auth_cookie(kwargs['cookie']) logger.debug("Password verification succeeded.") # if "cookie" not in kwargs or self.endpoint_context.cookie_name not in # kwargs["cookie"]: headers = [self.cookie_dealer.create_cookie(_dict["login"], "upm")] try: return_to = self.generate_return_url(kwargs["return_to"], _qp) except KeyError: try: return_to = self.generate_return_url(self.return_to, _qp, kwargs["path"]) except KeyError: return_to = self.generate_return_url(self.return_to, _qp) return {'redirect': return_to, 'headers': headers}
def permissions(self, cookie=None, **kwargs): if cookie is None: return None else: logger.debug("kwargs: %s" % sanitize(kwargs)) val = self.cookie_dealer.get_cookie_value(cookie) if val is None: return None else: uid, _ts, typ = val if typ == "uam": # shortlived _now = int(time.time()) if _now > (int(_ts) + int(self.cookie_dealer.cookie_ttl * 60)): logger.debug("Authentication timed out") raise ToOld("%d > (%d + %d)" % (_now, int(_ts), int(self.cookie_dealer.cookie_ttl * 60))) else: if "max_age" in kwargs and kwargs["max_age"]: _now = int(time.time()) if _now > (int(_ts) + int(kwargs["max_age"])): logger.debug("Authentication too old") raise ToOld("%d > (%d + %d)" % (_now, int(_ts), int(kwargs["max_age"]))) return self.permdb[uid]
def _post_parse_request(self, request, client_id="", **kwargs): """ This is where clients come to refresh their access tokens :param request: The request :param authn: Authentication info, comes from HTTP header :returns: """ request = RefreshAccessTokenRequest(**request.to_dict()) try: keyjar = self.endpoint_context.keyjar except AttributeError: keyjar = "" request.verify(keyjar=keyjar, opponent_id=client_id) if "client_id" not in request: # Optional for refresh access token request request["client_id"] = client_id logger.debug("%s: %s" % (request.__class__.__name__, sanitize(request))) return request
def _verify_sector_identifier(self, request): """ Verify `sector_identifier_uri` is reachable and that it contains `redirect_uri`s. :param request: Provider registration request :return: si_redirects, sector_id :raises: InvalidSectorIdentifier """ si_url = request["sector_identifier_uri"] try: res = self.endpoint_context.httpc.get( si_url, **self.endpoint_context.httpc_params) logger.debug("sector_identifier_uri => %s", sanitize(res.text)) except Exception as err: logger.error(err) # res = None raise InvalidSectorIdentifier( "Couldn't read from sector_identifier_uri") try: si_redirects = json.loads(res.text) except ValueError: raise InvalidSectorIdentifier( "Error deserializing sector_identifier_uri content") if "redirect_uris" in request: logger.debug("redirect_uris: %s", request["redirect_uris"]) for uri in request["redirect_uris"]: if uri not in si_redirects: raise InvalidSectorIdentifier( "redirect_uri missing from sector_identifiers") return si_redirects, si_url
def authenticated_as(self, cookie=None, **kwargs): if cookie is None: return None, 0 else: logger.debug("kwargs: %s" % sanitize(kwargs)) try: val = self.cookie_dealer.get_cookie_value(cookie) except (InvalidCookieSign, AssertionError) as err: logger.warning(err) val = None if val is None: return None, 0 else: uid, _ts, typ = val if typ == "uam": # short lived _ttl = self.cookie_dealer.ttl _now = int(time.time()) if _now > (int(_ts) + int(_ttl * 60)): logger.debug("Authentication timed out") raise ToOld("%d > (%d + %d)" % (_now, int(_ts), int(_ttl * 60))) else: if "max_age" in kwargs and kwargs["max_age"]: _now = int(time.time()) if _now > (int(_ts) + int(kwargs["max_age"])): logger.debug("Authentication too old") raise ToOld("%d > (%d + %d)" % ( _now, int(_ts), int(kwargs["max_age"]))) return {"uid": uid}, _ts
def _access_token_post_parse_request(self, request, client_id="", **kwargs): """ This is where clients come to get their access tokens :param request: The request :param authn: Authentication info, comes from HTTP header :returns: """ request = AccessTokenRequest(**request.to_dict()) if "state" in request: try: sinfo = self.endpoint_context.sdb[request["code"]] except KeyError: logger.error("Code not present in SessionDB") return self.error_cls(error="unauthorized_client") else: state = sinfo["authn_req"]["state"] if state != request["state"]: logger.error("State value mismatch") return self.error_cls(error="unauthorized_client") if "client_id" not in request: # Optional for access token request request["client_id"] = client_id logger.debug("%s: %s" % (request.__class__.__name__, sanitize(request))) return request
def __call__(self, cookie=None, end_point_index=0, **kwargs): """ Put up the login form """ argv = self.templ_arg_func(end_point_index, **kwargs) logger.info("do_authentication argv: %s" % sanitize(argv)) mte = self.template_lookup.get_template(self.mako_template) return {'response', mte.render(**argv).decode("utf-8")}
def create_authn_response(endpoint, request, sid): """ :param endpoint: :param request: :param sid: :return: """ # create the response aresp = AuthorizationResponse() if request.get("state"): aresp["state"] = request["state"] if "response_type" in request and request["response_type"] == ["none"]: fragment_enc = False else: _context = endpoint.endpoint_context _sinfo = _context.sdb[sid] if request.get("scope"): aresp["scope"] = request["scope"] rtype = set(request["response_type"][:]) handled_response_type = [] fragment_enc = True if len(rtype) == 1 and "code" in rtype: fragment_enc = False if "code" in request["response_type"]: _code = aresp["code"] = _context.sdb[sid]["code"] handled_response_type.append("code") else: _context.sdb.update(sid, code=None) _code = None if "token" in rtype: _dic = _context.sdb.upgrade_to_token(issue_refresh=False, key=sid) logger.debug("_dic: %s" % sanitize(_dic)) for key, val in _dic.items(): if key in aresp.parameters() and val is not None: aresp[key] = val handled_response_type.append("token") _access_token = aresp.get("access_token", None) not_handled = rtype.difference(handled_response_type) if not_handled: resp = AuthorizationErrorResponse( error="invalid_request", error_description="unsupported_response_type") return {"response_args": resp, "fragment_enc": fragment_enc} return {"response_args": aresp, "fragment_enc": fragment_enc}
def verify(self, request, **kwargs): _jwt = JWT(self.endpoint_context.keyjar) try: ca_jwt = _jwt.unpack(request["client_assertion"]) except (Invalid, MissingKey, BadSignature) as err: logger.info("%s" % sanitize(err)) raise AuthnFailure("Could not verify client_assertion.") authtoken = sanitize(ca_jwt) if hasattr(ca_jwt, "to_dict") and callable(ca_jwt, "to_dict"): authtoken = sanitize(ca_jwt.to_dict()) logger.debug("authntoken: {}".format(authtoken)) _endpoint = kwargs.get("endpoint") if _endpoint is None or not _endpoint: if self.endpoint_context.issuer in ca_jwt["aud"]: pass else: raise NotForMe("Not for me!") else: if set(ca_jwt["aud"]).intersection( self.endpoint_context.endpoint[_endpoint].allowed_target_uris()): pass else: raise NotForMe("Not for me!") # If there is a jti use it to make sure one-time usage is true _jti = ca_jwt.get('jti') if _jti: _key = "{}:{}".format(ca_jwt['iss'], _jti) if _key in self.endpoint_context.jti_db: raise MultipleUsage("Have seen this token once before") else: self.endpoint_context.jti_db.set(_key, utc_time_sans_frac()) request[verified_claim_name("client_assertion")] = ca_jwt client_id = kwargs.get("client_id") or ca_jwt["iss"] return {"client_id": client_id, "jwt": ca_jwt}
def permissions(self, cookie=None, **kwargs): if cookie is None: return None else: logger.debug("kwargs: %s" % sanitize(kwargs)) val = self.cookie_dealer.get_cookie_value(cookie) if val is None: return None else: b64, _ts, typ = val info = cookie_value(b64) return self.get(info["sub"], info["client_id"])
def create_return_url(base, query, **kwargs): """ Add a query string plus extra parameters to a base URL which may contain a query part already. :param base: redirect_uri may contain a query part, no fragment allowed. :param query: Old query part as a string :param kwargs: extra query parameters :return: Constructed URL """ part = urlsplit(base) if part.fragment: raise ValueError("Base URL contained parts it shouldn't") for key, values in parse_qs(query).items(): if key in kwargs: if isinstance(kwargs[key], str): kwargs[key] = [kwargs[key]] kwargs[key].extend(values) else: kwargs[key] = values if part.query: for key, values in parse_qs(part.query).items(): if key in kwargs: if isinstance(kwargs[key], str): kwargs[key] = [kwargs[key]] kwargs[key].extend(values) else: kwargs[key] = values _url_base = base.split("?")[0] else: _url_base = base logger.debug("kwargs: %s" % sanitize(kwargs)) if kwargs: return "%s?%s" % (_url_base, url_encode_params(kwargs)) else: return _url_base
def _post_parse_request(self, request, client_id='', **kwargs): """ This is where clients come to get their access tokens :param request: The request :param authn: Authentication info, comes from HTTP header :returns: """ if 'state' in request: try: sinfo = self.endpoint_context.sdb[request['code']] except KeyError: logger.error('Code not present in SessionDB') return self.error_cls(error="unauthorized_client") else: state = sinfo['authn_req']['state'] if state != request['state']: logger.error('State value mismatch') return self.error_cls(error="unauthorized_client") if "refresh_token" in request: request = RefreshAccessTokenRequest(**request.to_dict()) try: keyjar = self.endpoint_context.keyjar except AttributeError: keyjar = "" request.verify(keyjar=keyjar, opponent_id=client_id) if "client_id" not in request: # Optional for access token request request["client_id"] = client_id logger.debug("%s: %s" % (request.__class__.__name__, sanitize(request))) return request
def _refresh_token_post_parse_request(self, request, client_id="", **kwargs): """ This is where clients come to refresh their access tokens :param request: The request :param authn: Authentication info, comes from HTTP header :returns: """ request = RefreshAccessTokenRequest(**request.to_dict()) # verify that the request message is correct try: request.verify(keyjar=self.endpoint_context.keyjar) except (MissingRequiredAttribute, ValueError, MissingRequiredValue) as err: return self.error_cls(error="invalid_request", error_description="%s" % err) try: keyjar = self.endpoint_context.keyjar except AttributeError: keyjar = "" request.verify(keyjar=keyjar, opponent_id=client_id) if "client_id" not in request: # Optional for refresh access token request request["client_id"] = client_id logger.debug("%s: %s" % (request.__class__.__name__, sanitize(request))) return request
def client_registration_setup(self, request, new_id=True, set_secret=True): try: request.verify() except (MessageException, ValueError) as err: return ResponseMessage(error="invalid_configuration_request", error_description="%s" % err) request.rm_blanks() try: self.match_client_request(request) except CapabilitiesMisMatch as err: return ResponseMessage( error="invalid_request", error_description="Don't support proposed %s" % err, ) _context = self.endpoint_context if new_id: # create new id och secret client_id = rndstr(12) # cdb client_id MUST be unique! while client_id in _context.cdb: client_id = rndstr(12) if "client_id" in request: del request["client_id"] else: client_id = request.get("client_id") if not client_id: raise ValueError("Missing client_id") _cinfo = {"client_id": client_id, "client_salt": rndstr(8)} if "registration_read" in self.endpoint_context.endpoint: self.add_registration_api(_cinfo, client_id, _context) if new_id: _cinfo["client_id_issued_at"] = utc_time_sans_frac() client_secret = "" if set_secret: client_secret = self.add_client_secret(_cinfo, client_id, _context) _context.cdb[client_id] = _cinfo _cinfo = self.do_client_registration( request, client_id, ignore=["redirect_uris", "policy_uri", "logo_uri", "tos_uri"], ) if isinstance(_cinfo, ResponseMessage): return _cinfo args = dict([(k, v) for k, v in _cinfo.items() if k in RegistrationResponse.c_param]) comb_uri(args) response = RegistrationResponse(**args) # Add the client_secret as a symmetric key to the key jar if client_secret: _context.keyjar.add_symmetric(client_id, str(client_secret)) _context.cdb[client_id] = _cinfo # Not all databases can be sync'ed if hasattr(_context.cdb, "sync") and callable(_context.cdb.sync): _context.cdb.sync() msg = "registration_response: {}" logger.info(msg.format(sanitize(response.to_dict()))) return response
def create_authn_response(endpoint_context, request, sid): # create the response aresp = AuthorizationResponse() try: aresp["state"] = request["state"] except KeyError: pass if "response_type" in request and request["response_type"] == ["none"]: fragment_enc = False else: _sinfo = endpoint_context.sdb[sid] try: aresp["scope"] = request["scope"] except KeyError: pass rtype = set(request["response_type"][:]) handled_response_type = [] if len(rtype) == 1 and "code" in rtype: fragment_enc = False else: fragment_enc = True if "code" in request["response_type"]: _code = aresp["code"] = endpoint_context.sdb[sid]["code"] handled_response_type.append("code") else: endpoint_context.sdb.update(sid, code=None) _code = None if "token" in rtype: _dic = endpoint_context.sdb.upgrade_to_token(issue_refresh=False, key=sid) logger.debug("_dic: %s" % sanitize(_dic)) for key, val in _dic.items(): if key in aresp.parameters() and val is not None: aresp[key] = val handled_response_type.append("token") try: _access_token = aresp["access_token"] except KeyError: _access_token = None if "id_token" in request["response_type"]: user_info = userinfo_in_id_token_claims(endpoint_context, _sinfo) if request["response_type"] == ["id_token"]: # scopes should be returned here info = collect_user_info(endpoint_context, _sinfo) if user_info is None: user_info = info else: user_info.update(info) # client_info = endpoint_context.cdb[str(request["client_id"])] hargs = {} if {'code', 'id_token', 'token'}.issubset(rtype): hargs = {"code": _code, "access_token": _access_token} elif {'code', 'id_token'}.issubset(rtype): hargs = {"code": _code} elif {'id_token', 'token'}.issubset(rtype): hargs = {"access_token": _access_token} # or 'code id_token' try: id_token = sign_encrypt_id_token(endpoint_context, _sinfo, str(request["client_id"]), user_info=user_info, sign=True, **hargs) except (JWEException, NoSuitableSigningKeys) as err: logger.warning(str(err)) return AuthorizationErrorResponse( error="invalid_request", error_description="Could not sign/encrypt id_token") aresp["id_token"] = id_token _sinfo["id_token"] = id_token handled_response_type.append("id_token") not_handled = rtype.difference(handled_response_type) if not_handled: raise UnSupported("unsupported_response_type", list(not_handled)) return {'response_args': aresp, 'fragment_enc': fragment_enc}
def verify_redirect_uri(endpoint_context, request): """ MUST NOT contain a fragment MAY contain query component :return: An error response if the redirect URI is faulty otherwise None """ try: _redirect_uri = unquote(request["redirect_uri"]) part = urlparse(_redirect_uri) if part.fragment: raise URIError("Contains fragment") (_base, _query) = splitquery(_redirect_uri) if _query: _query = parse_qs(_query) match = False for regbase, rquery in endpoint_context.cdb[str(request["client_id"])][ "redirect_uris"]: # The URI MUST exactly match one of the Redirection URI if _base == regbase: # every registered query component must exist in the # redirect_uri if rquery: for key, vals in rquery.items(): assert key in _query for val in vals: assert val in _query[key] # and vice versa, every query component in the redirect_uri # must be registered if _query: if rquery is None: raise ValueError for key, vals in _query.items(): assert key in rquery for val in vals: assert val in rquery[key] match = True break if not match: raise RedirectURIError("Doesn't match any registered uris") # ignore query components that are not registered return None except Exception: logger.error("Faulty redirect_uri: %s" % request["redirect_uri"]) try: _cinfo = endpoint_context.cdb[str(request["client_id"])] except KeyError: try: cid = request["client_id"] except KeyError: logger.error('No client id found') raise UnknownClient('No client_id provided') else: logger.info("Unknown client: %s" % cid) raise UnknownClient(request["client_id"]) else: logger.info("Registered redirect_uris: %s" % sanitize(_cinfo)) raise RedirectURIError( "Faulty redirect_uri: %s" % request["redirect_uri"])
def parse_request(self, request, auth=None, **kwargs): """ :param request: :param endpoint_context: :param auth: :param kwargs: :return: """ logger.debug("- {} -".format(self.endpoint_name)) logger.info("Request: %s" % sanitize(request)) if request: if isinstance(request, dict): req = self.request_cls(**request) else: if self.request_format == 'jwt': req = self.request_cls().deserialize( request, "jwt", keyjar=self.endpoint_context.keyjar, verify=self.endpoint_context.verify_ssl, **kwargs) elif self.request_format == 'url': parts = urlparse(request) scheme, netloc, path, params, query, fragment = parts[:6] req = self.request_cls().deserialize(query, 'urlencoded') else: req = self.request_cls().deserialize( request, self.request_format) else: req = self.request_cls() # Verify that the client is allowed to do this _client_id = '' try: auth_info = self.client_authentication(req, auth, **kwargs) except UnknownOrNoAuthnMethod: if not self.client_auth_method: try: _client_id = req['client_id'] except KeyError: _client_id = '' else: raise UnAuthorizedClient() else: if 'client_id' in auth_info: req['client_id'] = auth_info['client_id'] _client_id = auth_info['client_id'] else: try: _client_id = req['client_id'] except KeyError: pass try: keyjar = self.endpoint_context.keyjar except AttributeError: keyjar = "" # verify that the request message is correct try: req.verify(keyjar=keyjar, opponent_id=_client_id) except (MissingRequiredAttribute, ValueError, MissingRequiredValue) as err: return self.error_cls(error="invalid_request", error_description="%s" % err) logger.info("Parsed and verified request: %s" % sanitize(req)) # Do any endpoint specific parsing return self.do_post_parse_request(req, _client_id, **kwargs)
def do_client_registration(self, request, client_id, ignore=None): if ignore is None: ignore = [] _context = self.endpoint_context _cinfo = _context.cdb[client_id].copy() logger.debug("_cinfo: %s" % sanitize(_cinfo)) for key, val in request.items(): if key not in ignore: _cinfo[key] = val if "post_logout_redirect_uris" in request: plruri = [] for uri in request["post_logout_redirect_uris"]: if urlparse(uri).fragment: err = ClientRegistrationErrorResponse( error="invalid_configuration_parameter", error_description="post_logout_redirect_uris " "contains " "fragment", ) return err base, query = splitquery(uri) if query: plruri.append((base, parse_qs(query))) else: plruri.append((base, query)) _cinfo["post_logout_redirect_uris"] = plruri if "redirect_uris" in request: try: ruri = self.verify_redirect_uris(request) _cinfo["redirect_uris"] = ruri except InvalidRedirectURIError as e: return ClientRegistrationErrorResponse( error="invalid_redirect_uri", error_description=str(e)) if "sector_identifier_uri" in request: try: _cinfo["si_redirects"], _cinfo[ "sector_id"] = self._verify_sector_identifier(request) except InvalidSectorIdentifier as err: return ResponseMessage(error="invalid_configuration_parameter", error_description=str(err)) for item in ["policy_uri", "logo_uri", "tos_uri"]: if item in request: if verify_url(request[item], _cinfo["redirect_uris"]): _cinfo[item] = request[item] else: return ResponseMessage( error="invalid_configuration_parameter", error_description="%s pointed to illegal URL" % item, ) # Do I have the necessary keys for item in [ "id_token_signed_response_alg", "userinfo_signed_response_alg" ]: if item in request: if request[item] in _context.provider_info[ PREFERENCE2PROVIDER[item]]: ktyp = alg2keytype(request[item]) # do I have this ktyp and for EC type keys the curve if ktyp not in ["none", "oct"]: _k = [] for iss in ["", _context.issuer]: _k.extend( _context.keyjar.get_signing_key( ktyp, alg=request[item], owner=iss)) if not _k: logger.warning('Lacking support for "{}"'.format( request[item])) del _cinfo[item] t = {"jwks_uri": "", "jwks": None} for item in ["jwks_uri", "jwks"]: if item in request: t[item] = request[item] # if it can't load keys because the URL is false it will # just silently fail. Waiting for better times. _context.keyjar.load_keys(client_id, jwks_uri=t["jwks_uri"], jwks=t["jwks"]) n_keys = 0 for kb in _context.keyjar.get(client_id, []): n_keys += len(kb.keys()) msg = "found {} keys for client_id={}" logger.debug(msg.format(n_keys, client_id)) return _cinfo
def create_authn_response(endpoint, request, sid): """ :param endpoint: :param request: :param sid: :return: """ # create the response aresp = AuthorizationResponse() if request.get("state"): aresp["state"] = request["state"] if "response_type" in request and request["response_type"] == ["none"]: fragment_enc = False else: _context = endpoint.endpoint_context _sinfo = _context.sdb[sid] if request.get("scope"): aresp["scope"] = request["scope"] rtype = set(request["response_type"][:]) handled_response_type = [] fragment_enc = True if len(rtype) == 1 and "code" in rtype: fragment_enc = False if "code" in request["response_type"]: _code = aresp["code"] = _context.sdb[sid]["code"] handled_response_type.append("code") else: _context.sdb.update(sid, code=None) _code = None if "token" in rtype: _dic = _context.sdb.upgrade_to_token(issue_refresh=False, key=sid) logger.debug("_dic: %s" % sanitize(_dic)) for key, val in _dic.items(): if key in aresp.parameters() and val is not None: aresp[key] = val handled_response_type.append("token") _access_token = aresp.get("access_token", None) if "id_token" in request["response_type"]: kwargs = {} if {"code", "id_token", "token"}.issubset(rtype): kwargs = {"code": _code, "access_token": _access_token} elif {"code", "id_token"}.issubset(rtype): kwargs = {"code": _code} elif {"id_token", "token"}.issubset(rtype): kwargs = {"access_token": _access_token} if request["response_type"] == ["id_token"]: kwargs["user_claims"] = True try: id_token = _context.idtoken.make(request, _sinfo, **kwargs) except (JWEException, NoSuitableSigningKeys) as err: logger.warning(str(err)) resp = AuthorizationErrorResponse( error="invalid_request", error_description="Could not sign/encrypt id_token", ) return {"response_args": resp, "fragment_enc": fragment_enc} aresp["id_token"] = id_token _sinfo["id_token"] = id_token handled_response_type.append("id_token") not_handled = rtype.difference(handled_response_type) if not_handled: resp = AuthorizationErrorResponse( error="invalid_request", error_description="unsupported_response_type") return {"response_args": resp, "fragment_enc": fragment_enc} return {"response_args": aresp, "fragment_enc": fragment_enc}
def parse_request(self, request, auth=None, **kwargs): """ :param request: The request the server got :param auth: Client authentication information :param kwargs: extra keyword arguments :return: """ LOGGER.debug("- {} -".format(self.endpoint_name)) LOGGER.info("Request: %s" % sanitize(request)) if request: if isinstance(request, (dict, Message)): req = self.request_cls(**request) else: _cls_inst = self.request_cls() if self.request_format == "jwt": req = _cls_inst.deserialize( request, "jwt", keyjar=self.endpoint_context.keyjar, verify=self.endpoint_context.httpc_params["verify"], **kwargs) elif self.request_format == "url": parts = urlparse(request) scheme, netloc, path, params, query, fragment = parts[:6] req = _cls_inst.deserialize(query, "urlencoded") else: req = _cls_inst.deserialize(request, self.request_format) else: req = self.request_cls() # Verify that the client is allowed to do this _client_id = "" try: auth_info = self.client_authentication(req, auth, endpoint=self.name, **kwargs) except UnknownOrNoAuthnMethod: # If there is no required client authentication method if not self.client_authn_method: try: _client_id = req["client_id"] except KeyError: _client_id = "" else: raise else: if "client_id" in auth_info: req["client_id"] = auth_info["client_id"] _client_id = auth_info["client_id"] else: _client_id = req.get("client_id") keyjar = self.endpoint_context.keyjar # verify that the request message is correct try: req.verify(keyjar=keyjar, opponent_id=_client_id) except (MissingRequiredAttribute, ValueError, MissingRequiredValue) as err: return self.error_cls(error="invalid_request", error_description="%s" % err) LOGGER.info("Parsed and verified request: %s" % sanitize(req)) # Do any endpoint specific parsing return self.do_post_parse_request(req, _client_id, **kwargs)
def collect_user_info(endpoint_context, session, userinfo_claims=None, scope_to_claims=None): """ Collect information about a user. This can happen in two cases, either when constructing an IdToken or when returning user info through the UserInfo endpoint :param session: Session information :param userinfo_claims: user info claims :return: User info """ authn_req = session["authn_req"] if scope_to_claims is None: scope_to_claims = endpoint_context.scope2claims supported_scopes = [ s for s in authn_req["scope"] if s in endpoint_context.provider_info["scopes_supported"] ] if userinfo_claims is None: uic = scope2claims(supported_scopes, map=scope_to_claims) # Get only keys allowed by user and update the dict if such info # is stored in session perm_set = session.get("permission") if perm_set: uic = {key: uic[key] for key in uic if key in perm_set} uic = update_claims(session, "userinfo", provider_info=endpoint_context.provider_info, old_claims=uic) if uic: userinfo_claims = Claims(**uic) else: userinfo_claims = None logger.debug("userinfo_claim: %s" % sanitize(userinfo_claims.to_dict())) logger.debug("Session info: %s" % sanitize(session)) authn_event = session["authn_event"] if authn_event: uid = authn_event["uid"] else: uid = session["uid"] info = endpoint_context.userinfo(uid, authn_req["client_id"], userinfo_claims) if "sub" in userinfo_claims: if not claims_match(session["sub"], userinfo_claims["sub"]): raise FailedAuthentication("Unmatched sub claim") info["sub"] = session["sub"] try: logger.debug("user_info_response: {}".format(info)) except UnicodeEncodeError: logger.debug("user_info_response: {}".format(info.encode("utf-8"))) return info