def test_parse_registration_request(self): regreq = RegistrationRequest( contacts=["*****@*****.**"], redirect_uris=["http://example.org/jqauthz"], application_name="pacubar", client_id=CLIENT_ID, operation="register", application_type="web") request = self.srv.parse_registration_request( data=regreq.to_urlencoded()) assert isinstance(request, RegistrationRequest) assert _eq(request.keys(), [ 'redirect_uris', 'contacts', 'client_id', 'application_name', 'operation', 'application_type', 'response_types' ]) assert request["application_name"] == "pacubar" assert request["operation"] == "register"
def test_registration_endpoint(self): req = RegistrationRequest() req["application_type"] = "web" req["client_name"] = "My super service" req["redirect_uris"] = ["http://example.com/authz"] req["contacts"] = ["*****@*****.**"] req["response_types"] = ["code"] resp = self.provider.registration_endpoint(request=req.to_json()) regresp = RegistrationResponse().deserialize(resp.message, "json") assert _eq(regresp.keys(), [ 'redirect_uris', 'contacts', 'application_type', 'client_name', 'registration_client_uri', 'client_secret_expires_at', 'registration_access_token', 'client_id', 'client_secret', 'client_id_issued_at', 'response_types' ])
def test_registered_redirect_uri_faulty_with_query_component(self, uri): rr = RegistrationRequest( operation="register", redirect_uris=["http://example.org/cb?foo=bar"], response_types=["code"]) registration_req = rr.to_json() resp = self.provider.registration_endpoint(request=registration_req) regresp = RegistrationResponse().from_json(resp.message) cid = regresp["client_id"] areq = AuthorizationRequest(redirect_uri=uri, client_id=cid, scope="openid", response_type="code") with pytest.raises(RedirectURIError): self.provider._verify_redirect_uri(areq)
def test_verify_sector_identifier_ru_missing_in_si(self): """Redirect_uris is not present in the sector_identifier_uri content.""" rr = RegistrationRequest(operation="register", sector_identifier_uri="https://example.com", redirect_uris=["http://example.com/missing"]) with responses.RequestsMock() as rsps, LogCapture( level=logging.DEBUG) as logcap: rsps.add(rsps.GET, "https://example.com", body=json.dumps(["http://example.com/present"])) with pytest.raises( InvalidSectorIdentifier, message="redirect uri missing from sector_identifiers"): self.provider._verify_sector_identifier(rr) assert len(logcap.records) == 2 assert logcap.records[0].msg == "sector_identifier_uri => %s" assert logcap.records[0].args == ('["http://example.com/present"]', ) assert logcap.records[1].msg == "redirect_uris: %s" assert logcap.records[1].args == (["http://example.com/missing"], )
def registration_endpoint(self, request, authn=None, **kwargs): logger.debug("@registration_endpoint: <<%s>>" % sanitize(request)) try: request = ClientMetadataStatement().deserialize(request, "json") except ValueError: request = ClientMetadataStatement().deserialize(request) logger.info("registration_request:%s" % sanitize(request.to_dict())) request_args = self.get_metadata_statement(request) request = RegistrationRequest(**request_args) result = self.client_registration_setup(request) if isinstance(result, Response): return result return Created(result.to_json(), content="application/json", headers=[("Cache-Control", "no-store")])
def init_app(self, app): self._app = app if not app.extensions: app.extensions = {} app.extensions['oidc_client'] = self config = app.config.get('OIDC_CLIENT', {}) client = Client(client_authn_method=CLIENT_AUTHN_METHOD) client.provider_config(config['issuer']) config['redirect_uris'] = [self._init_redirect_uri(app)] client.store_registration_info(RegistrationRequest(**config)) self.client = client self.client_registration_info = config self.logout_view = None
def test_verify_sector_identifier_ru_ok(self): """Redirect_uris is present in the sector_identifier_uri content.""" rr = RegistrationRequest(operation="register", sector_identifier_uri="https://example.com", redirect_uris=["http://example.com/present"]) redirects = ["http://example.com/present"] with responses.RequestsMock() as rsps, LogCapture( level=logging.DEBUG) as logcap: rsps.add(rsps.GET, "https://example.com", body=json.dumps(redirects)) si_redirects, si_url = self.provider._verify_sector_identifier(rr) assert si_url == "https://example.com" assert si_redirects == redirects assert len(logcap.records) == 2 assert logcap.records[0].msg == "sector_identifier_uri => %s" assert logcap.records[0].args == ('["http://example.com/present"]', ) assert logcap.records[1].msg == "redirect_uris: %s" assert logcap.records[1].args == (["http://example.com/present"], )
def test_dynamic_client(provider_info, browser): redirect_uri = "http://localhost" # Dynamic registration reg_req = RegistrationRequest(**{"redirect_uris": [redirect_uri], "response_types": ["id_token"]}) resp = requests.post(reg_req.request(provider_info["registration_endpoint"])) reg_resp = RegistrationResponse().from_json(resp.text) # Authentication auth_req = AuthorizationRequest( **{"client_id": reg_resp["client_id"], "scope": "openid", "response_type": "id_token", "redirect_uri": redirect_uri, "state": "state0", "nonce": "nonce0"}) browser.get(auth_req.request(provider_info["authorization_endpoint"])) fill_login_details(browser) # Authentication response urlencoded_resp = urlparse(browser.current_url).fragment auth_resp = AuthorizationResponse().from_urlencoded(urlencoded_resp) idt = IdToken().from_jwt(auth_resp["id_token"], verify=False) assert browser.current_url.startswith(redirect_uri) assert auth_resp["state"] == "state0" assert idt["nonce"] == "nonce0"
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 init_oidc_client(app): oidc_client = Client(client_authn_method=CLIENT_AUTHN_METHOD) oidc_client.store_registration_info( RegistrationRequest(**app.config['CLIENT_REGISTRATION_INFO'])) provider = app.config['PROVIDER_CONFIGURATION_INFO']['issuer'] try: oidc_client.provider_config(provider) except ConnectionError: app.logger.warning( 'No connection to provider {!s}. Can not start without provider configuration.' ' Retrying...'.format(provider)) # Retry after 20 seconds sleep(20) # Hack until we use new frontends try: oidc_client.provider_config(provider) except ConnectionError: app.logger.critical( 'No connection to provider {!s}. Can not start without provider configuration.' ' Exiting.'.format(provider)) exit(1) return oidc_client
def init_oidc_client(app): oidc_client = Client(client_authn_method=CLIENT_AUTHN_METHOD) with app.app_context(): try: app.config['AUTHORIZATION_RESPONSE_URI'] = url_for( 'vetting.authorization_response') except BuildError as e: app.logger.error( 'View vetting.authorization_response needs to be loaded or implemented.' ) raise e oidc_client.store_registration_info( RegistrationRequest(**app.config['CLIENT_REGISTRATION_INFO'])) provider = app.config['PROVIDER_CONFIGURATION_INFO']['issuer'] try: oidc_client.provider_config(provider) except ConnectionError as e: app.logger.critical( 'No connection to provider {!s}. Can not start without provider configuration.' .format(provider)) raise e app.oidc_client = oidc_client return app
def test_registration_request(self): req = RegistrationRequest( operation="register", default_max_age=10, require_auth_time=True, default_acr="foo", application_type="web", redirect_uris=["https://example.com/authz_cb"]) js = req.to_json() js_obj = json.loads(js) expected_js_obj = { "redirect_uris": ["https://example.com/authz_cb"], "application_type": "web", "default_acr": "foo", "require_auth_time": True, "operation": "register", "default_max_age": 10 } assert js_obj == expected_js_obj assert query_string_compare( req.to_urlencoded(), "redirect_uris=https%3A%2F%2Fexample.com%2Fauthz_cb&application_type=web&default_acr=foo&require_auth_time=True&operation=register&default_max_age=10" )
def test_deserialize(self): msg = { "application_type": "web", "redirect_uris": ["https://client.example.org/callback", "https://client.example.org/callback2"], "client_name": "My Example", "client_name#ja-Jpan-JP": "クライアント名", "logo_uri": "https://client.example.org/logo.png", "subject_type": "pairwise", "sector_identifier_uri": "https://other.example.net/file_of_redirect_uris.json", "token_endpoint_auth_method": "client_secret_basic", "jwks_uri": "https://client.example.org/my_public_keys.jwks", "userinfo_encrypted_response_alg": "RSA1_5", "userinfo_encrypted_response_enc": "A128CBC+HS256", "contacts": ["*****@*****.**", "*****@*****.**"], "request_uris": [ "https://client.example.org/rf.txt" "#qpXaRLh_n93TTR9F252ValdatUQvQiJi5BDub2BeznA"] } reg = RegistrationRequest().deserialize(json.dumps(msg), "json") assert _eq(list(msg.keys()) + ['response_types'], reg.keys())
def test_registration_request(): req = RegistrationRequest(operation="register", default_max_age=10, require_auth_time=True, default_acr="foo", application_type="web", redirect_uris=["https://example.com/authz_cb"]) js = req.to_json() js_obj = json.loads(js) expected_js_obj = { "redirect_uris": ["https://example.com/authz_cb"], "application_type": "web", "default_acr": "foo", "require_auth_time": True, "operation": "register", "default_max_age": 10 } assert js_obj == expected_js_obj ue = req.to_urlencoded() ue_splits = ue.split('&') expected_ue_splits = 'redirect_uris=https%3A%2F%2Fexample.com%2Fauthz_cb&application_type=web&default_acr=foo&require_auth_time=True&operation=register&default_max_age=10'.split( '&') assert _eq(ue_splits, expected_ue_splits)
# ----------------------------------------------------------------------------- # The RP as federation entity # ----------------------------------------------------------------------------- sunet_rp = Operator(iss='https://sunet.se/sysadm', keyjar=build_keyjar(key_conf)[1]) # ----------------------------------------------------------------------------- # -- construct Registration Request to be signed by organisation # ----------------------------------------------------------------------------- rreq = RegistrationRequest( redirect_uris=['https://sunet.se/rp1/callback'], application_type='web', response_types=['code'], signing_keys=sunet_rp.signing_keys_as_jwks(), jwks_uri_signed='https://sunet.se/rp1/jwks.jws' ) print_request('Client Registration request', rreq) # ----------------------------------------------------------------------------- # SUNET signs Registration Request once per federation # ----------------------------------------------------------------------------- # adds the developers software statement rreq.update({ "metadata_statements": [sunet_swamid], })
def registration_endpoint(self, request, authn=None, **kwargs): """ Registration endpoint. This is where a registration request should be handled. :param request: The request, either as a dictionary or as a JSON document :param authn: Authentication information :param kwargs: Extra key work arguments. :return: A request response or an error response. """ logger.debug("@registration_endpoint: <<{}>>".format( sanitize(request))) if isinstance(request, dict): request = ClientMetadataStatement(**request) else: try: request = ClientMetadataStatement().deserialize( request, "json") except ValueError: request = ClientMetadataStatement().deserialize(request) if not self.is_federation_request(request): return provider.Provider.registration_endpoint(self, request.to_json(), authn=None, **kwargs) try: request.verify() except Exception as err: return error('Invalid request') logger.info("registration_request:{}".format( sanitize(request.to_dict()))) les = self.federation_entity.get_metadata_statement( request, 'registration') if les: ms = self.federation_entity.pick_by_priority(les) self.federation = ms.fo else: # Nothing I can use return error(error='invalid_request', descr='No signed metadata statement I could use') _pc = ms.protected_claims() if _pc: request = RegistrationRequest(**_pc) else: request = RegistrationRequest( **ms.unprotected_and_protected_claims()) result = self.client_registration_setup(request) if 'signed_jwks_uri' in _pc: _kb = KeyBundle(source=_pc['signed_jwks_uri'], verify_keys=ms.signing_keys, verify_ssl=False) _kb.do_remote() replace_jwks_key_bundle(self.keyjar, result['client_id'], _kb) result['signed_jwks_uri'] = _pc['signed_jwks_uri'] if isinstance(result, Response): return result # TODO This is where the OP should sign the response if ms.fo: _fo = ms.fo _sig = self._signer() if _sig: sms = _sig.create_signed_metadata_statement(result, 'response', [_fo], single=True) else: raise SigningServiceError('No Signer') self.federation_entity.extend_with_ms(result, {_fo: sms}) return Created(result.to_json(), content="application/json", headers=[("Cache-Control", "no-store")])
TREQ = AccessTokenRequest(code="code", redirect_uri="http://example.com/authz", client_id=CLIENT_ID) AREQ = AuthorizationRequest(response_type="code", client_id="client_id", redirect_uri="http://example.com/authz", scope=["openid"], state="state0", nonce="N0nce") UIREQ = UserInfoRequest(access_token="access_token") REGREQ = RegistrationRequest(contacts=["*****@*****.**"], redirect_uris=["http://example.org/jqauthz"], application_name="pacubar", client_id=CLIENT_ID, operation="register", application_type="web") RSREQ = RefreshSessionRequest(id_token="id_token", redirect_url="http://example.com/authz", state="state0") #key, type, usage, owner="." alg = "HS256" ktype = alg2keytype(alg) keys = KC_SYM_S.get(ktype) CSREQ = CheckSessionRequest( id_token=IDTOKEN.to_jwt(key=keys, algorithm="HS256"))
def test_full_flow(self, context, frontend): redirect_uri = "https://client.example.com/redirect" response_type = "code id_token token" mock_callback = Mock() frontend.auth_req_callback_func = mock_callback # discovery http_response = frontend.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.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", state="state", nonce="nonce") context.request = dict(parse_qsl(authn_req.to_urlencoded())) frontend.handle_authn_request(context) assert mock_callback.call_count == 1 # fake authentication response from backend internal_response = self.setup_for_authn_response( context, frontend, authn_req) http_response = frontend.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.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.userinfo_endpoint(context) parsed = OpenIDSchema().deserialize(http_response.message, "json") assert "email" in parsed
def test_verify_redirect_uri_native_custom(self): areq = RegistrationRequest( redirect_uris=["com.example.app:/oauth2redirect"], application_type='native') self.provider.verify_redirect_uris(areq)
def test_verify_redirect_uri_native_loopback(self): areq = RegistrationRequest(redirect_uris=["http://127.0.0.1/cb"], application_type='native') self.provider.verify_redirect_uris(areq)
def test_verify_redirect_uri_native_http_localhost(self): areq = RegistrationRequest(redirect_uris=["http://localhost/cb"], application_type='native') self.provider.verify_redirect_uris(areq)
def registration_endpoint(self, request, authn=None, **kwargs): try: reg_req = RegistrationRequest().deserialize(request, "json") except ValueError: reg_req = RegistrationRequest().deserialize(request) self.events.store(EV_PROTOCOL_REQUEST, reg_req) try: response_type_cmp(kwargs['test_cnf']['response_type'], reg_req['response_types']) except KeyError: pass try: provider.Provider.verify_redirect_uris(reg_req) except InvalidRedirectURIError as err: return error(error="invalid_configuration_parameter", descr="Invalid redirect_uri: {}".format(err)) if "initiate_login_uri" in self.behavior_type: if not "initiate_login_uri" in reg_req: return error( error="invalid_configuration_parameter", descr= "No \"initiate_login_uri\" endpoint found in the Client Registration Request\"" ) # Do initial verification that all endpoints from the client uses # https for endp in ["jwks_uri", "initiate_login_uri"]: try: uris = reg_req[endp] except KeyError: continue if not isinstance(uris, list): uris = [uris] for uri in uris: if not uri.startswith("https://"): return error( error="invalid_configuration_parameter", descr="Non-HTTPS endpoint in '{}'".format(endp)) if not "contacts" in reg_req: return error( error="invalid_configuration_parameter", descr="No \"contacts\" claim provided in registration request." ) elif not "@" in reg_req["contacts"][0]: return error( error="invalid_configuration_parameter", descr= "First address in \"contacts\" value in registration request is not a valid e-mail address." ) _response = provider.Provider.registration_endpoint( self, request, authn, **kwargs) self.events.store(EV_HTTP_RESPONSE, _response) self.init_keys = [] if "jwks_uri" in reg_req: if _response.status == "200 OK": # find the client id req_resp = RegistrationResponse().from_json(_response.message) for kb in self.keyjar[req_resp["client_id"]]: if kb.imp_jwks: self.events.store("Client JWKS", kb.imp_jwks) return _response
# ----------------------------------------------------------------------------- # The RPs signing key # ----------------------------------------------------------------------------- rp_jwks, rp_keyjar, _ = build_keyjar(key_conf) print_private_key(rp_keyjar, "RPs signing key") # ----------------------------------------------------------------------------- # -- construct Registration Request to be signed by Developer # ----------------------------------------------------------------------------- rreq = RegistrationRequest(redirect_uris=['https://example.com/rp1/callback'], application_type='web', response_types=['code'], signing_key=rp_jwks['keys'][0], jwks_uri_signed='https://example.com/rp1/jwks.jws') print(70 * "-") print('Client Registration request') print(70 * "-") print_lines( json.dumps(rreq.to_dict(), sort_keys=True, indent=2, separators=(',', ': '))) # ----------------------------------------------------------------------------- # Developer signs Registration Request once per federation # -----------------------------------------------------------------------------
def test_registration_request_with_coupled_encryption_params(self, enc_param): registration_params = {"redirect_uris": ["https://example.com/authz_cb"], enc_param: "RS25asdasd6"} registration_req = RegistrationRequest(**registration_params) with pytest.raises(AssertionError): registration_req.verify()