def test_access_token_srv_conf(): client_config = { 'client_id': 'client_id', 'client_secret': 'a longesh password', 'redirect_uris': ['https://example.com/cli/authz_cb'] } entity = Entity(config=client_config) token_service = entity.client_get("service", 'accesstoken') _state_interface = token_service.client_get("service_context").state _state_val = _state_interface.create_state( token_service.client_get("service_context").issuer) auth_request = AuthorizationRequest( redirect_uri='https://example.com/cli/authz_cb', state=_state_val) _state_interface.store_item(auth_request, "auth_request", _state_val) auth_response = AuthorizationResponse(code='access_code') _state_interface.store_item(auth_response, "auth_response", _state_val) req_args = { 'redirect_uri': 'https://example.com/cli/authz_cb', 'code': 'access_code' } token_service.endpoint = 'https://example.com/authorize' _info = token_service.get_request_parameters(request_args=req_args, state=_state_val) assert _info msg = AccessTokenRequest().from_urlencoded(_info['body']) # client_secret_basic by default assert 'client_secret' not in msg
class TestPKCE384: @pytest.fixture(autouse=True) def create_client(self): config = { 'client_id': 'client_id', 'client_secret': 'a longesh password', 'redirect_uris': ['https://example.com/cli/authz_cb'], 'add_ons': { "pkce": { "function": "oidcrp.oauth2.add_on.pkce.add_support", "kwargs": { "code_challenge_length": 128, "code_challenge_method": "S384" } } } } self.entity = Entity(keyjar=CLI_KEY, config=config, services=DEFAULT_OAUTH2_SERVICES) if 'add_ons' in config: do_add_ons(config['add_ons'], self.entity.client_get("services")) def test_add_code_challenge_spec_values(self): auth_serv = self.entity.client_get("service", "authorization") request_args, _ = add_code_challenge({'state': 'state'}, auth_serv) assert set(request_args.keys()) == { 'code_challenge', 'code_challenge_method', 'state' } assert request_args['code_challenge_method'] == 'S384' request_args = add_code_verifier({}, auth_serv, state='state') assert len(request_args['code_verifier']) == 128
def create_request(self): self._iss = ISS client_config = { "redirect_uris": ["https://example.com/cli/authz_cb"], "issuer": self._iss, "requests_dir": "requests", "base_url": "https://example.com/cli/", "client_preferences": { "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"] } } services = { 'registration': { 'class': 'oidcrp.oidc.registration.Registration' }, 'read_registration': { 'class': 'oidcrp.oidc.read_registration.RegistrationRead' } } self.entity = Entity(config=client_config, services=services) self.reg_service = self.entity.client_get("service", 'registration') self.read_service = self.entity.client_get("service", 'registration_read')
def create_service(self): client_config = { 'client_id': 'client_id', 'client_secret': 'a longesh password', 'redirect_uris': ['https://example.com/cli/authz_cb'] } entity = Entity(config=client_config) self.token_service = entity.client_get("service", "accesstoken") auth_request = AuthorizationRequest( redirect_uri='https://example.com/cli/authz_cb', state='state') auth_response = AuthorizationResponse(code='access_code') _state = self.token_service.client_get("service_context").state _state.store_item(auth_request, 'auth_request', 'state') _state.store_item(auth_response, 'auth_response', 'state')
def create_service(self): client_config = { 'client_id': 'client_id', 'client_secret': 'a longesh password', 'redirect_uris': ['https://example.com/cli/authz_cb'] } entity = Entity(config=client_config) self.refresh_service = entity.client_get("service", 'refresh_token') auth_response = AuthorizationResponse(code='access_code') token_response = AccessTokenResponse(access_token='bearer_token', refresh_token='refresh') _state = self.refresh_service.client_get("service_context").state _state.store_item(auth_response, 'auth_response', 'abcdef') _state.store_item(token_response, 'token_response', 'abcdef') self.refresh_service.endpoint = 'https://example.com/token'
def rp_service_setup(self): entity_id = 'https://foodle.uninett.no' entity = Entity(config={ 'issuer': 'https://op.ntnu.no', 'keys': { 'key_defs': KEY_DEFS } }) service_context = entity.get_service_context() http_cli = Publisher(os.path.join(BASE_PATH, 'base_data')) self.federation_entity = FederationEntity( entity_id, trusted_roots=ANCHOR, authority_hints=['https://ntnu.no'], httpd=http_cli, entity_type='openid_relying_party', opponent_entity_type='openid_provider', config={'keys': { 'key_defs': KEY_DEFS }}) # The test data collector self.federation_entity.collector = DummyCollector( trusted_roots=ANCHOR, httpd=http_cli, root_dir=os.path.join(BASE_PATH, 'base_data')) service_context.federation_entity = self.federation_entity service_context.redirect_uris = ['https://foodle.uninett.no/cb'] # Note that the keys used for OIDC base protocol communication are separate from those used # in the federation context # service_context.keyjar = init_key_jar(key_defs=KEY_DEFS, issuer_id=entity_id) service_context.client_preferences = { "grant_types": ['authorization_code', 'implicit', 'refresh_token'], "id_token_signed_response_alg": "ES256", "token_endpoint_auth_method": "client_secret_basic", "federation_type": ['automatic'] } self.service = { 'discovery': FedProviderInfoDiscovery(client_get=entity.client_get), 'registration': Registration(client_get=entity.client_get) }
def create_service(self): client_config = { 'client_id': 'client_id', 'client_secret': 'another password' } services = { 'token': { 'class': 'oidcrp.oauth2.client_credentials.cc_access_token.CCAccessToken' }, 'refresh_token': { 'class': 'oidcrp.oauth2.client_credentials.cc_refresh_access_token' '.CCRefreshAccessToken' } } self.entity = Entity(config=client_config, services=services) self.entity.client_get("service",'accesstoken').endpoint = 'https://example.com/token' self.entity.client_get("service",'refresh_token').endpoint = 'https://example.com/token'
def create_client(self): config = { 'client_id': 'client_id', 'client_secret': 'a longesh password', 'redirect_uris': ['https://example.com/cli/authz_cb'], 'add_ons': { "pkce": { "function": "oidcrp.oauth2.add_on.pkce.add_support", "kwargs": { "code_challenge_length": 128, "code_challenge_method": "S384" } } } } self.entity = Entity(keyjar=CLI_KEY, config=config, services=DEFAULT_OAUTH2_SERVICES) if 'add_ons' in config: do_add_ons(config['add_ons'], self.entity.client_get("services"))
def create_service(self): self._iss = 'https://example.com/as' client_config = { 'client_id': 'client_id', 'client_secret': 'a longesh password', "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': ['https://example.com/cli/authz_cb'], 'issuer': self._iss } entity = Entity(config=client_config) self.auth_service = entity.client_get("service", 'provider_info') self.auth_service.endpoint = '{}/.well-known/openid-configuration'.format( self._iss)
def __init__(self, client_authn_factory=None, keyjar=None, verify_ssl=True, config=None, httplib=None, services=None, jwks_uri='', httpc_params=None): """ :param client_authn_factory: Factory that this client can use to initiate a client authentication class. :param keyjar: A py:class:`oidcmsg.key_jar.KeyJar` instance :param config: Configuration information passed on to the :py:class:`oidcrp.service_context.ServiceContext` initialization :param httplib: A HTTP client to use :param services: A list of service definitions :param jwks_uri: A jwks_uri :param httpc_params: HTTP request arguments :return: Client instance """ Entity.__init__(self, client_authn_factory=client_authn_factory, keyjar=keyjar, config=config, services=services, jwks_uri=jwks_uri, httpc_params=httpc_params) self.http = httplib or HTTPLib(httpc_params) if 'add_ons' in config: do_add_ons(config['add_ons'], self._service) # just ignore verify_ssl until it goes away self.verify_ssl = self.httpc_params.get("verify", True)
def test_conversation(): config = { "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_OIDC_SERVICES.copy() service_spec['WebFinger'] = {'class': WebFinger} entity = Entity(config=config, services=service_spec, keyjar=RP_KEYJAR) assert set(entity.client_get("services").keys()) == { 'accesstoken', 'authorization', 'webfinger', 'registration', 'refresh_token', 'userinfo', 'provider_info' } service_context = entity.client_get("service_context") # ======================== WebFinger ======================== webfinger_service = entity.client_get("service", 'webfinger') info = webfinger_service.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 = webfinger_service.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') ] webfinger_service.update_service_context(resp=response) entity.client_get("service_context").issuer = OP_BASEURL # =================== Provider info discovery ==================== provider_info_service = entity.client_get("service", 'provider_info') info = provider_info_service.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 = provider_info_service.parse_response(provider_info_response) assert isinstance(resp, ProviderConfigurationResponse) provider_info_service.update_service_context(resp) _pi = entity.client_get("service_context").provider_info assert _pi['issuer'] == OP_BASEURL assert _pi[ 'authorization_endpoint'] == 'https://example.org/op/authorization' assert _pi[ 'registration_endpoint'] == 'https://example.org/op/registration' # =================== Client registration ==================== registration_service = entity.client_get("service", 'registration') info = registration_service.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 = registration_service.parse_response( op_client_registration_response) registration_service.update_service_context(response) assert service_context.client_id == 'zls2qhN1jO6A' assert service_context.client_secret == 'c8434f28cf9375d9a7' 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' auth_service = entity.client_get("service", 'authorization') _state_interface = service_context.state info = auth_service.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 profile email address phone'] 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 = auth_service.parse_response(_authz_rep.to_urlencoded()) auth_service.update_service_context(_resp, key=STATE) _item = _state_interface.get_item(AuthorizationResponse, 'auth_response', STATE) assert _item['code'] == 'Z0FBQUFBQmFkdFFjUVpFWE81SHU5N1N4N01' # =================== Access token ==================== token_service = entity.client_get("service", 'accesstoken') request_args = { 'state': STATE, 'redirect_uri': service_context.redirect_uris[0] } info = token_service.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 = token_service.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' } token_service.update_service_context(_resp, key=STATE) _item = _state_interface.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 ==================== userinfo_service = entity.client_get("service", 'userinfo') info = userinfo_service.get_request_parameters(state=STATE) assert info['url'] == 'https://example.org/op/userinfo' assert info['headers'] == {'Authorization': 'Bearer Z0FBQUFBQmFkdFF'} op_resp = {"sub": "1b2fc9341a16ae4e30082965d537"} _resp = userinfo_service.parse_response(json.dumps(op_resp), state=STATE) userinfo_service.update_service_context(_resp, key=STATE) assert isinstance(_resp, OpenIDSchema) assert _resp.to_dict() == {'sub': '1b2fc9341a16ae4e30082965d537'} _item = _state_interface.get_item(OpenIDSchema, 'user_info', STATE) assert _item.to_dict() == {'sub': '1b2fc9341a16ae4e30082965d537'}
class TestRegistrationRead(object): @pytest.fixture(autouse=True) def create_request(self): self._iss = ISS client_config = { "redirect_uris": ["https://example.com/cli/authz_cb"], "issuer": self._iss, "requests_dir": "requests", "base_url": "https://example.com/cli/", "client_preferences": { "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"] } } services = { 'registration': { 'class': 'oidcrp.oidc.registration.Registration' }, 'read_registration': { 'class': 'oidcrp.oidc.read_registration.RegistrationRead' } } self.entity = Entity(config=client_config, services=services) self.reg_service = self.entity.client_get("service", 'registration') self.read_service = self.entity.client_get("service", 'registration_read') def test_construct(self): self.reg_service.endpoint = "{}/registration".format(ISS) _param = self.reg_service.get_request_parameters() now = int(time.time()) _client_registration_response = json.dumps({ "client_id": "zls2qhN1jO6A", "client_secret": "c8434f28cf9375d9a7", "registration_access_token": "NdGrGR7LCuzNtixvBFnDphGXv7wRcONn", "registration_client_uri": "{}/registration_api?client_id=zls2qhN1jO6A".format(ISS), "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"] }) with responses.RequestsMock() as rsps: rsps.add(_param["method"], _param["url"], body=_client_registration_response, status=200) _resp = requests.request(_param["method"], _param["url"], data=as_bytes(_param["body"]), headers=_param["headers"], verify=False) resp = self.reg_service.parse_response(_resp.text) self.reg_service.update_service_context(resp) assert resp _read_param = self.read_service.get_request_parameters() with responses.RequestsMock() as rsps: rsps.add(_param["method"], _param["url"], body=_client_registration_response, adding_headers={"Content-Type": "application/json"}, status=200) _resp = requests.request(_param["method"], _param["url"], headers=_param["headers"], verify=False) read_resp = self.reg_service.parse_response(_resp.text) assert isinstance(read_resp, RegistrationResponse)
from urllib.parse import parse_qs, unquote_plus, urlsplit from oidcrp.entity import Entity import pytest from oidcmsg.exception import MissingRequiredAttribute from oidcmsg.oidc import JRD, Link from oidcrp.oidc import OIC_ISSUER from oidcrp.oidc.webfinger import WebFinger from oidcrp.service_context import ServiceContext __author__ = 'Roland Hedberg' SERVICE_CONTEXT = ServiceContext() ENTITY = Entity(config={}) def test_query(): rel = 'http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer' pattern = 'https://{}/.well-known/webfinger?rel={}&resource={}' example_oidc = { 'example.com': ('example.com', rel, 'acct%3Aexample.com'), '*****@*****.**': ('example.com', rel, 'acct%3Ajoe%40example.com'), 'example.com/joe': ('example.com', rel, 'https%3A%2F%2Fexample.com%2Fjoe'), 'example.com:8080': ('example.com:8080', rel, 'https%3A%2F%2Fexample.com%3A8080'), '*****@*****.**': ('example.com', rel, 'acct%3AJane.Doe%40example.com'), '[email protected]:8080': ('[email protected]:8080', rel,
class TestRP(): @pytest.fixture(autouse=True) def create_service(self): client_config = { 'client_id': 'client_id', 'client_secret': 'another password' } services = { 'token': { 'class': 'oidcrp.oauth2.client_credentials.cc_access_token.CCAccessToken' }, 'refresh_token': { 'class': 'oidcrp.oauth2.client_credentials.cc_refresh_access_token' '.CCRefreshAccessToken' } } self.entity = Entity(config=client_config, services=services) self.entity.client_get("service",'accesstoken').endpoint = 'https://example.com/token' self.entity.client_get("service",'refresh_token').endpoint = 'https://example.com/token' def test_token_get_request(self): request_args = {'grant_type': 'client_credentials'} _srv = self.entity.client_get("service",'accesstoken') _info = _srv.get_request_parameters(request_args=request_args) assert _info['method'] == 'POST' assert _info['url'] == 'https://example.com/token' assert _info['body'] == 'grant_type=client_credentials' assert _info['headers'] == { 'Authorization': 'Basic Y2xpZW50X2lkOmFub3RoZXIrcGFzc3dvcmQ=', 'Content-Type': 'application/x-www-form-urlencoded' } def test_token_parse_response(self): request_args = {'grant_type': 'client_credentials'} _srv = self.entity.client_get("service",'accesstoken') _request_info = _srv.get_request_parameters(request_args=request_args) response = AccessTokenResponse(**{ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value" }) _response = _srv.parse_response(response.to_json(), sformat="json") # since no state attribute is involved, a key is minted _key = rndstr(16) _srv.update_service_context(_response, key=_key) info = _srv.client_get("service_context").state.get_item(AccessTokenResponse, 'token_response', _key) assert '__expires_at' in info def test_refresh_token_get_request(self): _srv = self.entity.client_get("service",'accesstoken') _srv.update_service_context({ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value" }) _srv = self.entity.client_get("service",'refresh_token') _id = rndstr(16) _info = _srv.get_request_parameters(state_id=_id) assert _info['method'] == 'POST' assert _info['url'] == 'https://example.com/token' assert _info[ 'body'] == 'grant_type=refresh_token' assert _info['headers'] == { 'Authorization': 'Bearer tGzv3JOkF0XG5Qx2TlKWIA', 'Content-Type': 'application/x-www-form-urlencoded' } def test_refresh_token_parse_response(self): request_args = {'grant_type': 'client_credentials'} _srv = self.entity.client_get("service",'accesstoken') _request_info = _srv.get_request_parameters(request_args=request_args) response = AccessTokenResponse(**{ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value" }) _response = _srv.parse_response(response.to_json(), sformat="json") # since no state attribute is involved, a key is minted _key = rndstr(16) _srv.update_service_context(_response, key=_key) info = _srv.client_get("service_context").state.get_item(AccessTokenResponse, 'token_response', _key) assert '__expires_at' in info # Move from token to refresh token service _srv = self.entity.client_get("service",'refresh_token') _request_info = _srv.get_request_parameters(request_args=request_args, state=_key) refresh_response = AccessTokenResponse(**{ "access_token": 'wy4R01DmMoB5xkI65nNkVv1l', "token_type": "example", "expires_in": 3600, "refresh_token": 'lhNX9LSG8w1QuD6tSgc6CPfJ', }) _response = _srv.parse_response(refresh_response.to_json(), sformat="json") _srv.update_service_context(_response, key=_key) info = _srv.client_get("service_context").state.get_item(AccessTokenResponse, 'token_response', _key) assert '__expires_at' in info def test_2nd_refresh_token_parse_response(self): request_args = {'grant_type': 'client_credentials'} _srv = self.entity.client_get("service",'accesstoken') _request_info = _srv.get_request_parameters(request_args=request_args) response = AccessTokenResponse(**{ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "example", "expires_in": 3600, "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter": "example_value" }) _response = _srv.parse_response(response.to_json(), sformat="json") # since no state attribute is involved, a key is minted _key = rndstr(16) _srv.update_service_context(_response, key=_key) info = _srv.client_get("service_context").state.get_item(AccessTokenResponse, 'token_response', _key) assert '__expires_at' in info # Move from token to refresh token service _srv = self.entity.client_get("service",'refresh_token') _request_info = _srv.get_request_parameters(request_args=request_args, state=_key) refresh_response = AccessTokenResponse(**{ "access_token": 'wy4R01DmMoB5xkI65nNkVv1l', "token_type": "example", "expires_in": 3600, "refresh_token": 'lhNX9LSG8w1QuD6tSgc6CPfJ', }) _response = _srv.parse_response(refresh_response.to_json(), sformat="json") _srv.update_service_context(_response, key=_key) info = _srv.client_get("service_context").state.get_item(AccessTokenResponse, 'token_response', _key) assert '__expires_at' in info _request_info = _srv.get_request_parameters(request_args=request_args, state=_key) assert _request_info['headers'] == { 'Authorization': 'Bearer {}'.format(refresh_response["refresh_token"]), 'Content-Type': 'application/x-www-form-urlencoded' }
def create_setup(self): # First the RP entity = Entity( config={ 'behaviour': { 'federation_types_supported': ['automatic'] }, 'issuer': "https://op.ntnu.no", 'keys': { 'key_defs': KEYSPEC } }) service_context = entity.get_service_context() self.rp_federation_entity = FederationEntity( entity_id=RP_ENTITY_ID, trusted_roots=ANCHOR, authority_hints=['https://ntnu.no'], entity_type='openid_relying_party', opponent_entity_type='openid_provider') self.rp_federation_entity.keyjar.import_jwks(read_info( os.path.join(ROOT_DIR, 'foodle.uninett.no'), 'foodle.uninett.no', 'jwks'), issuer_id=RP_ENTITY_ID) self.rp_federation_entity.collector = DummyCollector( trusted_roots=ANCHOR, root_dir=ROOT_DIR) # add the federation part to the service context service_context.federation_entity = self.rp_federation_entity # The RP has/supports 3 services self.discovery_service = FedProviderInfoDiscovery(entity.client_get) self.registration_service = RPRegistration(entity.client_get) # self.authorization_service = FedAuthorization(entity.client_get) # and now for the OP op_entity_id = "https://op.ntnu.no" conf = { "issuer": op_entity_id, "password": "******", "token_handler_args": { "jwks_def": { "private_path": "private/token_jwks.json", "read_only": False, "key_defs": [ { "type": "oct", "bytes": 24, "use": ["enc"], "kid": "code" }, { "type": "oct", "bytes": 24, "use": ["enc"], "kid": "refresh" }, ], }, "code": { "lifetime": 600 }, "token": { "class": "oidcop.token.jwt_token.JWTToken", "kwargs": { "lifetime": 3600, "add_claims": [ "email", "email_verified", "phone_number", "phone_number_verified", ], "add_claim_by_scope": True, "aud": ["https://example.org/appl"] }, }, "refresh": { "lifetime": 86400 }, }, "claims_interface": { "class": "oidcop.session.claims.ClaimsInterface", "kwargs": {} }, "verify_ssl": False, "capabilities": CAPABILITIES, "keys": { "uri_path": "static/jwks.json", "key_defs": KEYSPEC }, "id_token": { "class": IDToken, "kwargs": { "default_claims": { "email": { "essential": True }, "email_verified": { "essential": True }, } }, }, "endpoint": { "provider_config": { "path": ".well-known/openid-configuration", "class": ProviderConfiguration, "kwargs": {}, }, "registration": { "path": "registration", "class": OPRegistration, "kwargs": {}, }, "authorization": { "path": "authorization", "class": Authorization, "kwargs": { "response_types_supported": [" ".join(x) for x in RESPONSE_TYPES_SUPPORTED], "response_modes_supported": ["query", "fragment", "form_post"], "claims_parameter_supported": True, "request_parameter_supported": True, "request_uri_parameter_supported": True, }, }, "pushed_authorization": { "path": "pushed_authorization", "class": PushedAuthorization, "kwargs": { "client_authn_method": [ "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt", ] }, }, }, "authentication": { "anon": { "acr": "http://www.swamid.se/policy/assurance/al1", "class": "oidcop.user_authn.user.NoAuthn", "kwargs": { "user": "******" }, } }, "template_dir": "template", "cookie_handler": { "class": CookieHandler, "kwargs": { "keys": { "key_defs": COOKIE_KEYDEFS }, "name": { "session": "oidc_op", "register": "oidc_op_reg", "session_management": "oidc_op_sman" } }, }, 'add_on': { "automatic_registration": { "function": "fedservice.op.add_on.automatic_registration.add_support", "kwargs": { "new_id": False, # default False "where": ["pushed_authorization"] } } } } server = Server(conf) endpoint_context = server.get_endpoint_context() _clients = yaml.safe_load(io.StringIO(client_yaml)) # endpoint_context.cdb = _clients["oidc_clients"] endpoint_context.keyjar.import_jwks( endpoint_context.keyjar.export_jwks(True, ""), conf["issuer"]) self.pushed_authorization_endpoint = server.server_get( "endpoint", "pushed_authorization") self.authorization_endpoint = server.server_get( "endpoint", "authorization") self.registration_endpoint = server.server_get("endpoint", "registration") federation_entity = FederationEntity( op_entity_id, trusted_roots=ANCHOR, authority_hints=['https://ntnu.no'], entity_type='openid_relying_party', httpd=Publisher(ROOT_DIR), opponent_entity_type='openid_relying_party') # federation_entity.keyjar.import_jwks( # read_info(os.path.join(ROOT_DIR, 'op.ntnu.no'), # 'op.ntnu.no', 'jwks'), # issuer_id=op_entity_id) federation_entity.collector = DummyCollector(httpd=Publisher(ROOT_DIR), trusted_roots=ANCHOR, root_dir=ROOT_DIR) self.authorization_endpoint.server_get( "endpoint_context").federation_entity = federation_entity
class TestPKCE256: @pytest.fixture(autouse=True) def create_client(self): config = { 'client_id': 'client_id', 'client_secret': 'a longesh password', 'redirect_uris': ['https://example.com/cli/authz_cb'], 'behaviour': { 'response_types': ['code'] }, 'add_ons': { "pkce": { "function": "oidcrp.oauth2.add_on.pkce.add_support", "kwargs": { "code_challenge_length": 64, "code_challenge_method": "S256" } } } } self.entity = Entity(keyjar=CLI_KEY, config=config, services=DEFAULT_OAUTH2_SERVICES) if 'add_ons' in config: do_add_ons(config['add_ons'], self.entity.client_get("services")) def test_add_code_challenge_default_values(self): auth_serv = self.entity.client_get("service", "authorization") _state_key = self.entity.client_get( "service_context").state.create_state(iss="Issuer") request_args, _ = add_code_challenge({'state': _state_key}, auth_serv) # default values are length:64 method:S256 assert set(request_args.keys()) == { 'code_challenge', 'code_challenge_method', 'state' } assert request_args['code_challenge_method'] == 'S256' request_args = add_code_verifier({}, auth_serv, state=_state_key) assert len(request_args['code_verifier']) == 64 def test_authorization_and_pkce(self): auth_serv = self.entity.client_get("service", "authorization") _state = self.entity.client_get("service_context").state.create_state( iss='Issuer') request = auth_serv.construct_request({ "state": _state, "response_type": "code" }) assert set(request.keys()) == { 'client_id', 'code_challenge', 'code_challenge_method', 'state', 'redirect_uri', 'response_type' } def test_access_token_and_pkce(self): authz_service = self.entity.client_get("service", "authorization") request = authz_service.construct_request({ "state": 'state', "response_type": "code" }) _state = request['state'] auth_response = AuthorizationResponse(code='access code') self.entity.client_get("service_context").state.store_item( auth_response, 'auth_response', _state) token_service = self.entity.client_get("service", "accesstoken") request = token_service.construct_request(state=_state) assert set(request.keys()) == { 'client_id', 'redirect_uri', 'grant_type', 'client_secret', 'code_verifier', 'code', 'state' }
def create_endpoint(self): # First the RP entity = Entity( config={ 'behaviour': { 'federation_types_supported': ['explicit'] }, 'issuer': "https://op.ntnu.no", 'keys': { 'key_defs': KEYSPEC } }) service_context = entity.client_get("service_context") # the federation part of the RP self.rp_federation_entity = FederationEntity( entity_id=ENTITY_ID, trusted_roots=ANCHOR, authority_hints=['https://ntnu.no'], entity_type='openid_relying_party', opponent_entity_type='openid_provider') self.rp_federation_entity.collector = DummyCollector( trusted_roots=ANCHOR, root_dir=ROOT_DIR) self.rp_federation_entity.keyjar.import_jwks(read_info( os.path.join(ROOT_DIR, 'foodle.uninett.no'), 'foodle.uninett.no', 'jwks'), issuer_id=ENTITY_ID) # add the federation part to the service context service_context.federation_entity = self.rp_federation_entity # The RP has/supports 3 services self.service = { 'discovery': FedProviderInfoDiscovery(entity.client_get), 'registration': Registration(entity.client_get), 'authorization': FedAuthorization(entity.client_get), } # and now for the OP op_entity_id = "https://op.ntnu.no" conf = { "issuer": op_entity_id, "password": "******", "token_expires_in": 600, "grant_expires_in": 300, "refresh_token_expires_in": 86400, "httpc_param": { 'verify': False, "timeout": 2 }, "claims_interface": { "class": "oidcop.session.claims.ClaimsInterface", "kwargs": {} }, "cookie_handler": { "class": CookieHandler, "kwargs": { "keys": { "key_defs": COOKIE_KEYDEFS }, "name": { "session": "oidc_op", "register": "oidc_op_reg", "session_management": "oidc_op_sman" } }, }, "endpoint": { 'provider_info': { 'path': '.well-known/openid-federation', 'class': provider_config.ProviderConfiguration, 'kwargs': { 'client_authn_method': None } }, 'registration': { 'path': 'fed_registration', 'class': registration.Registration, 'kwargs': { 'client_authn_method': None } }, 'authorization': { 'path': 'authorization', 'class': authorization.Authorization, 'kwargs': { "response_modes_supported": ['query', 'fragment', 'form_post'], "claims_parameter_supported": True, "request_parameter_supported": True, "request_uri_parameter_supported": True, "client_authn_method": ['request_param'] } } }, "keys": { "private_path": "own/jwks.json", "uri_path": "static/jwks.json", "key_defs": KEYSPEC }, "authentication": { "anon": { 'acr': UNSPECIFIED, "class": NoAuthn, "kwargs": { "user": "******" } } }, 'template_dir': 'template' } server = Server(conf) self.registration_endpoint = server.server_get("endpoint", "registration") self.authorization_endpoint = server.server_get( "endpoint", "authorization") self.provider_endpoint = server.server_get("endpoint", "provider_config") # === Federation stuff ======= federation_entity = FederationEntity( op_entity_id, trusted_roots=ANCHOR, authority_hints=['https://ntnu.no'], entity_type='openid_relying_party', httpd=Publisher(ROOT_DIR), opponent_entity_type='openid_relying_party') federation_entity.keyjar.import_jwks(read_info( os.path.join(ROOT_DIR, 'op.ntnu.no'), 'op.ntnu.no', 'jwks'), issuer_id=op_entity_id) federation_entity.collector = DummyCollector(httpd=Publisher(ROOT_DIR), trusted_roots=ANCHOR, root_dir=ROOT_DIR) self.registration_endpoint.server_get( "endpoint_context").federation_entity = federation_entity
def entity(): return Entity(config=CLIENT_CONF)
def create_service(self): self.entity = Entity(config=CLIENT_CONF) self.auth_service = self.entity.client_get("service", 'authorization')
class TestAuthorization(object): @pytest.fixture(autouse=True) def create_service(self): self.entity = Entity(config=CLIENT_CONF) self.auth_service = self.entity.client_get("service", 'authorization') def test_construct(self): req_args = {'foo': 'bar'} _req = self.auth_service.construct(request_args=req_args, state='state') assert isinstance(_req, AuthorizationRequest) assert set( _req.keys()) == {'client_id', 'redirect_uri', 'foo', 'state'} _context = self.entity.client_get("service_context") assert _context.state.get_state('state') _item = _context.state.get_item(AuthorizationRequest, 'auth_request', 'state') assert _item.to_dict() == { 'foo': 'bar', 'redirect_uri': 'https://example.com/cli/authz_cb', 'state': 'state', 'client_id': 'client_id' } def test_get_request_parameters(self): req_args = {'response_type': 'code'} self.auth_service.endpoint = 'https://example.com/authorize' _info = self.auth_service.get_request_parameters(request_args=req_args, state='state') assert set(_info.keys()) == {'url', 'method', 'request'} msg = AuthorizationRequest().from_urlencoded( self.auth_service.get_urlinfo(_info['url'])) assert msg.to_dict() == { 'client_id': 'client_id', 'redirect_uri': 'https://example.com/cli/authz_cb', 'response_type': 'code', 'state': 'state' } def test_request_init(self): req_args = {'response_type': 'code', 'state': "state"} self.auth_service.endpoint = 'https://example.com/authorize' _info = self.auth_service.get_request_parameters(request_args=req_args) assert set(_info.keys()) == {'url', 'method', 'request'} msg = AuthorizationRequest().from_urlencoded( self.auth_service.get_urlinfo(_info['url'])) assert msg.to_dict() == { 'client_id': 'client_id', 'redirect_uri': 'https://example.com/cli/authz_cb', 'response_type': 'code', 'state': 'state' } def test_response(self): _state = "today" req_args = {'response_type': 'code', 'state': _state} self.auth_service.endpoint = 'https://example.com/authorize' _info = self.auth_service.get_request_parameters(request_args=req_args) assert set(_info.keys()) == {'url', 'method', 'request'} msg = AuthorizationRequest().from_urlencoded( self.auth_service.get_urlinfo(_info['url'])) self.auth_service.client_get("service_context").state.store_item( msg, "auth_request", _state) resp1 = AuthorizationResponse(code="auth_grant", state=_state) response = self.auth_service.parse_response(resp1.to_urlencoded(), "urlencoded", state=_state) self.auth_service.update_service_context(response, key=_state) assert self.auth_service.client_get("service_context").state.get_state( _state)