def openid_conf(self): endpoint = {} for point, path in ENDPOINT.items(): endpoint[point] = "%s%s" % (self.host, path) signing_algs = list(jws.SIGNER_ALGS.keys()) resp = ProviderConfigurationResponse( issuer=self.name, scopes_supported=["openid", "profile", "email", "address"], identifiers_supported=["public", "PPID"], flows_supported=[ "code", "token", "code token", "id_token", "code id_token", "token id_token" ], subject_types_supported=["pairwise", "public"], response_types_supported=[ "code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token" ], jwks_uri="http://example.com/oidc/jwks", id_token_signing_alg_values_supported=signing_algs, grant_types_supported=["authorization_code", "implicit"], **endpoint) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response
def test_provider_configuration_endpoint(self, context, frontend): expected_capabilities = { "response_types_supported": ["code", "id_token", "code id_token token"], "jwks_uri": "{}/{}/jwks".format(BASE_URL, frontend.name), "authorization_endpoint": "{}/foo_backend/{}/authorization".format(BASE_URL, frontend.name), "token_endpoint": "{}/{}/token".format(BASE_URL, frontend.name), "userinfo_endpoint": "{}/{}/userinfo".format(BASE_URL, frontend.name), "id_token_signing_alg_values_supported": ["RS256"], "response_modes_supported": ["fragment", "query"], "subject_types_supported": ["pairwise"], "claim_types_supported": ["normal"], "claims_parameter_supported": True, "request_parameter_supported": False, "request_uri_parameter_supported": False, "scopes_supported": ["openid", "email"], "claims_supported": ["email"], "grant_types_supported": ["authorization_code", "implicit"], "issuer": BASE_URL, "require_request_uri_registration": True, "token_endpoint_auth_methods_supported": ["client_secret_basic"], "version": "3.0", } http_response = frontend.provider_config(context) provider_config = ProviderConfigurationResponse().deserialize(http_response.message, "json") assert provider_config.to_dict() == expected_capabilities
def provider_config(self, issuer, keys=True, endpoints=True): if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer url = OIDCONF_PATTERN % _issuer r = self.http_request(url) if r.status_code == 200: pcr = ProviderConfigurationResponse().from_json(r.text) else: raise Exception("Trying '%s', status %s" % (url, r.status_code)) if "issuer" in pcr: if pcr["issuer"].endswith("/"): _pcr_issuer = pcr["issuer"][:-1] else: _pcr_issuer = pcr["issuer"] try: assert _issuer == _pcr_issuer except AssertionError: raise Exception("provider info issuer mismatch '%s' != '%s'" % ( _issuer, _pcr_issuer)) if endpoints: for key, val in pcr.items(): if key.endswith("_endpoint"): setattr(self, key, val) if keys: self.keystore.load_keys(pcr, _issuer) return pcr
def test_provider_configuration_endpoint(self, context, frontend): expected_capabilities = { "response_types_supported": ["code", "id_token", "code id_token token"], "jwks_uri": "{}/{}/jwks".format(BASE_URL, frontend.name), "authorization_endpoint": "{}/foo_backend/{}/authorization".format(BASE_URL, frontend.name), "token_endpoint": "{}/{}/token".format(BASE_URL, frontend.name), "userinfo_endpoint": "{}/{}/userinfo".format(BASE_URL, frontend.name), "id_token_signing_alg_values_supported": ["RS256"], "response_modes_supported": ["fragment", "query"], "subject_types_supported": ["pairwise"], "claim_types_supported": ["normal"], "claims_parameter_supported": True, "request_parameter_supported": False, "request_uri_parameter_supported": False, "claims_supported": ["email"], "grant_types_supported": ["authorization_code", "implicit"], "issuer": BASE_URL, "require_request_uri_registration": False, "token_endpoint_auth_methods_supported": ["client_secret_basic"], "version": "3.0" } http_response = frontend.provider_config(context) provider_config = ProviderConfigurationResponse().deserialize(http_response.message, "json") provider_config_dict = provider_config.to_dict() scopes_supported = provider_config_dict.pop("scopes_supported") assert all(scope in scopes_supported for scope in ["openid", "email"]) assert provider_config_dict == expected_capabilities
def openid_conf(self): endpoint = {} for point, path in ENDPOINT.items(): endpoint[point] = "%s%s" % (self.host, path) signing_algs = list(jws.SIGNER_ALGS.keys()) resp = ProviderConfigurationResponse( issuer=self.name, scopes_supported=["openid", "profile", "email", "address"], identifiers_supported=["public", "PPID"], flows_supported=["code", "token", "code token", "id_token", "code id_token", "token id_token"], subject_types_supported=["pairwise", "public"], response_types_supported=["code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"], jwks_uri="http://example.com/oidc/jwks", id_token_signing_alg_values_supported=signing_algs, grant_types_supported=["authorization_code", "implicit"], **endpoint) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response
def test_example_response(): resp = { "version": "3.0", "issuer": "https://server.example.com", "authorization_endpoint": "https://server.example.com/connect/authorize", "token_endpoint": "https://server.example.com/connect/token", "token_endpoint_auth_methods_supported": ["client_secret_basic", "private_key_jwt"], "token_endpoint_alg_values_supported": ["RS256", "ES256"], "userinfo_endpoint": "https://server.example.com/connect/userinfo", "check_session_iframe": "https://server.example.com/connect/check_session", "end_session_endpoint": "https://server.example.com/connect/end_session", "jwks_uri": "https://server.example.com/jwks.json", "registration_endpoint": "https://server.example.com/connect/register", "scopes_supported": ["openid", "profile", "email", "address", "phone", "offline_access"], "response_types_supported": ["code", "code id_token", "id_token", "token id_token"], "acr_values_supported": ["urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze"], "subject_types_supported": ["public", "pairwise"], "userinfo_signing_alg_values_supported": ["RS256", "ES256", "HS256"], "userinfo_encryption_alg_values_supported": ["RSA1_5", "A128KW"], "userinfo_encryption_enc_values_supported": ["A128CBC+HS256", "A128GCM"], "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"], "id_token_encryption_alg_values_supported": ["RSA1_5", "A128KW"], "id_token_encryption_enc_values_supported": ["A128CBC+HS256", "A128GCM"], "request_object_signing_alg_values_supported": ["none", "RS256", "ES256"], "display_values_supported": ["page", "popup"], "claim_types_supported": ["normal", "distributed"], "claims_supported": ["sub", "iss", "auth_time", "acr", "name", "given_name", "family_name", "nickname", "profile", "picture", "website", "email", "email_verified", "locale", "zoneinfo", "http://example.info/claims/groups"], "claims_parameter_supported": True, "service_documentation": "http://server.example.com/connect/service_documentation.html", "ui_locales_supported": ["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"] } pcr = ProviderConfigurationResponse().deserialize(json.dumps(resp), "json") rk = resp.keys() # parameters with default value if missing rk.extend(["grant_types_supported", "request_parameter_supported", "request_uri_parameter_supported", "require_request_uri_registration"]) rk.sort() pk = pcr.keys() pk.sort() print rk print pk assert _eq(pk, rk)
def test_example_response(): resp = { "version": "3.0", "issuer": "https://server.example.com", "authorization_endpoint": "https://server.example.com/connect/authorize", "token_endpoint": "https://server.example.com/connect/token", "token_endpoint_auth_methods_supported": ["client_secret_basic", "private_key_jwt"], "token_endpoint_alg_values_supported": ["RS256", "ES256"], "userinfo_endpoint": "https://server.example.com/connect/userinfo", "check_session_iframe": "https://server.example.com/connect/check_session", "end_session_endpoint": "https://server.example.com/connect/end_session", "jwks_uri": "https://server.example.com/jwks.json", "registration_endpoint": "https://server.example.com/connect/register", "scopes_supported": ["openid", "profile", "email", "address", "phone", "offline_access"], "response_types_supported": ["code", "code id_token", "id_token", "token id_token"], "acr_values_supported": ["urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze"], "subject_types_supported": ["public", "pairwise"], "userinfo_signing_alg_values_supported": ["RS256", "ES256", "HS256"], "userinfo_encryption_alg_values_supported": ["RSA1_5", "A128KW"], "userinfo_encryption_enc_values_supported": ["A128CBC+HS256", "A128GCM"], "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"], "id_token_encryption_alg_values_supported": ["RSA1_5", "A128KW"], "id_token_encryption_enc_values_supported": ["A128CBC+HS256", "A128GCM"], "request_object_signing_alg_values_supported": ["none", "RS256", "ES256"], "display_values_supported": ["page", "popup"], "claim_types_supported": ["normal", "distributed"], "claims_supported": ["sub", "iss", "auth_time", "acr", "name", "given_name", "family_name", "nickname", "profile", "picture", "website", "email", "email_verified", "locale", "zoneinfo", "http://example.info/claims/groups"], "claims_parameter_supported": True, "service_documentation": "http://server.example.com/connect/service_documentation.html", "ui_locales_supported": ["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"] } pcr = ProviderConfigurationResponse().deserialize(json.dumps(resp), "json") rk = resp.keys() # parameters with default value if missing rk.extend(["grant_types_supported", "request_parameter_supported", "request_uri_parameter_supported", "require_request_uri_registration"]) rk.sort() pk = pcr.keys() pk.sort() print rk print pk assert _eq(pk, rk)
def provider_config(self, issuer, keys=True, endpoints=True): if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer url = OIDCONF_PATTERN % _issuer pcr = None r = self.http_request(url) if r.status_code == 200: pcr = ProviderConfigurationResponse().from_json(r.text) elif r.status_code == 302: while r.status_code == 302: r = self.http_request(r.headers["location"]) if r.status_code == 200: pcr = ProviderConfigurationResponse().from_json(r.text) break if pcr is None: raise Exception("Trying '%s', status %s" % (url, r.status_code)) if "issuer" in pcr: _pcr_issuer = pcr["issuer"] if pcr["issuer"].endswith("/"): if issuer.endswith("/"): _issuer = issuer else: _issuer = issuer + "/" else: if issuer.endswith("/"): _issuer = issuer[:-1] else: _issuer = issuer try: assert _issuer == _pcr_issuer except AssertionError: raise Exception("provider info issuer mismatch '%s' != '%s'" % ( _issuer, _pcr_issuer)) self.provider_info[_pcr_issuer] = pcr else: _pcr_issuer = issuer if endpoints: for key, val in pcr.items(): if key.endswith("_endpoint"): setattr(self, key, val) if keys: self.keyjar.load_keys(pcr, _pcr_issuer) return pcr
def providerinfo_endpoint(self, environ, start_response, **kwargs): logger.info("@providerinfo_endpoint") try: _response = ProviderConfigurationResponse( issuer=self.baseurl, token_endpoint_auth_types_supported=[ "client_secret_post", "client_secret_basic", "client_secret_jwt"], scopes_supported=["openid"], response_types_supported=["code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"], user_id_types_supported=["public"], #request_object_algs_supported=["HS256"] ) if not self.baseurl.endswith("/"): self.baseurl += "/" #keys = self.keystore.keys_by_owner(owner=".") #for cert in self.cert: # _response["x509_url"] = "%s%s" % (self.baseurl, cert) if self.jwk: _response["jwk_url"] = self.jwk #logger.info("endpoints: %s" % self.endpoints) for endp in self.endpoints: #logger.info("# %s, %s" % (endp, endp.name)) _response[endp.name] = "%s%s" % (self.baseurl, endp.type) #if self.test_mode: #print sys.stderr >> "providerinfo_endpoint.handle: %s" % # kwargs["handle"] logger.info("provider_info_response: %s" % (_response.to_dict(),)) headers=[("Cache-Control", "no-store"), ("x-ffo", "bar")] if "handle" in kwargs: (key, timestamp) = kwargs["handle"] if key.startswith(STR) and key.endswith(STR): cookie = self.cookie_func(self.cookie_name, key, self.seed, self.cookie_ttl) headers.append(cookie) resp = Response(_response.to_json(), content="application/json", headers=headers) except Exception, err: message = traceback.format_exception(*sys.exc_info()) logger.error(message) resp = Response(message, content="html/text")
def __init__(self, signing_key, configuration_information, authz_state, clients, userinfo, *, id_token_lifetime=3600, extra_scopes=None): # type: (jwkest.jwk.Key, Dict[str, Union[str, Sequence[str]]], se_leg_op.authz_state.AuthorizationState, # Mapping[str, Mapping[str, Any]], se_leg_op.userinfo.Userinfo, int) -> None """ Creates a new provider instance. :param configuration_information: see <a href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata"> "OpenID Connect Discovery 1.0", Section 3</a> :param clients: see <a href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata"> "OpenID Connect Dynamic Client Registration 1.0", Section 2</a> :param userinfo: read-only interface for user info :param id_token_lifetime: how long the signed ID Tokens should be valid (in seconds), defaults to 1 hour """ self.signing_key = signing_key self.configuration_information = ProviderConfigurationResponse(**configuration_information) if 'subject_types_supported' not in configuration_information: self.configuration_information['subject_types_supported'] = ['pairwise'] if 'id_token_signing_alg_values_supported' not in configuration_information: self.configuration_information['id_token_signing_alg_values_supported'] = ['RS256'] if 'scopes_supported' not in configuration_information: self.configuration_information['scopes_supported'] = ['openid'] if 'response_types_supported' not in configuration_information: self.configuration_information['response_types_supported'] = ['code', 'id_token', 'token id_token'] self.extra_scopes = {} if extra_scopes is None else extra_scopes _scopes = self.configuration_information['scopes_supported'] _scopes.extend(self.extra_scopes.keys()) self.configuration_information['scopes_supported'] = list(set(_scopes)) self.configuration_information.verify() self.authz_state = authz_state self.clients = clients self.userinfo = userinfo self.id_token_lifetime = id_token_lifetime self.authentication_request_validators = [] # type: List[Callable[[oic.oic.message.AuthorizationRequest], Boolean]] self.authentication_request_validators.append(authorization_request_verify) self.authentication_request_validators.append( functools.partial(client_id_is_known, self)) self.authentication_request_validators.append( functools.partial(redirect_uri_is_in_registered_redirect_uris, self)) self.authentication_request_validators.append( functools.partial(response_type_is_in_registered_response_types, self)) self.authentication_request_validators.append(userinfo_claims_only_specified_when_access_token_is_issued) self.authentication_request_validators.append(functools.partial(requested_scope_is_supported, self)) self.registration_request_validators = [] # type: List[Callable[[oic.oic.message.RegistrationRequest], Boolean]] self.registration_request_validators.append(registration_request_verify) self.registration_request_validators.append( functools.partial(client_preferences_match_provider_capabilities, self))
def test_deserialize(self): resp = { "authorization_endpoint": "https://server.example.com/connect/authorize", "issuer": "https://server.example.com", "token_endpoint": "https://server.example.com/connect/token", "token_endpoint_auth_methods_supported": [ "client_secret_basic", "private_key_jwt", ], "userinfo_endpoint": "https://server.example.com/connect/user", "check_id_endpoint": "https://server.example.com/connect/check_id", "refresh_session_endpoint": "https://server.example.com/connect/refresh_session", "end_session_endpoint": "https://server.example.com/connect/end_session", "jwk_url": "https://server.example.com/jwk.json", "registration_endpoint": "https://server.example.com/connect/register", "scopes_supported": ["openid", "profile", "email", "address", "phone"], "response_types_supported": ["code", "code id_token", "token id_token"], "acrs_supported": ["1", "2", "http://id.incommon.org/assurance/bronze"], "user_id_types_supported": ["public", "pairwise"], "userinfo_algs_supported": [ "HS256", "RS256", "A128CBC", "A128KW", "RSA1_5", ], "id_token_algs_supported": [ "HS256", "RS256", "A128CBC", "A128KW", "RSA1_5", ], "request_object_algs_supported": [ "HS256", "RS256", "A128CBC", "A128KW", "RSA1_5", ], } pcr = ProviderConfigurationResponse().deserialize( json.dumps(resp), "json") assert _eq(pcr["user_id_types_supported"], ["public", "pairwise"]) assert _eq(pcr["acrs_supported"], ["1", "2", "http://id.incommon.org/assurance/bronze"])
def _create_client(provider_metadata, client_metadata, verify_ssl=True): """ Create a pyoidc client instance. :param provider_metadata: provider configuration information :type provider_metadata: Mapping[str, Union[str, Sequence[str]]] :param client_metadata: client metadata :type client_metadata: Mapping[str, Union[str, Sequence[str]]] :return: client instance to use for communicating with the configured provider :rtype: oic.oic.Client """ client = oic.Client(client_authn_method=CLIENT_AUTHN_METHOD, verify_ssl=verify_ssl) # Provider configuration information if "authorization_endpoint" in provider_metadata: # no dynamic discovery necessary client.handle_provider_config( ProviderConfigurationResponse(**provider_metadata), provider_metadata["issuer"]) else: # do dynamic discovery client.provider_config(provider_metadata["issuer"]) # Client information if "client_id" in client_metadata: # static client info provided client.store_registration_info(RegistrationRequest(**client_metadata)) else: # do dynamic registration client.register(client.provider_info['registration_endpoint'], **client_metadata) client.subject_type = (client.registration_response.get("subject_type") or client.provider_info["subject_types_supported"][0]) return client
def __init__(self, provider_configuration, redirect_uri): """ Args: provider_configuration (flask_pyoidc.provider_configuration.ProviderConfiguration) """ self._provider_configuration = provider_configuration self._client = Client(client_authn_method=CLIENT_AUTHN_METHOD, settings=provider_configuration.client_settings) # Token Introspection is implemented under extension sub-package of # the client in pyoidc. self._client_extension = ClientExtension( client_authn_method=CLIENT_AUTHN_METHOD, settings=provider_configuration.client_settings) # Client Credentials Flow is implemented under oauth2 sub-package of # the client in pyoidc. self._oauth2_client = Oauth2Client( client_authn_method=CLIENT_AUTHN_METHOD, message_factory=CCMessageFactory, settings=self._provider_configuration.client_settings) provider_metadata = provider_configuration.ensure_provider_metadata( self._client) self._client.handle_provider_config( ProviderConfigurationResponse(**provider_metadata.to_dict()), provider_metadata['issuer']) if self._provider_configuration.registered_client_metadata: client_metadata = self._provider_configuration.registered_client_metadata.to_dict( ) client_metadata.update(redirect_uris=list(redirect_uri)) self._store_registration_info(client_metadata) self._redirect_uri = redirect_uri
def setup_conv(): entity = Client(client_authn_method=CLIENT_AUTHN_METHOD, verify_ssl=False) entity.provider_info = ProviderConfigurationResponse( authorization_endpoint="https://example.com", ) cls_factories = {'': oper.factory} func_factory = func.factory flow_state = FlowState('flows', profile_handler=ProfileHandler, cls_factories=cls_factories, func_factory=func_factory, display_order=OP_ORDER) iss = 'https://example.org' tag = 'foobar' session_handler = SessionHandler(iss, tag, flows=flow_state, tool_conf={}) # , rest=rest, **webenv) session_handler.iss = iss session_handler.tag = tag info = WebIh(session=session_handler, profile_handler=ProfileHandler) conv = Conversation([], entity, factory, callback_uris=[]) conv.events = Events() conv.tool_config = {} return {'conv': conv, 'io': info}
def init_relationship(self, provider_url): if not self.provider_info: opc = ProviderConfigurationResponse() try: pcr = self.provider_config(provider_url, serv_pattern=UMACONF_PATTERN) except Exception, err: raise else: opc.update(pcr) try: pcr = self.provider_config(provider_url, serv_pattern=UMACONF_PATTERN) except Exception, err: raise
def test_full_flow(self, context, frontend_with_extra_scopes): redirect_uri = "https://client.example.com/redirect" response_type = "code id_token token" mock_callback = Mock() frontend_with_extra_scopes.auth_req_callback_func = mock_callback # discovery http_response = frontend_with_extra_scopes.provider_config(context) provider_config = ProviderConfigurationResponse().deserialize(http_response.message, "json") # client registration registration_request = RegistrationRequest(redirect_uris=[redirect_uri], response_types=[response_type]) context.request = registration_request.to_dict() http_response = frontend_with_extra_scopes.client_registration(context) registration_response = RegistrationResponse().deserialize(http_response.message, "json") # authentication request authn_req = AuthorizationRequest( redirect_uri=redirect_uri, client_id=registration_response["client_id"], response_type=response_type, scope="openid email eduperson", state="state", nonce="nonce", ) context.request = dict(parse_qsl(authn_req.to_urlencoded())) frontend_with_extra_scopes.handle_authn_request(context) assert mock_callback.call_count == 1 # fake authentication response from backend internal_response = self.setup_for_authn_response( context, frontend_with_extra_scopes, authn_req ) http_response = frontend_with_extra_scopes.handle_authn_response( context, internal_response ) authn_resp = AuthorizationResponse().deserialize(urlparse(http_response.message).fragment, "urlencoded") assert "code" in authn_resp assert "access_token" in authn_resp assert "id_token" in authn_resp # token request context.request = AccessTokenRequest(redirect_uri=authn_req["redirect_uri"], code=authn_resp["code"]).to_dict() credentials = "{}:{}".format(registration_response["client_id"], registration_response["client_secret"]) basic_auth = urlsafe_b64encode(credentials.encode("utf-8")).decode("utf-8") context.request_authorization = "Basic {}".format(basic_auth) http_response = frontend_with_extra_scopes.token_endpoint(context) parsed = AccessTokenResponse().deserialize(http_response.message, "json") assert "access_token" in parsed assert "id_token" in parsed # userinfo request context.request = {} context.request_authorization = "Bearer {}".format(parsed["access_token"]) http_response = frontend_with_extra_scopes.userinfo_endpoint(context) parsed = OpenIDSchema().deserialize(http_response.message, "json") assert "email" in parsed assert "eduperson_principal_name" in parsed assert "eduperson_scoped_affiliation" in parsed
def run(self): if self.dynamic: self.catch_exception_and_error(self.conv.entity.provider_config, **self.op_args) else: self.conv.events.store(EV_NOOP, "Dynamic discovery") self.conv.entity.provider_info = ProviderConfigurationResponse( **self.conv.entity_config["provider_info"])
def test_complete_auth_token_idtoken_no_alg_config(self): _state = "state0" self.consumer.consumer_config["response_type"] = ["id_token", "token"] self.consumer.provider_info = ProviderConfigurationResponse( issuer="https://example.com") # abs min self.consumer.authz_req = {} # Store AuthzReq with state as key args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], "nonce": "nonce", } token = IdToken( iss="https://example.com", aud="client_1", sub="some_sub", exp=1565348600, iat=1565348300, nonce="nonce", ) location = ( "https://example.com/cb?state=state0&access_token=token&token_type=bearer&" "scope=openid&id_token={}".format( token.to_jwt(key=[SYMKey(key="hemlig")], algorithm="HS256"))) with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) result = self.consumer.do_authorization_request(state=_state, request_args=args) query = parse_qs(urlparse(result.request.url).query) assert query["client_id"] == ["client_1"] assert query["scope"] == ["openid"] assert query["response_type"] == ["id_token token"] assert query["state"] == ["state0"] assert query["nonce"] == ["nonce"] assert query["redirect_uri"] == ["https://example.com/cb"] parsed = urlparse(result.headers["location"]) with freeze_time("2019-08-09 11:00:00"): part = self.consumer.parse_authz(query=parsed.query, algs={"sign": "HS256"}) assert isinstance(part, tuple) auth = part[0] atr = part[1] idt = part[2] assert auth is None assert isinstance(atr, AccessTokenResponse) assert _eq( atr.keys(), ["access_token", "id_token", "token_type", "state", "scope"]) assert isinstance(idt, IdToken)
def _provider_config(self, context): """ Construct the provider configuration information (served at /.well-known/openid-configuration). :type context: satosa.context.Context :rtype: oic.utils.http_util.Response :param context: the current context :return: HTTP response to the client """ http_resp = self.provider.providerinfo_endpoint() if not isinstance(http_resp, Response): return http_resp provider_config = ProviderConfigurationResponse().deserialize(http_resp.message, "json") del provider_config["token_endpoint_auth_methods_supported"] del provider_config["require_request_uri_registration"] http_resp.message = provider_config.to_json() return http_resp
def test_provider_configuration_endpoint(self): expected_capabilities = { "response_types_supported": ["id_token"], "id_token_signing_alg_values_supported": ["RS256"], "response_modes_supported": ["fragment", "query"], "subject_types_supported": ["public", "pairwise"], "grant_types_supported": ["implicit"], "claim_types_supported": ["normal"], "claims_parameter_supported": True, "request_parameter_supported": False, "request_uri_parameter_supported": False, } http_response = self.instance._provider_config(Context()) provider_config = ProviderConfigurationResponse().deserialize(http_response.message, "json") assert all( item in provider_config.to_dict().items() for item in expected_capabilities.items()) assert provider_config["authorization_endpoint"] == "{}/foo_backend/authorization".format(self.ISSUER)
def prepare_client(): # http://pyoidc.readthedocs.io/en/latest/examples/rp.html # Instantiate a client client = Client(client_authn_method=CLIENT_AUTHN_METHOD) # Register the OP # endpoints are now loaded from a json file rather than defined here # DEV endpoints # issuer = "https://unity.eudat-aai.fz-juelich.de:443" # authorization_endpoint = "https://unity.eudat-aai.fz-juelich.de:443/oauth2-as/oauth2-authz" # token_endpoint = "https://unity.eudat-aai.fz-juelich.de:443/oauth2/token" # userinfo_endpoint = "https://unity.eudat-aai.fz-juelich.de:443/oauth2/userinfo" # PROD endpoints # issuer = "https://b2access.eudat.eu:443" # authorization_endpoint = "https://b2access.eudat.eu:443/oauth2-as/oauth2-authz" # token_endpoint = "https://b2access.eudat.eu:443/oauth2/token" # userinfo_endpoint = "https://b2access.eudat.eu:443/oauth2/userinfo" try: dir = os.path.dirname(__file__) provider_endpoints = json.load(open(dir + '/provider_endpoints.json')) issuer = provider_endpoints['issuer'] authorization_endpoint = provider_endpoints['authorization_endpoint'] token_endpoint = provider_endpoints['token_endpoint'] userinfo_endpoint = provider_endpoints['userinfo_endpoint'] except: print "Error when reading provider_endpoints.json" stdlogger.error("Error when reading provider_endpoints.json") issuer = "error" authorization_endpoint = "error" token_endpoint = "error" userinfo_endpoint = "error" op_info = ProviderConfigurationResponse(issuer=issuer, authorization_endpoint=authorization_endpoint, token_endpoint=token_endpoint, userinfo_endpoint=userinfo_endpoint) client.provider_info = op_info # Set our credentials (that we got from manually registering to B2Access), as well as the redirect URI try: dir = os.path.dirname(__file__) client_credentials = json.load(open(dir + '/client_credentials.json')) id = client_credentials['client_id'] secret = client_credentials['client_secret'] uri = client_credentials['client_redirect_uri'] except: print "Error when reading client_credential.json" stdlogger.error("Error when reading client_credential.json") id = "error" secret = "error" uri = "error" # /!\ Added the redirect URI here, else it's not defined later (in args ={[...] client.registration_response["redirect_uris"][0]) uris = [uri] info = {"client_id": id, "client_secret": secret, "redirect_uris": uris} client_reg = RegistrationResponse(**info) client.store_registration_info(client_reg) return client
def _provider_config(self, context): """ Construct the provider configuration information (served at /.well-known/openid-configuration). :type context: satosa.context.Context :rtype: oic.utils.http_util.Response :param context: the current context :return: HTTP response to the client """ http_resp = self.provider.providerinfo_endpoint() if not isinstance(http_resp, Response): return http_resp provider_config = ProviderConfigurationResponse().deserialize( http_resp.message, "json") del provider_config["token_endpoint_auth_methods_supported"] del provider_config["require_request_uri_registration"] http_resp.message = provider_config.to_json() return http_resp
def test_register_endpoints_dynamic_client_registration_is_configurable( self, frontend_config, client_registration_enabled): frontend_config["provider"]["client_registration_supported"] = client_registration_enabled frontend = self.create_frontend(frontend_config) urls = frontend.register_endpoints(["test"]) assert (("^{}/{}".format(frontend.name, RegistrationEndpoint.url), frontend.client_registration) in urls) == client_registration_enabled provider_info = ProviderConfigurationResponse().deserialize(frontend.provider_config(None).message, "json") assert ("registration_endpoint" in provider_info) == client_registration_enabled
def _build_oidc_client(self): client = Client(client_authn_method=CLIENT_AUTHN_METHOD) provider_config = ProviderConfigurationResponse( **self.settings["oidc"]["provider"]["configuration"]) client.handle_provider_config(provider_config, provider_config["issuer"]) client.store_registration_info( RegistrationResponse( **self.settings["oidc"]["provider"]["registration"])) return client
def create_oidc_client(issuer=None, registration_info=None): if "oidc" not in g: g.oidc = Client(client_authn_method=CLIENT_AUTHN_METHOD) config = get(f"{issuer}/.well-known/openid-configuration", headers={"Content-type": "application/json"}).json() provider_config = ProviderConfigurationResponse(**config) g.oidc.handle_provider_config(provider_config, issuer) g.oidc.store_registration_info( RegistrationResponse(**registration_info) ) g.oidc.redirect_uris.append(f"{g.oidc.registration_response['redirect_uris'][0]}/callback") return g.oidc
def test_token_endpoint_is_not_required_for_implicit_flow_only(self): provider_config = { "issuer": "https://server.example.com", "authorization_endpoint": "https://server.example.com/connect/authorize", "jwks_uri": "https://server.example.com/jwks.json", "response_types_supported": ["id_token", "token id_token"], "subject_types_supported": ["public", "pairwise"], "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"], } # should not raise an exception assert ProviderConfigurationResponse(**provider_config).verify()
def test_register_endpoints_token_and_userinfo_endpoint_is_not_published_if_only_implicit_flow( self, frontend_config, context): frontend_config["provider"]["response_types_supported"] = ["id_token", "id_token token"] frontend = self.create_frontend(frontend_config) urls = frontend.register_endpoints(["test"]) assert ("^{}/{}".format("test", TokenEndpoint.url), frontend.token_endpoint) not in urls assert ("^{}/{}".format("test", UserinfoEndpoint.url), frontend.userinfo_endpoint) not in urls http_response = frontend.provider_config(context) provider_config = ProviderConfigurationResponse().deserialize(http_response.message, "json") assert "token_endpoint" not in provider_config
def test_token_endpoint_is_required_for_other_than_implicit_flow_only(self): provider_config = { "issuer": "https://server.example.com", "authorization_endpoint": "https://server.example.com/connect/authorize", "jwks_uri": "https://server.example.com/jwks.json", "response_types_supported": ["code", "id_token"], "subject_types_supported": ["public", "pairwise"], "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"], } with pytest.raises(MissingRequiredAttribute): ProviderConfigurationResponse(**provider_config).verify()
def test_complete_auth_token_idtoken_none_cipher_token(self): _state = "state0" self.consumer.consumer_config["response_type"] = ["token"] self.consumer.registration_response = RegistrationResponse( id_token_signed_response_alg="none") self.consumer.provider_info = ProviderConfigurationResponse( issuer="https://example.com") # abs min self.consumer.authz_req = {} # Store AuthzReq with state as key self.consumer.sdb[_state] = {"redirect_uris": []} args = { "client_id": self.consumer.client_id, "response_type": self.consumer.consumer_config["response_type"], "scope": ["openid"], "nonce": "nonce", } token = IdToken( iss="https://example.com", aud="client_1", sub="some_sub", exp=1565348600, iat=1565348300, nonce="nonce", ) # Downgrade the algorithm to `none` location = ( "https://example.com/cb?state=state0&access_token=token&token_type=bearer&" "scope=openid&id_token={}".format( token.to_jwt(key=KC_RSA.keys(), algorithm="none"))) with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/authorization", status=302, headers={"location": location}, ) result = self.consumer.do_authorization_request(state=_state, request_args=args) query = parse_qs(urlparse(result.request.url).query) assert query["client_id"] == ["client_1"] assert query["scope"] == ["openid"] assert query["response_type"] == ["token"] assert query["state"] == ["state0"] assert query["nonce"] == ["nonce"] assert query["redirect_uri"] == ["https://example.com/cb"] parsed = urlparse(result.headers["location"]) with freeze_time("2019-08-09 11:00:00"): with pytest.raises(WrongSigningAlgorithm): self.consumer.parse_authz(query=parsed.query)
def init_relationship(self, provider_url): if not self.provider_info: opc = ProviderConfigurationResponse() try: pcr = self.provider_config(provider_url, serv_pattern=UMACONF_PATTERN) except Exception as err: raise else: opc.update(pcr) if 'oidc_provider' in self.conf: try: pcr = self.oidc_client.provider_config(provider_url) except Exception as err: raise else: opc.update(pcr) self.provider_info = opc if not self.client_secret: self.register( self.provider_info["dynamic_client_endpoint"])
def test_required_parameters(self, required_param): provider_config = { "issuer": "https://server.example.com", "authorization_endpoint": "https://server.example.com/connect/authorize", "jwks_uri": "https://server.example.com/jwks.json", "response_types_supported": ["code", "code id_token", "id_token", "token id_token"], "subject_types_supported": ["public", "pairwise"], "id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"], } del provider_config[required_param] with pytest.raises(MissingRequiredAttribute): ProviderConfigurationResponse(**provider_config).verify()
def __init__(self, flask_app, client_registration_info=None, issuer=None, provider_configuration_info=None, userinfo_endpoint_method='POST', extra_request_args=None): self.app = flask_app self.userinfo_endpoint_method = userinfo_endpoint_method self.extra_request_args = extra_request_args or {} self.client = Client(client_authn_method=CLIENT_AUTHN_METHOD) # Raise exception if oic auth will fail based on lack of data. if not issuer and not provider_configuration_info: raise ValueError( 'Either \'issuer\' (for dynamic discovery) or provider_configuration_info' ' for static configuration must be specified.') # If only issuer provided assume discovery and initalize anyway. if issuer and not provider_configuration_info: self.client.provider_config(issuer) else: # Otherwise assume non-discovery for oidc self.client.handle_provider_config( ProviderConfigurationResponse(**provider_configuration_info), provider_configuration_info['issuer']) self.client_registration_info = client_registration_info or {} # setup redirect_uri as a flask route self.app.add_url_rule('/redirect_uri', 'redirect_uri', self._handle_authentication_response) # dynamically add the Flask redirect uri to the client info with self.app.app_context(): self.client_registration_info['redirect_uris'] \ = url_for('redirect_uri') # if non-discovery client add the provided info from the constructor if client_registration_info and 'client_id' in client_registration_info: # static client info provided self.client.store_registration_info( RegistrationRequest(**client_registration_info)) self.logout_view = None self._error_view = None
def __init__(self, provider_configuration, redirect_uri): """ Args: provider_configuration (flask_pyoidc.provider_configuration.ProviderConfiguration) """ self._provider_configuration = provider_configuration self._client = Client(client_authn_method=CLIENT_AUTHN_METHOD) provider_metadata = provider_configuration.ensure_provider_metadata() self._client.handle_provider_config(ProviderConfigurationResponse(**provider_metadata.to_dict()), provider_metadata['issuer']) if self._provider_configuration.registered_client_metadata: client_metadata = self._provider_configuration.registered_client_metadata.to_dict() registration_response = RegistrationResponse(**client_metadata) self._client.store_registration_info(registration_response) self._redirect_uri = redirect_uri
def parse_args(self): OAuth2.parse_args(self) if self.args.external_server: self.environ["keyprovider"] = None _keyjar = self.client.keyjar pcr = ProviderConfigurationResponse() n = 0 for param in URL_TYPES: if param in self.pinfo: n += 1 pcr[param] = self.pinfo[param] if n: if _keyjar is None: _keyjar = self.client.keyjar = KeyJar() _keyjar.load_keys(pcr, self.pinfo["issuer"])
def setup_consumer(self, session_db_factory): client_id = "client_1" client_config = { "client_id": client_id, "client_authn_method": CLIENT_AUTHN_METHOD, } self.consumer = Consumer(DictSessionBackend(), CONFIG, client_config, SERVER_INFO) self.consumer.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"] } self.consumer.keyjar = CLIKEYS self.consumer.redirect_uris = ["https://example.com/cb"] self.consumer.authorization_endpoint = "https://example.com/authorization" self.consumer.token_endpoint = "https://example.com/token" self.consumer.userinfo_endpoint = "https://example.com/userinfo" # type: ignore self.consumer.client_secret = "hemlig" self.consumer.secret_type = "basic" self.consumer.provider_info = ProviderConfigurationResponse( issuer="https://example.com") # abs min
def create_client(self): """Create the OpenIDConnect client from the data stored in the contest object, and store some information in this handler object. """ oic_info = json.loads(self.contest.openidconnect_info) self.redirect_uri = ( self.request.protocol + "://" + self.request.host + self.request.path) self.op_info = oic_info["op_info"] self.client_info = oic_info["client_info"] self.client_info["redirect_uris"] = [self.redirect_uri] self.jwks = oic_info["jwks"] keyjar = KeyJar() keyjar.import_jwks(self.jwks, self.op_info["issuer"]) self.client = Client( client_authn_method=CLIENT_AUTHN_METHOD, keyjar=keyjar) self.client.provider_info = \ ProviderConfigurationResponse(**self.op_info) self.client.store_registration_info( RegistrationResponse(**self.client_info))
def __init__(self, flask_app, client_registration_info=None, issuer=None, provider_configuration_info=None, userinfo_endpoint_method='POST', extra_request_args=None): self.app = flask_app self.userinfo_endpoint_method = userinfo_endpoint_method self.extra_request_args = extra_request_args or {} self.client = Client(client_authn_method=CLIENT_AUTHN_METHOD) if not issuer and not provider_configuration_info: raise ValueError( 'Either \'issuer\' (for dynamic discovery) or \'provider_configuration_info\' (for static configuration must be specified.' ) if issuer and not provider_configuration_info: self.client.provider_config(issuer) else: self.client.handle_provider_config( ProviderConfigurationResponse(**provider_configuration_info), provider_configuration_info['issuer']) self.client_registration_info = client_registration_info or {} # setup redirect_uri self.app.add_url_rule('/redirect_uri', 'redirect_uri', self._handle_authentication_response) with self.app.app_context(): self.client_registration_info['redirect_uris'] = url_for( 'redirect_uri') if client_registration_info and 'client_id' in client_registration_info: # static client info provided self.client.store_registration_info( RegistrationRequest(**client_registration_info)) self.logout_view = None self._error_view = None
def create_openid_client(oidc_provider_config, client_id, client_secret): """ Build an OIDC client using the oidc provider configuration """ # build our OpenID client openid_client = Client(client_authn_method=CLIENT_AUTHN_METHOD, client_id=client_id) # save the client information to the OIDC client client_registration_info = RegistrationResponse( **{ "client_id": client_id, "client_secret": client_secret }) openid_client.store_registration_info(client_registration_info) # save the provider config to the OIDC client provider_configuration = ProviderConfigurationResponse( **oidc_provider_config) openid_client.handle_provider_config(provider_configuration, provider_configuration['issuer'], True, True) return openid_client
def init_relationship(self, provider_url): if not self.provider_info: opc = ProviderConfigurationResponse() try: pcr = self.provider_config(provider_url, serv_pattern=UMACONF_PATTERN) except Exception as err: raise else: opc.update(pcr) if 'oidc_provider' in self.conf: try: pcr = self.oidc_client.provider_config(provider_url) except Exception as err: raise else: opc.update(pcr) self.provider_info = opc if not self.client_secret: self.register(self.provider_info["dynamic_client_endpoint"])
def create_client(self, userid="", **kwargs): """ Do an instantiation of a client instance. :param userid: An identifier of the user :param: Keyword arguments Keys are ["srv_discovery_url", "client_info", "client_registration", "provider_info"] :return: client instance """ _key_set = set(list(kwargs.keys())) try: _verify_ssl = kwargs["verify_ssl"] except KeyError: _verify_ssl = self.verify_ssl else: _key_set.discard("verify_ssl") client = self.client_cls( client_authn_method=CLIENT_AUTHN_METHOD, behaviour=kwargs["behaviour"], verify_ssl=_verify_ssl, ) try: client.userinfo_request_method = kwargs["userinfo_request_method"] except KeyError: pass else: _key_set.discard("userinfo_request_method") # The behaviour parameter is not significant for the election process _key_set.discard("behaviour") for param in ["allow"]: try: setattr(client, param, kwargs[param]) except KeyError: pass else: _key_set.discard(param) if _key_set == {"client_info"}: # Everything dynamic # There has to be a userid if not userid: raise MissingAttribute("Missing userid specification") # Find the service that provides information about the OP issuer = client.wf.discovery_query(userid) # Gather OP information client.provider_config(issuer) # register the client client.register( client.provider_info["registration_endpoint"], **kwargs["client_info"] ) self.get_path(kwargs["client_info"]["redirect_uris"], issuer) elif _key_set == set(["client_info", "srv_discovery_url"]): # Ship the webfinger part # Gather OP information client.provider_config(kwargs["srv_discovery_url"]) # register the client client.register( client.provider_info["registration_endpoint"], **kwargs["client_info"] ) self.get_path( kwargs["client_info"]["redirect_uris"], kwargs["srv_discovery_url"] ) elif _key_set == set(["provider_info", "client_info"]): client.handle_provider_config( ProviderConfigurationResponse(**kwargs["provider_info"]), kwargs["provider_info"]["issuer"], ) client.register( client.provider_info["registration_endpoint"], **kwargs["client_info"] ) self.get_path( kwargs["client_info"]["redirect_uris"], kwargs["provider_info"]["issuer"], ) elif _key_set == set(["provider_info", "client_registration"]): client.handle_provider_config( ProviderConfigurationResponse(**kwargs["provider_info"]), kwargs["provider_info"]["issuer"], ) client.store_registration_info( RegistrationResponse(**kwargs["client_registration"]) ) self.get_path( kwargs["client_info"]["redirect_uris"], kwargs["provider_info"]["issuer"], ) elif _key_set == set(["srv_discovery_url", "client_registration"]): client.provider_config(kwargs["srv_discovery_url"]) client.store_registration_info( RegistrationResponse(**kwargs["client_registration"]) ) self.get_path( kwargs["client_registration"]["redirect_uris"], kwargs["srv_discovery_url"], ) else: raise Exception("Configuration error ?") return client
def providerinfo_endpoint(self, handle="", **kwargs): _log_debug = logger.debug _log_info = logger.info _log_info("@providerinfo_endpoint") try: _response = ProviderConfigurationResponse( issuer=self.baseurl, token_endpoint_auth_methods_supported=[ "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt"], scopes_supported=["openid"], response_types_supported=["code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"], subject_types_supported=["public", "pairwise"], grant_types_supported=[ "authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer"], claim_types_supported=["normal", "aggregated", "distributed"], claims_supported=SCOPE2CLAIMS.keys(), claims_parameter_supported="true", request_parameter_supported="true", request_uri_parameter_supported="true", #request_object_algs_supported=["HS256"] ) sign_algs = jws.SIGNER_ALGS.keys() for typ in ["userinfo", "id_token", "request_object", "token_endpoint_auth"]: _response["%s_signing_alg_values_supported" % typ] = sign_algs algs = jwe.SUPPORTED["alg"] for typ in ["userinfo", "id_token", "request_object"]: _response["%s_encryption_alg_values_supported" % typ] = algs encs = jwe.SUPPORTED["enc"] for typ in ["userinfo", "id_token", "request_object"]: _response["%s_encryption_enc_values_supported" % typ] = encs if not self.baseurl.endswith("/"): self.baseurl += "/" #keys = self.keyjar.keys_by_owner(owner=".") if self.jwks_uri: _response["jwks_uri"] = self.jwks_uri #_log_info("endpoints: %s" % self.endpoints) for endp in self.endpoints: #_log_info("# %s, %s" % (endp, endp.name)) _response[endp.name] = "%s%s" % (self.baseurl, endp.etype) _log_info("provider_info_response: %s" % (_response.to_dict(),)) headers = [("Cache-Control", "no-store"), ("x-ffo", "bar")] if handle: (key, timestamp) = handle if key.startswith(STR) and key.endswith(STR): cookie = self.cookie_func(key, self.cookie_name, "pinfo", self.sso_ttl) headers.append(cookie) resp = Response(_response.to_json(), content="application/json", headers=headers) except Exception, err: message = traceback.format_exception(*sys.exc_info()) logger.error(message) resp = Response(message, content="html/text")
try: OAS.cookie_name = config.COOKIENAME except AttributeError: pass OAS.cookie_func = http_util.cookie #print URLS if args.debug: OAS.debug = True if args.authn_as: OAS.authn_as = args.authn_as if args.provider_conf: prc = ProviderConfigurationResponse().from_json(open(args.provider_conf).read()) endpoints = [] for key in prc.keys(): if key.endswith("_endpoint"): endpoints.append(key) else: endpoints = ENDPOINTS add_endpoints(endpoints) OAS.endpoints = endpoints if args.port == 80: OAS.baseurl = config.baseurl else: if config.baseurl.endswith("/"): config.baseurl = config.baseurl[:-1]