def test_access_and_id_token_by_reference(self, httpserver): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = {'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud} idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=client.service_context.keyjar.get_signing_key('oct'), algorithm="HS256", lifetime=300) _info = {"access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600} at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json()) client.service['accesstoken'].endpoint = httpserver.url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) resp = self.rph.get_access_and_id_token(state=res['state']) assert resp['access_token'] == 'accessTok' assert isinstance(resp['id_token'], IdToken)
def test_get_access_token(self): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=GITHUB_KEY.get_signing_key(issuer_id=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("POST", _url, body=at.to_json(), adding_headers={"Content-Type": "application/json"}, status=200) client.service['accesstoken'].endpoint = _url auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = self.rph.finalize_auth(client, _session['iss'], auth_response.to_dict()) resp = self.rph.get_access_token(res['state'], client) assert set(resp.keys()) == { 'access_token', 'expires_in', 'id_token', 'token_type', '__verified_id_token', '__expires_at' } atresp = client.service['accesstoken'].get_item( AccessTokenResponse, 'token_response', res['state']) assert set(atresp.keys()) == { 'access_token', 'expires_in', 'id_token', 'token_type', '__verified_id_token', '__expires_at' }
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 rphandler_setup(self, httpserver): self.rph = RPHandler(base_url=BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY) res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer=_github_id), _github_id) idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=GITHUB_KEY.get_signing_key( 'rsa', owner=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600, 'refresh_token': 'refreshing' } at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json(), headers={'Content-Type': 'application/json'}) client.service['accesstoken'].endpoint = httpserver.url _response = AuthorizationResponse(code='access_code', state=res['state']) auth_response = self.rph.finalize_auth(client, _session['iss'], _response.to_dict()) token_resp = self.rph.get_access_and_id_token(auth_response, client=client) httpserver.serve_content('{"sub":"EndUserSubject"}', headers={'Content-Type': 'application/json'}) client.service['userinfo'].endpoint = httpserver.url self.rph.get_user_info(res['state'], client, token_resp['access_token']) self.state = res['state']
def test_access_and_id_token_by_reference(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='github') _session = rph_1.get_session_information(res['state']) client = rph_1.issuer2rp[_session['iss']] _context = client.client_get("service_context") _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = _context.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } _github_id = iss_id('github') _context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) idts = IdToken(**idval) _signed_jwt = idts.to_jwt(key=GITHUB_KEY.get_signing_key( 'rsa', issuer_id=_github_id), algorithm="RS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) _url = "https://github.com/token" with responses.RequestsMock() as rsps: rsps.add("POST", _url, body=at.to_json(), adding_headers={"Content-Type": "application/json"}, status=200) client.client_get("service", 'accesstoken').endpoint = _url _response = AuthorizationResponse(code='access_code', state=res['state']) _ = rph_1.finalize_auth(client, _session['iss'], _response.to_dict()) resp = rph_1.get_access_and_id_token(state=res['state']) assert resp['access_token'] == 'accessTok' assert isinstance(resp['id_token'], IdToken)
def test_finalize_auth(self): res = self.rph.begin(issuer_id='linkedin') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = self.rph.finalize_auth(client, _session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'state', 'code'} aresp = client.service['authorization'].get_item( AuthorizationResponse, 'auth_response', res['state']) assert set(aresp.keys()) == {'state', 'code'}
def test_finalize(self): auth_query = self.rph.begin(issuer_id='github') # The authorization query is sent and after successful authentication client = self.rph.get_client_from_session_key( state=auth_query['state']) # register a response p = urlparse( CLIENT_CONFIG['github']['provider_info']['authorization_endpoint']) self.mock_op.register_get_response(p.path, 'Redirect', 302) _ = client.http(auth_query['url']) # the user is redirected back to the RP with a positive response auth_response = AuthorizationResponse(code='access_code', state=auth_query['state']) # need session information and the client instance _session = self.rph.get_session_information(auth_response['state']) client = self.rph.get_client_from_session_key( state=auth_response['state']) # Faking resp = construct_access_token_response( _session['auth_request']['nonce'], issuer=self.issuer, client_id=CLIENT_CONFIG['github']['client_id'], key_jar=GITHUB_KEY) p = urlparse( CLIENT_CONFIG['github']['provider_info']['token_endpoint']) self.mock_op.register_post_response( p.path, resp.to_json(), 200, {'content-type': "application/json"}) _info = OpenIDSchema(sub='EndUserSubject', given_name='Diana', family_name='Krall', occupation='Jazz pianist') p = urlparse( CLIENT_CONFIG['github']['provider_info']['userinfo_endpoint']) self.mock_op.register_get_response( p.path, _info.to_json(), 200, {'content-type': "application/json"}) _github_id = iss_id('github') client.service_context.keyjar.import_jwks( GITHUB_KEY.export_jwks(issuer_id=_github_id), _github_id) # do the rest (= get access token and user info) # assume code flow resp = self.rph.finalize(_session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'userinfo', 'state', 'token', 'id_token'}
def test_get_access_token(self, httpserver): res = self.rph.begin(issuer_id='github') _session = self.rph.get_session_information(res['state']) client = self.rph.issuer2rp[_session['iss']] _nonce = _session['auth_request']['nonce'] _iss = _session['iss'] _aud = client.client_id idval = { 'nonce': _nonce, 'sub': 'EndUserSubject', 'iss': _iss, 'aud': _aud } idts = IdToken(**idval) _signed_jwt = idts.to_jwt( key=client.service_context.keyjar.get_signing_key('oct'), algorithm="HS256", lifetime=300) _info = { "access_token": "accessTok", "id_token": _signed_jwt, "token_type": "Bearer", "expires_in": 3600 } at = AccessTokenResponse(**_info) httpserver.serve_content(at.to_json(), headers={'Content-Type': 'application/json'}) client.service['accesstoken'].endpoint = httpserver.url auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = self.rph.finalize_auth(client, _session['iss'], auth_response.to_dict()) resp = self.rph.get_access_token(res['state'], client) assert set(resp.keys()) == { 'access_token', 'expires_in', 'id_token', 'token_type', '__verified_id_token', '__expires_at' } atresp = client.service['accesstoken'].get_item( AccessTokenResponse, 'token_response', res['state']) assert set(atresp.keys()) == { 'access_token', 'expires_in', 'id_token', 'token_type', '__verified_id_token', '__expires_at' }
def test_response_mode(self, response_mode): request = AuthorizationRequest( client_id="client_1", response_type=["code"], redirect_uri="https://example.com/cb", state="state", scope="openid", response_mode=response_mode, ) response_args = AuthorizationResponse(scope="openid", code="abcdefghijklmnop") if response_mode == "fragment": info = self.endpoint.response_mode( request, response_args, request["redirect_uri"], fragment_enc=True ) else: info = self.endpoint.response_mode(request, response_args, request["redirect_uri"]) if response_mode == "form_post": assert set(info.keys()) == { "response_msg", "content_type", "response_placement", } elif response_mode == "fragment": assert set(info.keys()) == {"response_args", "return_uri", "fragment_enc"} elif response_mode == "query": assert set(info.keys()) == {"response_args", "return_uri"}
def test_response_mode_form_post(self): request = {"response_mode": "form_post"} info = { "response_args": AuthorizationResponse(foo="bar"), "return_uri": "https://example.com/cb", } info = self.endpoint.response_mode(request, **info) assert set(info.keys()) == {"response_args", "return_uri", "response_msg"} assert info["response_msg"] == FORM_POST
def test_finalize_auth(self): rph_1 = RPHandler(BASE_URL, client_configs=CLIENT_CONFIG, keyjar=CLI_KEY, module_dirs=['oidc']) res = rph_1.begin(issuer_id='linkedin') _session = rph_1.get_session_information(res['state']) client = rph_1.issuer2rp[_session['iss']] auth_response = AuthorizationResponse(code='access_code', state=res['state']) resp = rph_1.finalize_auth(client, _session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'state', 'code'} aresp = client.client_get( "service", 'authorization').client_get("service_context").state.get_item( AuthorizationResponse, 'auth_response', res['state']) assert set(aresp.keys()) == {'state', 'code'}
def test_response_mode_form_post(self): request = {"response_mode": "form_post"} info = { "response_args": AuthorizationResponse(foo="bar"), "return_uri": "https://example.com/cb", } info = self.endpoint.response_mode(request, **info) assert set(info.keys()) == { "response_args", "return_uri", "response_msg", "content_type", "response_placement" } assert info["response_msg"] == FORM_POST.format( action="https://example.com/cb", inputs='<input type="hidden" name="foo" value="bar"/>', )
def test_finalize(self): auth_query = self.rph.begin(issuer_id='github') # The authorization query is sent and after successful authentication client = self.rph.get_client_from_session_key( state=auth_query['state']) _ = client.http(auth_query['url']) # the user is redirected back to the RP with a positive response auth_response = AuthorizationResponse(code='access_code', state=auth_query['state']) # need session information and the client instance _session = self.rph.get_session_information(auth_response['state']) client = self.rph.get_client_from_session_key( state=auth_response['state']) # Faking self.rph.httplib.keyjar = client.service_context.keyjar # do the rest (= get access token and user info) # assume code flow resp = self.rph.finalize(_session['iss'], auth_response.to_dict()) assert set(resp.keys()) == {'userinfo', 'state', 'token'}
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}
'nonce': NONCE }) print() print('--- Authorization, request ----') print('uri: {}'.format(info['url'])) op_authz_resp = { 'state': 'Oh3w3gKlvoM2ehFqlxI3HIK5', 'scope': 'openid', 'code': 'Z0FBQUFBQmFkdFFjUVpFWE81SHU5N1N4N01', 'iss': OP_BASEURL, 'client_id': 'zls2qhN1jO6A' } _authz_rep = AuthorizationResponse(**op_authz_resp) print(_authz_rep.to_urlencoded()) _resp = service['authorization'].parse_response(_authz_rep.to_urlencoded()) service['authorization'].update_service_context(_resp, key=STATE) print() print('--- Authorization registration, response ----') print(_resp) _json = service['authorization'].state_db.get(STATE) _state = State().from_json(_json) print('code: {}'.format(_state['auth_response']['code'])) # =================== Access token ==================== request_args = {
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 test_conversation(): service_context = ServiceContext( RP_KEYJAR, { "client_preferences": { "application_type": "web", "application_name": "rphandler", "contacts": ["*****@*****.**"], "response_types": ["code"], "scope": ["openid", "profile", "email", "address", "phone"], "token_endpoint_auth_method": "client_secret_basic", }, "redirect_uris": ["{}/authz_cb".format(RP_BASEURL)], "jwks_uri": "{}/static/jwks.json".format(RP_BASEURL) }) service_spec = DEFAULT_SERVICES.copy() service_spec['WebFinger'] = {'class': WebFinger} service = init_services(service_spec, state_db=InMemoryStateDataBase(), service_context=service_context) assert set(service.keys()) == { 'accesstoken', 'authorization', 'webfinger', 'registration', 'refresh_token', 'userinfo', 'provider_info' } service_context.service = service # ======================== WebFinger ======================== info = service['webfinger'].get_request_parameters( request_args={'resource': '*****@*****.**'}) assert info[ 'url'] == 'https://example.org/.well-known/webfinger?rel=http' \ '%3A%2F' \ '%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer' \ '&resource' \ '=acct%3Afoobar%40example.org' webfinger_response = json.dumps({ "subject": "acct:[email protected]", "links": [{ "rel": "http://openid.net/specs/connect/1.0/issuer", "href": "https://example.org/op" }], "expires": "2018-02-04T11:08:41Z" }) response = service['webfinger'].parse_response(webfinger_response) assert isinstance(response, JRD) assert set(response.keys()) == {'subject', 'links', 'expires'} assert response['links'] == [ Link(rel='http://openid.net/specs/connect/1.0/issuer', href='https://example.org/op') ] service['webfinger'].update_service_context(resp=response) assert service_context.issuer == OP_BASEURL # =================== Provider info discovery ==================== info = service['provider_info'].get_request_parameters() assert info[ 'url'] == 'https://example.org/op/.well-known/openid' \ '-configuration' provider_info_response = json.dumps({ "version": "3.0", "token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt" ], "claims_parameter_supported": True, "request_parameter_supported": True, "request_uri_parameter_supported": True, "require_request_uri_registration": True, "grant_types_supported": [ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "refresh_token" ], "response_types_supported": [ "code", "id_token", "id_token token", "code id_token", "code token", "code id_token token" ], "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["public", "pairwise"], "claim_types_supported": ["normal", "aggregated", "distributed"], "claims_supported": [ "birthdate", "address", "nickname", "picture", "website", "email", "gender", "sub", "phone_number_verified", "given_name", "profile", "phone_number", "updated_at", "middle_name", "name", "locale", "email_verified", "preferred_username", "zoneinfo", "family_name" ], "scopes_supported": [ "openid", "profile", "email", "address", "phone", "offline_access", "openid" ], "userinfo_signing_alg_values_supported": [ "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512", "none" ], "id_token_signing_alg_values_supported": [ "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512", "none" ], "request_object_signing_alg_values_supported": [ "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512", "none" ], "token_endpoint_auth_signing_alg_values_supported": [ "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "HS256", "HS384", "HS512", "PS256", "PS384", "PS512" ], "userinfo_encryption_alg_values_supported": [ "RSA1_5", "RSA-OAEP", "RSA-OAEP-256", "A128KW", "A192KW", "A256KW", "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW" ], "id_token_encryption_alg_values_supported": [ "RSA1_5", "RSA-OAEP", "RSA-OAEP-256", "A128KW", "A192KW", "A256KW", "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW" ], "request_object_encryption_alg_values_supported": [ "RSA1_5", "RSA-OAEP", "RSA-OAEP-256", "A128KW", "A192KW", "A256KW", "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW" ], "userinfo_encryption_enc_values_supported": [ "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128GCM", "A192GCM", "A256GCM" ], "id_token_encryption_enc_values_supported": [ "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128GCM", "A192GCM", "A256GCM" ], "request_object_encryption_enc_values_supported": [ "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128GCM", "A192GCM", "A256GCM" ], "acr_values_supported": ["PASSWORD"], "issuer": OP_BASEURL, "jwks_uri": "{}/static/jwks_tE2iLbOAqXhe8bqh.json".format(OP_BASEURL), "authorization_endpoint": "{}/authorization".format(OP_BASEURL), "token_endpoint": "{}/token".format(OP_BASEURL), "userinfo_endpoint": "{}/userinfo".format(OP_BASEURL), "registration_endpoint": "{}/registration".format(OP_BASEURL), "end_session_endpoint": "{}/end_session".format(OP_BASEURL) }) resp = service['provider_info'].parse_response(provider_info_response) assert isinstance(resp, ProviderConfigurationResponse) service['provider_info'].update_service_context(resp) assert service_context.provider_info['issuer'] == OP_BASEURL assert service_context.provider_info[ 'authorization_endpoint'] == \ 'https://example.org/op/authorization' assert service_context.provider_info[ 'registration_endpoint'] == 'https://example.org/op/registration' # =================== Client registration ==================== info = service['registration'].get_request_parameters() assert info['url'] == 'https://example.org/op/registration' _body = json.loads(info['body']) assert _body == { "application_type": "web", "response_types": ["code"], "contacts": ["*****@*****.**"], "jwks_uri": "https://example.com/rp/static/jwks.json", "redirect_uris": ["{}/authz_cb".format(RP_BASEURL)], 'token_endpoint_auth_method': 'client_secret_basic', "grant_types": ["authorization_code"] } assert info['headers'] == {'Content-Type': 'application/json'} now = int(time.time()) op_client_registration_response = json.dumps({ "client_id": "zls2qhN1jO6A", "client_secret": "c8434f28cf9375d9a7", "registration_access_token": "NdGrGR7LCuzNtixvBFnDphGXv7wRcONn", "registration_client_uri": "{}/registration?client_id=zls2qhN1jO6A".format(RP_BASEURL), "client_secret_expires_at": now + 3600, "client_id_issued_at": now, "application_type": "web", "response_types": ["code"], "contacts": ["*****@*****.**"], "redirect_uris": ["{}/authz_cb".format(RP_BASEURL)], "token_endpoint_auth_method": "client_secret_basic", "grant_types": ["authorization_code"] }) response = service['registration'].parse_response( op_client_registration_response) service['registration'].update_service_context(response) assert service_context.client_id == 'zls2qhN1jO6A' assert service_context.client_secret == 'c8434f28cf9375d9a7' assert isinstance(service_context.registration_response, RegistrationResponse) assert set(service_context.registration_response.keys()) == { 'client_secret_expires_at', 'contacts', 'client_id', 'token_endpoint_auth_method', 'redirect_uris', 'response_types', 'client_id_issued_at', 'client_secret', 'application_type', 'registration_client_uri', 'registration_access_token', 'grant_types' } # =================== Authorization ==================== STATE = 'Oh3w3gKlvoM2ehFqlxI3HIK5' NONCE = 'UvudLKz287YByZdsY3AJoPAlEXQkJ0dK' info = service['authorization'].get_request_parameters(request_args={ 'state': STATE, 'nonce': NONCE }) p = urlparse(info['url']) _query = parse_qs(p.query) assert set(_query.keys()) == { 'state', 'nonce', 'response_type', 'scope', 'client_id', 'redirect_uri' } assert _query['scope'] == ['openid'] assert _query['nonce'] == [NONCE] assert _query['state'] == [STATE] op_authz_resp = { 'state': STATE, 'scope': 'openid', 'code': 'Z0FBQUFBQmFkdFFjUVpFWE81SHU5N1N4N01', 'iss': OP_BASEURL, 'client_id': 'zls2qhN1jO6A' } _authz_rep = AuthorizationResponse(**op_authz_resp) _resp = service['authorization'].parse_response(_authz_rep.to_urlencoded()) service['authorization'].update_service_context(_resp, key=STATE) _item = service['authorization'].get_item(AuthorizationResponse, 'auth_response', STATE) assert _item['code'] == 'Z0FBQUFBQmFkdFFjUVpFWE81SHU5N1N4N01' # =================== Access token ==================== request_args = { 'state': STATE, 'redirect_uri': service_context.redirect_uris[0] } info = service['accesstoken'].get_request_parameters( request_args=request_args) assert info['url'] == 'https://example.org/op/token' _qp = parse_qs(info['body']) assert _qp == { 'grant_type': ['authorization_code'], 'redirect_uri': ['https://example.com/rp/authz_cb'], 'client_id': ['zls2qhN1jO6A'], 'state': ['Oh3w3gKlvoM2ehFqlxI3HIK5'], 'code': ['Z0FBQUFBQmFkdFFjUVpFWE81SHU5N1N4N01'] } assert info['headers'] == { 'Authorization': 'Basic ' 'emxzMnFoTjFqTzZBOmM4NDM0ZjI4Y2Y5Mzc1ZDlhNw==', 'Content-Type': 'application/x-www-form-urlencoded' } # create the IdToken _jwt = JWT(OP_KEYJAR, OP_BASEURL, lifetime=3600, sign=True, sign_alg='RS256') payload = { 'sub': '1b2fc9341a16ae4e30082965d537', 'acr': 'PASSWORD', 'auth_time': 1517736988, 'nonce': NONCE } _jws = _jwt.pack(payload=payload, recv='zls2qhN1jO6A') _resp = { "state": "Oh3w3gKlvoM2ehFqlxI3HIK5", "scope": "openid", "access_token": "Z0FBQUFBQmFkdFF", "token_type": "Bearer", 'expires_in': 600, "id_token": _jws } service_context.issuer = OP_BASEURL _resp = service['accesstoken'].parse_response(json.dumps(_resp), state=STATE) assert isinstance(_resp, AccessTokenResponse) assert set(_resp['__verified_id_token'].keys()) == { 'iss', 'nonce', 'acr', 'auth_time', 'aud', 'iat', 'exp', 'sub' } service['accesstoken'].update_service_context(_resp, key=STATE) _item = service['authorization'].get_item(AccessTokenResponse, 'token_response', STATE) assert set(_item.keys()) == { 'state', 'scope', 'access_token', 'token_type', 'id_token', '__verified_id_token', 'expires_in', '__expires_at' } assert _item['token_type'] == 'Bearer' assert _item['access_token'] == 'Z0FBQUFBQmFkdFF' # =================== User info ==================== info = service['userinfo'].get_request_parameters(state=STATE) assert info['url'] == 'https://example.org/op/userinfo' assert info['headers'] == {'Authorization': 'Bearer Z0FBQUFBQmFkdFF'} op_resp = {"sub": "1b2fc9341a16ae4e30082965d537"} _resp = service['userinfo'].parse_response(json.dumps(op_resp), state=STATE) service['userinfo'].update_service_context(_resp, key=STATE) assert isinstance(_resp, OpenIDSchema) assert _resp.to_dict() == {'sub': '1b2fc9341a16ae4e30082965d537'} _item = service['authorization'].get_item(OpenIDSchema, 'user_info', STATE) assert _item.to_dict() == {'sub': '1b2fc9341a16ae4e30082965d537'}