def test_pushed_auth_request(self): _msg = Message().from_urlencoded(AUTHN_REQUEST) _jwt = JWT(key_jar=self.rp_keyjar, iss="s6BhdRkqt3") _jws = _jwt.pack(_msg.to_dict()) authn_request = "request={}".format(_jws) http_info = { "headers": {"authorization": "Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3"} } _req = self.pushed_authorization_endpoint.parse_request(authn_request, http_info=http_info) assert isinstance(_req, AuthorizationRequest) _req = remove_jwt_parameters(_req) assert set(_req.keys()) == { "state", "redirect_uri", "response_type", "scope", "code_challenge_method", "client_id", "code_challenge", "request", "__verified_request", }
def from_dict(self, dictionary, **kwargs): Message.from_dict(self, dictionary, **kwargs) if "jwk" in self: self.key = key_from_jwk_dict(self["jwk"]) self.key.deserialize() return self
def test_do_response_body_urlencoded(self): self.endpoint.response_placement = "body" self.endpoint.response_format = "urlencoded" msg = self.endpoint.do_response(EXAMPLE_MSG) assert isinstance(msg, dict) umsg = Message().from_urlencoded(msg["response"]) assert set(umsg.keys()) == set(EXAMPLE_MSG.keys())
def __init__(self, set_defaults=True, **kwargs): self.key = None Message.__init__(self, set_defaults=set_defaults, **kwargs) if self.key: pass elif "jwk" in self: self.key = key_from_jwk_dict(self["jwk"]) self.key.deserialize()
def decode_jwt(cls, jwt, verify=True, format='dict'): keyjar = cls.keyjar() try: jwt = Message().from_jwt(jwt, keyjar=keyjar) jwt.verify() except Exception as e: logger.error(e) raise InvalidJWT('Not a valid JWT: signature failed on save.') if format: return getattr(jwt, 'to_{}'.format(format))() else: return jwt
def test_do_response_url_fragment(self): self.endpoint.response_placement = "url" self.endpoint.response_format = "urlencoded" msg = self.endpoint.do_response(EXAMPLE_MSG, fragment_enc=True, return_uri="https://example.org/cb_i") assert isinstance(msg, dict) parse_res = urlparse(msg["response"]) assert parse_res.scheme == "https" assert parse_res.netloc == "example.org" assert parse_res.path == "/cb_i" umsg = Message().from_urlencoded(parse_res.fragment) assert set(umsg.keys()) == set(EXAMPLE_MSG.keys())
def test_do_response_url_fragment(self): self.endpoint.response_placement = 'url' self.endpoint.response_format = 'urlencoded' msg = self.endpoint.do_response(EXAMPLE_MSG, fragment_enc=True, return_uri='https://example.org/cb_i') assert isinstance(msg, dict) parse_res = urlparse(msg['response']) assert parse_res.scheme == 'https' assert parse_res.netloc == 'example.org' assert parse_res.path == '/cb_i' umsg = Message().from_urlencoded(parse_res.fragment) assert set(umsg.keys()) == set(EXAMPLE_MSG.keys())
def test_end_session_endpoint_with_cookie_wrong_user(self): # Need cookie and ID Token to figure this out id_token = self._auth_with_id_token("1234567") cookie = self._create_cookie("diggins", "_sid_", "1234567", "client_1") msg = Message(id_token=id_token) verify_id_token(msg, keyjar=self.session_endpoint.endpoint_context.keyjar) msg2 = Message(id_token_hint=id_token) msg2[verified_claim_name("id_token_hint")] = msg[ verified_claim_name("id_token") ] with pytest.raises(ValueError): self.session_endpoint.process_request(msg2, cookie=cookie)
def self_sign(self, req, receiver='', aud=None): """ Sign the extended request. :param req: Request, a :py:class:`fedoidcmsg.MetadataStatement' instance :param receiver: The intended user of this metadata statement :param aud: The audience, a list of receivers. :return: An augmented set of request arguments """ if self.entity_id: _iss = self.entity_id else: _iss = self.iss creq = req.copy() if not 'metadata_statement_uris' in creq and not \ 'metadata_statements' in creq: _copy = creq.copy() _jws = self.self_signer.sign(_copy, receiver=receiver, iss=_iss, aud=aud) sms_spec = {'metadata_statements': {self.iss: _jws}} else: for ref in ['metadata_statement_uris', 'metadata_statements']: try: del creq[ref] except KeyError: pass sms_spec = {'metadata_statements': Message()} for ref in ['metadata_statement_uris', 'metadata_statements']: if ref not in req: continue for foid, value in req[ref].items(): _copy = creq.copy() _copy[ref] = Message() _copy[ref][foid] = value _jws = self.self_signer.sign(_copy, receiver=receiver, iss=_iss, aud=aud) sms_spec['metadata_statements'][foid] = _jws creq.update(sms_spec) return creq
def test_end_session_endpoint_with_wrong_post_logout_redirect_uri(self): self._code_auth("1234567") self._code_auth2("abcdefg") id_token = self._auth_with_id_token("1234567") _sdb = self.session_endpoint.endpoint_context.sdb _sid = self._get_sid() cookie = self._create_cookie("diana", _sid, "1234567", "client_1") post_logout_redirect_uri = "https://demo.example.com/log_out" msg = Message(id_token=id_token) verify_id_token(msg, keyjar=self.session_endpoint.endpoint_context.keyjar) with pytest.raises(RedirectURIError): self.session_endpoint.process_request( { "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde", "id_token_hint": id_token, verified_claim_name("id_token_hint"): msg[ verified_claim_name("id_token") ], }, cookie=cookie, )
def test_pushed_auth_urlencoded_process(self): _req = self.pushed_authorization_endpoint.parse_request( AUTHN_REQUEST, auth="Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3") assert isinstance(_req, AuthorizationRequest) assert set(_req.keys()) == { "state", "redirect_uri", "response_type", "scope", "code_challenge_method", "client_id", "code_challenge", } _resp = self.pushed_authorization_endpoint.process_request(_req) _msg = Message().from_urlencoded(AUTHN_REQUEST) assert _resp["return_uri"] == _msg["redirect_uri"] # And now for the authorization request with the OP provided request_uri _msg["request_uri"] = _resp["http_response"]["request_uri"] for parameter in ["code_challenge", "code_challenge_method"]: del _msg[parameter] _req = self.authorization_endpoint.parse_request(_msg) assert "code_challenge" in _req
def test_end_session_endpoint_with_wrong_post_logout_redirect_uri(self): _resp = self._code_auth("1234567") self._code_auth2("abcdefg") resp_args, _session_id = self._auth_with_id_token("1234567") id_token = resp_args["id_token"] cookie = self._create_cookie(_session_id) http_info = {"cookie": [cookie]} post_logout_redirect_uri = "https://demo.example.com/log_out" msg = Message(id_token=id_token) verify_id_token( msg, keyjar=self.session_endpoint.server_get("endpoint_context").keyjar) with pytest.raises(RedirectURIError): self.session_endpoint.process_request( { "post_logout_redirect_uri": post_logout_redirect_uri, "state": "abcde", "id_token_hint": id_token, verified_claim_name("id_token_hint"): msg[verified_claim_name("id_token")], }, http_info=http_info, )
def create(data, alg=None, lifetime=None, **kwargs): """ Only signed JWT from here """ alg = alg or JWTAUTH_ALGORITHM keys = import_string(JWTAUTH_KEYJAR_HANDLER).keys() access_token, rtoken = data['access_token'], data['refresh_token'] return { 'access_token': Message(**access_token).to_jwt(keys, alg), 'refresh_token': Message(**rtoken).to_jwt(keys, alg), 'token_type': 'bearer', 'expires_in': lifetime or data.get('expires_in', JWTAUTH_ACCESS_TOKEN_LIFETIME) }
def test_quote(): csb = ClientSecretBasic() http_args = csb.construct( Message(), password= '******', user='******') assert http_args['headers'][ 'Authorization'] == 'Basic Nzk2ZDhmYWUtYTQyZi00ZTRmLWFiMjUtZDYyMDViNmQ0ZmEyOk1LRU0lMkZBN1BrbjdKdVUwTEFjeHlIVkt2d2RjenN1Z2FQVTBCaWVMYjRDYlFBZ1FqJTJCeXBjYW5GT0NiMCUyRkZBNWg='
def decode_token(txt, attr_name='access_token', verify_sign=True): issuer = oidcop_app.srv_config['issuer'] jwks_path = oidcop_app.srv_config.conf['keys']['private_path'] jwks = json.loads(open(jwks_path).read()) key_jar = KeyJar() key_jar.import_jwks(jwks, issuer=issuer) msg = Message().from_jwt(txt, keyjar=key_jar, verify=verify_sign) return msg
def post_parse_response(self, response, **kwargs): _context = self.client_get("service_context") _state_interface = _context.state _args = _state_interface.multiple_extend_request_args( {}, kwargs['state'], ['id_token'], ['auth_response', 'token_response', 'refresh_token_response'] ) try: _sub = _args['id_token']['sub'] except KeyError: logger.warning("Can not verify value on sub") else: if response['sub'] != _sub: raise ValueError('Incorrect "sub" value') try: _csrc = response["_claim_sources"] except KeyError: pass else: for csrc, spec in _csrc.items(): if "JWT" in spec: try: aggregated_claims = Message().from_jwt( spec["JWT"].encode("utf-8"), keyjar=_context.keyjar) except MissingSigningKey as err: logger.warning( 'Error encountered while unpacking aggregated ' 'claims'.format(err)) else: claims = [value for value, src in response["_claim_names"].items() if src == csrc] for key in claims: response[key] = aggregated_claims[key] elif 'endpoint' in spec: _info = { "headers": self.get_authn_header( {}, self.default_authn_method, authn_endpoint=self.endpoint_name, key=kwargs["state"] ), "url": spec["endpoint"] } # Extension point for meth in self.post_parse_process: response = meth(response, _state_interface, kwargs['state']) _state_interface.store_item(response, 'user_info', kwargs['state']) return response
def test_end_session_endpoint_with_cookie_id_token_and_unknown_sid(self): # Need cookie and ID Token to figure this out resp_args, _session_id = self._auth_with_id_token("1234567") id_token = resp_args["id_token"] _uid, _cid, _gid = self.session_manager.decrypt_session_id(_session_id) cookie = self._create_cookie( self.session_manager.session_key(_uid, "client_66", _gid)) http_info = {"cookie": [cookie]} msg = Message(id_token=id_token) verify_id_token( msg, keyjar=self.session_endpoint.server_get("endpoint_context").keyjar) msg2 = Message(id_token_hint=id_token) msg2[verified_claim_name("id_token_hint")] = msg[verified_claim_name( "id_token")] with pytest.raises(ValueError): self.session_endpoint.process_request(msg2, http_info=http_info)
def gather_metadata_statements(self, fos=None, context=''): """ Only gathers metadata statements and returns them. :param fos: Signed metadata statements from these Federation Operators should be added. :param context: context of the metadata exchange :return: Dictionary with signed Metadata Statements as values """ if not context: context = self.context _res = {} if self.metadata_statements: try: cms = self.metadata_statements[context] except KeyError: if self.metadata_statements == { 'register': {}, 'discovery': {}, 'response': {} }: # No superior so an FO then. Nothing to add .. pass else: logger.error( 'No metadata statements for this context: {}'.format( context)) raise ValueError('Wrong context "{}"'.format(context)) else: if cms != {}: if fos is None: fos = list(cms.keys()) for f in fos: try: val = cms[f] except KeyError: continue if val.startswith('http'): value_type = 'metadata_statement_uris' else: value_type = 'metadata_statements' try: _res[value_type][f] = val except KeyError: _res[value_type] = Message() _res[value_type][f] = val return _res
def push_authorization(request_args, service, **kwargs): """ :param request_args: All the request arguments as a AuthorizationRequest instance :param service: The service to which this post construct method is applied. :param kwargs: Extra keyword arguments. """ method_args = service.service_context.add_on["pushed_authorization"] # construct the message body if method_args["body_format"] == "urlencoded": _body = request_args.to_urlencoded() else: _jwt = JWT(key_jar=service.service_context.keyjar, iss=service.service_context.base_url) _jws = _jwt.pack(request_args.to_dict()) _msg = Message(request=_jws) if method_args["merge_rule"] == "lax": for param in request_args.required_parameters(): _msg[param] = request_args.get(param) _body = _msg.to_urlencoded() # Send it to the Pushed Authorization Request Endpoint resp = method_args["http_client"].get( service.service_context. provider_info["pushed_authorization_request_endpoint"], data=_body) if resp.status_code == 200: _resp = Message().from_json(resp.text) _req = JWTSecuredAuthorizationRequest(request_uri=_resp["request_uri"]) if method_args["merge_rule"] == "lax": for param in request_args.required_parameters(): _req[param] = request_args.get(param) request_args = _req return request_args
def token_request(request): logger.debug(f'{request.headers}: {request.POST}') id_token = { "sub": "Microsoft:[email protected]", "nonce": "ITyym7MixGzWnTp4AMFimVk5", "at_hash": "a_jseUswllpYcJVPYEcj5w", "sid": "3dd91e80-ec4b-4cca-af44-58dfdd0544cb", "aud": "2c43a070-425f-4613-859c-d234ec7d71af", "exp": 1615749924, "iat": 1615746324, "iss": ISSUER } jwt_id_token = Message(**id_token) keys = [RSAKey(**JWK_PRIVATE)] signed_jwt_id_token = jwt_id_token.to_jwt(keys, "RS256") return JsonResponse({ 'access_token': 'sadasd', 'id_token': signed_jwt_id_token, 'token_type': 'bearer', 'expires_in': 3600, 'scope': 'openid profile' })
def add_code_challenge(request_args, service, **kwargs): """ PKCE RFC 7636 support To be added as a post_construct method to an :py:class:`oidcservice.oidc.service.Authorization` instance :param service: The service that uses this function :param request_args: Set of request arguments :param kwargs: Extra set of keyword arguments :return: Updated set of request arguments """ _kwargs = service.service_context.add_on["pkce"] try: cv_len = _kwargs['code_challenge_length'] except KeyError: cv_len = 64 # Use default # code_verifier: string of length cv_len code_verifier = unreserved(cv_len) _cv = code_verifier.encode() try: _method = _kwargs['code_challenge_method'] except KeyError: _method = 'S256' try: # Pick hash method _hash_method = CC_METHOD[_method] # Use it on the code_verifier _hv = _hash_method(_cv).digest() # base64 encode the hash value code_challenge = b64e(_hv).decode('ascii') except KeyError: raise Unsupported( 'PKCE Transformation method:{}'.format(_method)) _item = Message(code_verifier=code_verifier, code_challenge_method=_method) service.store_item(_item, 'pkce', request_args['state']) request_args.update( { "code_challenge": code_challenge, "code_challenge_method": _method }) return request_args, {}
def multiple_extend_request_args(self, args, key, parameters, item_types, orig=False): """ Go through a set of items (by their type) and add the attribute-value that match the list of parameters to the arguments If the same parameter occurs in 2 different items then the value in the later one will be the one used. :param args: Initial set of arguments :param key: Key to the State information in the state database :param parameters: A list of parameters that we're looking for :param item_types: A list of item_type specifying which items we are interested in. :param orig: Where the value of a claim is a signed JWT return that. :return: A possibly augmented set of arguments. """ _state = self.get_state(key) for typ in item_types: try: _item = Message(**_state[typ]) except KeyError: continue for parameter in parameters: if orig: try: args[parameter] = _item[parameter] except KeyError: pass else: try: args[parameter] = _item[verified_claim_name(parameter)] except KeyError: try: args[parameter] = _item[parameter] except KeyError: pass return args
def validate_jwt(jwt: str, key_jar): try: recv = Message().from_jwt(jwt, keyjar=key_jar) return recv.verify(), key_jar except: return False
def decode_token(bearer_token, keyjar, verify_sign=True): msg = Message().from_jwt(bearer_token, keyjar=keyjar, verify=verify_sign) return msg.to_dict()
def to_jwt(self, key=None, algorithm="", lev=0, lifetime=0): self.pack(alg=algorithm, lifetime=lifetime) return Message.to_jwt(self, key=key, algorithm=algorithm, lev=lev)
def evidence_list_deser(val, sformat="urlencoded", lev=0): if isinstance(val, dict): return [Message(**val)] _res = [evidence_deser(v, sformat) for v in val] return _res
issuer = "https://127.0.0.1:8000" issuer_metadata_url = '{}/.well-known/openid-configuration'.format(issuer) issuer_metadata = requests.get(issuer_metadata_url, verify=False).text issuer_jwks_uri = json.loads(issuer_metadata)['jwks_uri'] jwks_raw = requests.get(issuer_jwks_uri, verify=False).text jwks = json.loads(jwks_raw) # built keyjar key_jar = KeyJar() key_jar.import_jwks(jwks, issuer=issuer) # it depends by JWT, is the signing key identifier could be extracted from it then it should be verified, otherwise not # see difference between at and unverifiable_at verify_sign = 1 at = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJXdG9SekV4VXkxak9GVXlSV2hwZUdkbFREWlBaME55TW1ka05ERlFaakJSUzJreVQwaExVazVJUVEifQ.eyJzdWIiOiAiODAzMjcwNDJiOTZiOWYxYzAwZDlkMDRkYjgxNmU4NGFmNGUzNjE2ZGIxZDA2OTRiMTNhYjg2ZjQ5ZmQyNTFiZiIsICJhdXRoX3RpbWUiOiAxNTc2MDU4MTc4LCAiYWNyIjogIm9pZGNlbmRwb2ludC51c2VyX2F1dGhuLmF1dGhuX2NvbnRleHQuSU5URVJORVRQUk9UT0NPTFBBU1NXT1JEIiwgImVtYWlsIjogImdpdXNlcHBlLmRlbWFyY29AdW5pY2FsLml0IiwgIm5vbmNlIjogImFFbERSTUNBUWhHZWVBVUs1b0RyRG1PdSIsICJpc3MiOiAiaHR0cHM6Ly8xMjcuMC4wLjE6ODAwMCIsICJpYXQiOiAxNTc2MDU4MTc4LCAiZXhwIjogMTU3NjA1ODQ3OCwgImF1ZCI6IFsiejl3VU90dG1GM3FjIl19.lAKU1F_77T11gNcDvRMHy3hEJANaCkjTtbyR5D7DmOFOTKTYBWIT_ZqEmGnBmGpDBhnGcwbupFavltAOq03PGyJYu-Bxk3ktqTCjfcLTQroE-o3KTx_xYjVDh60p3RAAK9iD5ligmH2I5vFtFl-5ckTl2tX9Zn_IAqJrpuCIvgwBHzje-61upXKyXzBthOKa2dWAlOMHXYKlFV_4mhnr3Q3RNP8hmgFkltD3d-INo8tlysRQebuxnQ7LizQtOIhWACioHZosfpQIu2_L89PiTdcUuFeAeRNa_kUe9Oztk7ffCcDk2Xqa5C-8gxpgHTX6STEa59jeG07q7_s4pj7Fig" unverifiable_at = "eyJhbGciOiJFUzI1NiIsImtpZCI6IlQwZGZTM1ZVYUcxS1ZubG9VVTQwUXpJMlMyMHpjSHBRYlMxdGIzZ3hZVWhCYzNGaFZWTlpTbWhMTUEifQ.eyJzaWQiOiAiNzUyYTc2NWZlMjg4MGE4NmU2OTlkNTcxZWM4YTE2MWE2NTkzZjVhMmJlNzdkMDkzZDRmNzU2MmUiLCAidHR5cGUiOiAiVCIsICJzdWIiOiAiODAzMjcwNDJiOTZiOWYxYzAwZDlkMDRkYjgxNmU4NGFmNGUzNjE2ZGIxZDA2OTRiMTNhYjg2ZjQ5ZmQyNTFiZiIsICJlbWFpbCI6ICJnaXVzZXBwZS5kZW1hcmNvQHVuaWNhbC5pdCIsICJnZW5kZXIiOiAibWFsZSIsICJpc3MiOiAiIiwgImlhdCI6IDE1NzYwNTI3ODMsICJleHAiOiAxNTc2MDU2MzgzLCAiYXVkIjogWyJ6OXdVT3R0bUYzcWMiLCAiaHR0cHM6Ly8xMjcuMC4wLjE6ODAwMCJdfQ.xjkeVPF8fcHZVVZa3wYHxatyoVizML10iD579T_HB1tj7Cm_JNRdKB0nUoq7HddfeeNG911YNWHHl0lD9TQ2gA" m = Message().from_jwt(at, keyjar=key_jar, verify=verify_sign) print('Access Token header: ', m.jws_header) print('Access Token payload: ', m) t = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJXdG9SekV4VXkxak9GVXlSV2hwZUdkbFREWlBaME55TW1ka05ERlFaakJSUzJreVQwaExVazVJUVEifQ.eyJzdWIiOiAiODAzMjcwNDJiOTZiOWYxYzAwZDlkMDRkYjgxNmU4NGFmNGUzNjE2ZGIxZDA2OTRiMTNhYjg2ZjQ5ZmQyNTFiZiIsICJhdXRoX3RpbWUiOiAxNTc2MDUyNzc3LCAiYWNyIjogIm9pZGNlbmRwb2ludC51c2VyX2F1dGhuLmF1dGhuX2NvbnRleHQuSU5URVJORVRQUk9UT0NPTFBBU1NXT1JEIiwgImVtYWlsIjogImdpdXNlcHBlLmRlbWFyY29AdW5pY2FsLml0IiwgIm5vbmNlIjogIlRKdnFjYW85RjRubkp5bTBPZnlNeXlBbSIsICJpc3MiOiAiaHR0cHM6Ly8xMjcuMC4wLjE6ODAwMCIsICJpYXQiOiAxNTc2MDUyNzgzLCAiZXhwIjogMTU3NjA1MzA4MywgImF1ZCI6IFsiejl3VU90dG1GM3FjIl19.kQq0WBXRTUcKY7RI8A0Yb1CyoOds5-EnDrc7mhc4pzp5xig9lOUWaBts6Q5vwoI29-iadPUf62SIcrBuGPAyvAFHC-pqSy-vcXbYFpWN18n9SaL7O2fkQD-PlPl088X6fJ-Muqt_RvkRjY_cwY-VuJNh3_uXXZdsLnUimsLI3E4q6aCMkoaqDajbq35L40_xcjRhmQHh9Rn_4pIfnRGKZdsQICYJ6ivLSO5dxxydDtyA1nip1ER8hV3ISj8qfJq1_tFe5JrvcEWXbs9MMmsnlXTTB6X-71ysmzqVcjY2o-CviZGQTwEXwJYY54_u24NUwwM72pWVL97N1ug7nIlusA" m = Message().from_jwt(t, keyjar=key_jar, verify=verify_sign) print('ID Token header: ', m.jws_header) print('ID Token payload: ', m)
FED_KEYDEF = [{"type": "EC", "crv": "P-256", "use": ["sig"]}] # Identifiers for all the entities ALL = ['https://edugain.org', 'https://swamid.sunet.se'] # Create the federation entities FEDENT = create_federation_entities(ALL, FED_KEYDEF, root_dir='../') SWAMID = FEDENT['https://swamid.sunet.se'] EDUGAIN = FEDENT['https://edugain.org'] FEDENT[SUNET_OP.iss] = SUNET_OP _sms = create_compounded_metadata_statement( [SUNET_OP.iss, SWAMID.iss, EDUGAIN.iss], FEDENT, { SWAMID.iss: Message(), SUNET_OP.iss: _info }, 'discovery', lifetime=86400) fp = open('sms/discovery/{}'.format(quote_plus(EDUGAIN.iss)), 'w') fp.write(_sms) fp.close() try: _sms = make_signing_sequence([SUNET_OP.iss, SWAMID.iss, EDUGAIN.iss], FEDENT, 'response', lifetime=86400) except Exception as err:
def verify(self, **kwargs): Message.verify(self, **kwargs) if self["typ"] != "dpop+jwt": raise ValueError("Wrong type") if self["alg"] == "none": raise ValueError("'none' is not allowed as signing algorithm")
from oidcmsg.message import Message KEYDEFS = [ { "type": "RSA", "key": "", "use": ["sig"] }, { "type": "EC", "crv": "P-256", "use": ["sig"] }, ] REQ = Message(foo="bar", hej="hopp") EXAMPLE_MSG = { "name": "Jane Doe", "given_name": "Jane", "family_name": "Doe", "email": "*****@*****.**", "picture": "http://example.com/janedoe/me.jpg", } def pre(args, request, endpoint_context): args.update( {"name": "{}, {}".format(args["family_name"], args["given_name"])}) return args