def test_provider_authenticated_token(): provider = Provider("pyoicserv", sdb.SessionDB(), CDB, FUNCTIONS) _session_db = {} cons = Consumer(_session_db, client_config = CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True cons.response_type = "token" environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] resp = provider.authorization_endpoint(environ, start_response) sid = resp[0][len("FORM with "):] environ2 = create_return_form_env("user", "password", sid) resp2 = provider.authenticated(environ2, start_response) assert len(resp2) == 1 txt = resp2[0] assert "access_token=" in txt assert "token_type=Bearer" in txt
def test_consumer_client_get_access_token_reques(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.client_secret = "secret0" cons.state = "state" cons.redirect_uris = ["https://www.example.com/oic/cb"] resp1 = AuthorizationResponse(code="auth_grant", state="state") cons.parse_response(AuthorizationResponse, resp1.to_urlencoded(), "urlencoded") resp2 = AccessTokenResponse(access_token="token1", token_type="Bearer", expires_in=0, state="state") cons.parse_response(AccessTokenResponse, resp2.to_urlencoded(), "urlencoded") url, body, http_args = cons.get_access_token_request() assert url == "http://localhost:8088/token" print body assert body == ("code=auth_grant&client_secret=secret0&" "grant_type=authorization_code&client_id=number5&" "redirect_uri=https%3A%2F%2Fwww.example.com%2Foic%2Fcb") assert http_args == { 'headers': { 'content-type': 'application/x-www-form-urlencoded' } }
def test_factory(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) sid = stateID("https://example.org/", cons.seed) _state = sid cons._backup(sid) cons.sdb["seed:%s" % cons.seed] = sid kaka = make_cookie(CLIENT_CONFIG["client_id"], _state, cons.seed, expire=360, path="/") _oac = factory(kaka[1], _session_db, CLIENT_CONFIG["client_id"], client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) assert _oac assert _oac.client_id == cons.client_id assert _oac.seed == cons.seed
def __init__(self, outgoing, internal_attributes, config, base_url, name, external_type, user_id_attr): """ :param outgoing: Callback should be called by the module after the authorization in the backend is done. :param internal_attributes: Mapping dictionary between SATOSA internal attribute names and the names returned by underlying IdP's/OP's as well as what attributes the calling SP's and RP's expects namevice. :param config: Configuration parameters for the module. :param base_url: base url of the service :param name: name of the plugin :param external_type: The name for this module in the internal attributes. :type outgoing: (satosa.context.Context, satosa.internal_data.InternalResponse) -> satosa.response.Response :type internal_attributes: dict[string, dict[str, str | list[str]]] :type config: dict[str, dict[str, str] | list[str]] :type base_url: str :type name: str :type external_type: str """ super().__init__(outgoing, internal_attributes, base_url, name) self.config = config self.redirect_url = "%s/%s" % (self.config["base_url"], self.config["authz_page"]) self.external_type = external_type self.user_id_attr = user_id_attr self.consumer = Consumer(session_db=None, client_config=self.config["client_config"], server_info=self.config["server_info"], authz_page=self.config["authz_page"], response_type=self.config["response_type"]) self.consumer.client_secret = self.config["client_secret"]
def test_provider_authenticated_none(): provider = Provider("pyoicserv", sdb.SessionDB(), CDB, FUNCTIONS) _session_db = {} cons = Consumer(_session_db, client_config = CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True cons.response_type = "none" environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] resp = provider.authorization_endpoint(environ, start_response) sid = resp[0][len("FORM with "):] environ2 = create_return_form_env("user", "password", sid) resp2 = provider.authenticated(environ2, start_response) assert len(resp2) == 1 txt = resp2[0] pos0 = txt.index("<title>") + len("<title>Redirecting to ") pos1 = txt.index("</title>") location = txt[pos0:pos1] print location assert location.startswith("http://localhost:8087/authz") query = location.split("?")[1] assert query.startswith("state=") assert "&" not in query
def test_factory(): sdb = DictSessionBackend() consumer = Consumer( sdb, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, settings=CLIENT_SETTINGS, **CONSUMER_CONFIG, ) sid = stateID("https://example.org/", consumer.seed) _state = sid consumer._backup(sid) consumer.sdb["seed:%s" % consumer.seed] = sid kaka = make_cookie(CLIENT_CONFIG["client_id"], _state, consumer.seed, expire=360, path="/") _oac = factory( kaka[1], sdb, CLIENT_CONFIG["client_id"], client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG, ) assert _oac.client_id == consumer.client_id assert _oac.seed == consumer.seed
def test_consumer_parse_access_token(): # implicit flow test _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True environ = BASE_ENVIRON cons.response_type = ["token"] sid, loc = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AccessTokenResponse(access_token="2YotnFZFEjr1zCsicMWpAA", token_type="example", refresh_token="tGzv3JOkF0XG5Qx2TlKWIA", example_parameter="example_value", state=sid) res = cons.handle_authorization_response(query=atr.to_urlencoded()) assert res.type() == "AccessTokenResponse" print cons.grant[sid] grant = cons.grant[sid] assert len(grant.tokens) == 1 token = grant.tokens[0] assert token.access_token == "2YotnFZFEjr1zCsicMWpAA"
def test_provider_authenticated_token(): provider = Provider("pyoicserv", sdb.SessionDB(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, AUTHZ, verify_client, symkey=rndstr(16)) _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True sid, location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization", "token") QUERY_STRING = location.split("?")[1] resp = provider.authorization_endpoint(QUERY_STRING) print resp.headers print resp.message txt = resp.message assert "access_token=" in txt assert "token_type=Bearer" in txt
def test_authenticated(self): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) sid, location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") resp = self.provider.authorization_endpoint(urlparse(location).query) assert resp.status == "303 See Other" resp = urlparse(resp.message).query with LogCapture(level=logging.DEBUG) as logcap: aresp = cons.handle_authorization_response(query=resp) assert isinstance(aresp, AuthorizationResponse) assert _eq(aresp.keys(), ['state', 'code', 'client_id', 'iss']) assert _eq(cons.grant[sid].keys(), ['tokens', 'code', 'exp_in', 'seed', 'id_token', 'grant_expiration_time']) state = aresp['state'] assert _eq(logcap.records[0].msg, '- authorization - code flow -') assert logcap.records[1].msg in expected_outcome( 'QUERY: ', ['state={}'.format(state), 'code=<REDACTED>', 'client_id=client1', 'iss=https%3A%2F%2Fexample.com%2Fas']) expected = {'iss': 'https://example.com/as', 'state': state, 'code': '<REDACTED>', 'client_id': 'client1'} # Eval here to avoid intermittent failures due to dict ordering assert _eq(eval(logcap.records[2].msg[29:-1]), expected) expected = ["'client_id': 'client1'", "'iss': 'https://example.com/as'", "'keyjar': <KeyJar(issuers=[])>"] assert _eq(sorted(logcap.records[3].msg[22:-1].split(', ')), expected)
def test_consumer_parse_access_token(): # implicit flow test _session_db = {} cons = Consumer(_session_db, client_config = CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True environ = BASE_ENVIRON cons.response_type = ["token"] _ = cons.begin(environ, start_response) atr = AccessTokenResponse(access_token="2YotnFZFEjr1zCsicMWpAA", token_type="example", refresh_token="tGzv3JOkF0XG5Qx2TlKWIA", example_parameter="example_value", state=cons.state) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = atr.to_urlencoded() res = cons.handle_authorization_response(environ, start_response) assert res.type() == "AccessTokenResponse" print cons.grant[cons.state] grant = cons.grant[cons.state] assert len(grant.tokens) == 1 token = grant.tokens[0] assert token.access_token == "2YotnFZFEjr1zCsicMWpAA"
def test_provider_authenticated(): provider = Provider("pyoicserv", sdb.SessionDB(), CDB, AUTHN_BROKER, AUTHZ, verify_client, symkey=rndstr(16)) _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") query_string = location.split("?")[1] resp = provider.authorization_endpoint(query_string) assert resp.status == "302 Found" print resp.headers print resp.message if content_type(resp.headers) == "json": resp = resp.message else: resp = resp.message.split("?")[1] aresp = cons.handle_authorization_response(query=resp) print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), ['state', 'code']) print cons.grant[cons.state].keys() assert _eq(cons.grant[cons.state].keys(), ['tokens', 'code', 'exp_in', 'seed', 'id_token', 'grant_expiration_time'])
def create_consumer(self): self.consumer = Consumer( DictSessionBackend(), client_config=CLIENT_CONFIG, server_info=SERVER_INFO, settings=CLIENT_SETTINGS, **CONSUMER_CONFIG, )
def test_consumer_client_auth_info(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.client_secret = "secret0" ra, ha, extra = cons.client_auth_info() assert ra == {'client_secret': 'secret0', 'client_id': 'number5'} assert ha == {} assert extra == {'auth_method': 'bearer_body'}
def handle_authz_response(environ, start_response): _cli = Consumer(SESSION_DB, CLIENT_CONFIG, SERVER_INFO, **CONSUMER_CONFIG) aresp = _cli.parse_authz(environ, start_response, DEVNULL()) print "ARESP: %s" % aresp kaka = http_util.cookie(CLIENT_CONFIG["client_id"], aresp.state, _cli.seed, expire=360, path="/") resp = http_util.Response("Your will is registered", headers=[kaka]) return resp(environ, start_response)
def test_provider_config(self): c = Consumer(None, None) response = ASConfigurationResponse(**{'issuer': 'https://example.com', 'end_session_endpoint': 'https://example.com/end_session'}) with responses.RequestsMock() as rsps: rsps.add(responses.GET, 'https://example.com/.well-known/openid-configuration', json=response.to_dict()) info = c.provider_config('https://example.com') assert isinstance(info, ASConfigurationResponse) assert _eq(info.keys(), ['issuer', 'version', 'end_session_endpoint']) assert info["end_session_endpoint"] == "https://example.com/end_session"
def test_consumer_client_get_access_token_reques(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.client_secret = "secret0" _state = "state" cons.redirect_uris = ["https://www.example.com/oic/cb"] resp1 = AuthorizationResponse(code="auth_grant", state=_state) cons.parse_response(AuthorizationResponse, resp1.to_urlencoded(), "urlencoded") resp2 = AccessTokenResponse(access_token="token1", token_type="Bearer", expires_in=0, state=_state) cons.parse_response(AccessTokenResponse, resp2.to_urlencoded(), "urlencoded") url, body, http_args = cons.get_access_token_request(_state) url_obj = URLObject.create(url) expected_url_obj = URLObject.create("http://localhost:8088/token") assert url_obj == expected_url_obj body_splits = body.split('&') expected_body_splits = "code=auth_grant&client_secret=secret0&" \ "grant_type=authorization_code&client_id=number5&" \ "redirect_uri=https%3A%2F%2Fwww.example.com%2Foic%2Fcb".split('&') assert set(body_splits) == set(expected_body_splits) assert http_args == { 'headers': { 'Content-type': 'application/x-www-form-urlencoded' } }
def test_consumer_client_get_access_token_reques(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.client_secret = "secret0" cons.state = "state" cons.redirect_uris = ["https://www.example.com/oic/cb"] resp1 = AuthorizationResponse(code="auth_grant", state="state") cons.parse_response(AuthorizationResponse, resp1.to_urlencoded(), "urlencoded") resp2 = AccessTokenResponse(access_token="token1", token_type="Bearer", expires_in=0, state="state") cons.parse_response(AccessTokenResponse, resp2.to_urlencoded(), "urlencoded") url, body, http_args = cons.get_access_token_request() assert url == "http://localhost:8088/token" print body assert body == ("code=auth_grant&client_secret=secret0&" "grant_type=authorization_code&client_id=number5&" "redirect_uri=https%3A%2F%2Fwww.example.com%2Foic%2Fcb") assert http_args == {'headers': { 'content-type': 'application/x-www-form-urlencoded'}}
def test_authenticated(self): _session_db = DictSessionBackend() cons = Consumer( _session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG, ) sid, location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") resp = self.provider.authorization_endpoint(urlparse(location).query) assert resp.status_code == 303 resp = urlparse(resp.message).query with LogCapture(level=logging.DEBUG) as logcap: aresp = cons.handle_authorization_response(query=resp) assert isinstance(aresp, AuthorizationResponse) assert _eq(aresp.keys(), ["state", "code", "client_id", "iss"]) assert _eq( cons.grant[sid].keys(), [ "tokens", "code", "exp_in", "seed", "id_token", "grant_expiration_time" ], ) state = aresp["state"] assert _eq(logcap.records[0].msg, "- authorization - code flow -") assert verify_outcome( logcap.records[1].msg, "QUERY: ", [ "state={}".format(state), "code=<REDACTED>", "client_id=client1", "iss=https://example.com/as", ], ) expected = { "iss": "https://example.com/as", "state": state, "code": "<REDACTED>", "client_id": "client1", } # Eval here to avoid intermittent failures due to dict ordering assert _eq(eval(logcap.records[2].msg[29:-1]), expected) expected2 = [ "'client_id': 'client1'", "'iss': 'https://example.com/as'", "'keyjar': <KeyJar(issuers=[])>", ] assert _eq(sorted(logcap.records[3].msg[22:-1].split(", ")), expected2)
def test_init(self): cons = Consumer({}, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons._backup("123456") assert "123456" in cons.sdb cons = Consumer({}, client_config=CLIENT_CONFIG, **CONSUMER_CONFIG) assert cons.authorization_endpoint is None cons = Consumer({}, **CONSUMER_CONFIG) assert cons.authorization_endpoint is None
def test_authenticated_token(self): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) sid, location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization", "token") QUERY_STRING = location.split("?")[1] resp = self.provider.authorization_endpoint(QUERY_STRING) auth_resp = parse_qs(urlparse(resp.message).fragment) assert "access_token" in auth_resp assert auth_resp["token_type"][0] == "Bearer"
def test_consumer_parse_authz_error_2(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True _ = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = TokenErrorResponse(error="invalid_client") QUERY_STRING = atr.to_urlencoded() raises(AuthzError, "cons.handle_authorization_response(query=QUERY_STRING)")
def test_consumer_client_get_access_token_reques(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.client_secret = "secret0" _state = "state" cons.redirect_uris = ["https://www.example.com/oic/cb"] resp1 = AuthorizationResponse(code="auth_grant", state=_state) cons.parse_response(AuthorizationResponse, resp1.to_urlencoded(), "urlencoded") resp2 = AccessTokenResponse(access_token="token1", token_type="Bearer", expires_in=0, state=_state) cons.parse_response(AccessTokenResponse, resp2.to_urlencoded(), "urlencoded") url, body, http_args = cons.get_access_token_request(_state) url_obj = URLObject.create(url) expected_url_obj = URLObject.create("http://localhost:8088/token") assert url_obj == expected_url_obj body_splits = body.split('&') expected_body_splits = "code=auth_grant&client_secret=secret0&" \ "grant_type=authorization_code&client_id=number5&" \ "redirect_uri=https%3A%2F%2Fwww.example.com%2Foic%2Fcb".split('&') assert set(body_splits) == set(expected_body_splits) assert http_args == {'headers': { 'Content-type': 'application/x-www-form-urlencoded'}}
def test_authenticated(self): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) sid, location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") resp = self.provider.authorization_endpoint(urlparse(location).query) assert resp.status == "302 Found" resp = urlparse(resp.message).query aresp = cons.handle_authorization_response(query=resp) assert isinstance(aresp, AuthorizationResponse) assert _eq(aresp.keys(), ["state", "code"]) assert _eq(cons.grant[sid].keys(), ["tokens", "code", "exp_in", "seed", "id_token", "grant_expiration_time"])
def test_provider_authenticated_token(): provider = Provider("pyoicserv", sdb.SessionDB(), CDB, AUTHN_BROKER, AUTHZ, verify_client) _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization", "token") QUERY_STRING = location.split("?")[1] resp = provider.authorization_endpoint(QUERY_STRING) print resp.headers print resp.message txt = resp.message assert "access_token=" in txt assert "token_type=Bearer" in txt
def register(environ, start_response, logger, kaka=None): """ Initialize the OAuth2 flow """ _session_db = environ["oic.sessiondb"] _cc = environ["oic.client_config"] _conc = environ["oic.consumer.config"] _server_info = environ["oic.server.info"] # get the redirect to the authorization server endpoint _oac = Consumer(_session_db, _conc, _cc, _server_info) location = _oac.begin(environ, start_response, logger) resp = http_util.Redirect(location) return resp(environ, start_response)
def test_consumer_parse_authz_error_2(): _session_db = {} cons = Consumer(_session_db, client_config = CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True environ = BASE_ENVIRON _ = cons.begin(environ, start_response) atr = TokenErrorResponse(error="invalid_client") environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = atr.to_urlencoded() raises(AuthzError, "cons.handle_authorization_response(environ, start_response)")
def __init__(self, outgoing, internal_attributes, config, base_url, name, external_type, user_id_attr): """ :param outgoing: Callback should be called by the module after the authorization in the backend is done. :param internal_attributes: Mapping dictionary between SATOSA internal attribute names and the names returned by underlying IdP's/OP's as well as what attributes the calling SP's and RP's expects namevice. :param config: Configuration parameters for the module. :param base_url: base url of the service :param name: name of the plugin :param external_type: The name for this module in the internal attributes. :type outgoing: (satosa.context.Context, satosa.internal_data.InternalResponse) -> satosa.response.Response :type internal_attributes: dict[string, dict[str, str | list[str]]] :type config: dict[str, dict[str, str] | list[str]] :type base_url: str :type name: str :type external_type: str """ super().__init__(outgoing, internal_attributes, base_url, name) self.config = config self.redirect_url = "%s/%s" % (self.config["base_url"], self.config["authz_page"]) self.external_type = external_type self.user_id_attr = user_id_attr self.consumer = Consumer( session_db=None, client_config=self.config["client_config"], server_info=self.config["server_info"], authz_page=self.config["authz_page"], response_type=self.config["response_type"]) self.consumer.client_secret = self.config["client_secret"]
def test_provider_authenticated(): provider = Provider("pyoicserv", sdb.SessionDB(), CDB, FUNCTIONS) _session_db = {} cons = Consumer(_session_db, client_config = CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] resp = provider.authorization_endpoint(environ, start_response) sid = resp[0][len("FORM with "):] environ2 = create_return_form_env("user", "password", sid) resp2 = provider.authenticated(environ2, start_response) print resp2[0] assert len(resp2) == 1 txt = resp2[0] pos0 = txt.index("<title>") + len("<title>Redirecting to ") pos1 = txt.index("</title>") location = txt[pos0:pos1] print location assert location.startswith("http://localhost:8087/authz") environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] aresp = cons.handle_authorization_response(environ, start_response) #aresp = client.parse_response(AuthorizationResponse, location, # format="urlencoded", # state="id-6da9ca0cc23959f5f33e8becd9b08cae") print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), ['state', 'code']) print cons.grant[cons.state].keys() assert _eq(cons.grant[cons.state].keys(), ['tokens', 'code', 'exp_in', 'seed', 'id_token', 'grant_expiration_time'])
def authz(environ, start_response, logger, kaka=None): """ This is where I get back to after authentication at the Authorization service """ _session_db = environ["oic.sessiondb"] _cc = environ["oic.client_config"] _conc = environ["oic.consumer.config"] _server_info = environ["oic.server.info"] _log_info = logger.info try: _cli = Consumer(_session_db, _conc, _cc, _server_info) response = _cli.parse_authz(environ, start_response, logger) except (AuthzError, TokenError), err: resp = http_util.Unauthorized("%s" % err) return resp(environ, start_response)
def test_consumer_parse_authz_exception(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True sid, loc = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AuthorizationResponse(code="SplxlOBeZQQYbYS6WxSbIA", state=sid) adict = atr.to_dict() del adict["code"] QUERY_STRING = urllib.urlencode(adict) raises(MissingRequiredAttribute, "cons.handle_authorization_response(query=QUERY_STRING)")
def get_consumer(self): """ Creates a OAuth 2.0 consumer from a given configuration. :param user_id_hash_type: Tells the OAuth consumer how to ask for user id. In oidc can pairwise and public be used. :type user_id_hash_type: UserIdHashType :rtype: Consumer :return: An OAuth 2.0 consumer. """ consumer = Consumer(session_db=None, client_config=self.config["client_config"], server_info=self.config["server_info"], authz_page=self.config["authz_page"], response_type=self.config["response_type"]) consumer.client_secret = self.config["client_secret"] return consumer
def test_consumer_handle_authorization_response(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True sid, loc = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AuthorizationResponse(code="SplxlOBeZQQYbYS6WxSbIA", state=sid) res = cons.handle_authorization_response(query=atr.to_urlencoded()) assert res.type() == "AuthorizationResponse" print cons.grant[sid] grant = cons.grant[sid] assert grant.code == "SplxlOBeZQQYbYS6WxSbIA"
def test_consumer_begin(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) sid, loc = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") # state is dynamic params = {"scope": "openid", "state": sid, "redirect_uri": "http://localhost:8087/authz", "response_type": "code", "client_id": "number5"} url = "http://localhost:8088/authorization?%s" % urllib.urlencode(params) assert loc == url
def test_factory(): sdb = {} consumer = Consumer(sdb, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) sid = stateID("https://example.org/", consumer.seed) _state = sid consumer._backup(sid) consumer.sdb["seed:%s" % consumer.seed] = sid kaka = make_cookie(CLIENT_CONFIG["client_id"], _state, consumer.seed, expire=360, path="/") _oac = factory(kaka[1], sdb, CLIENT_CONFIG["client_id"], client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) assert _oac.client_id == consumer.client_id assert _oac.seed == consumer.seed
def test_provider_config(self): c = Consumer(None, None) response = ASConfigurationResponse( **{ "issuer": "https://example.com", "end_session_endpoint": "https://example.com/end_session", }) with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/.well-known/openid-configuration", json=response.to_dict(), ) info = c.provider_config("https://example.com") assert isinstance(info, ASConfigurationResponse) assert _eq(info.keys(), ["issuer", "version", "end_session_endpoint"]) assert info[ "end_session_endpoint"] == "https://example.com/end_session"
def test_authenticated(self): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) sid, location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") resp = self.provider.authorization_endpoint(urlparse(location).query) assert resp.status_code == 303 resp = urlparse(resp.message).query with LogCapture(level=logging.DEBUG) as logcap: aresp = cons.handle_authorization_response(query=resp) assert isinstance(aresp, AuthorizationResponse) assert _eq(aresp.keys(), ['state', 'code', 'client_id', 'iss']) assert _eq(cons.grant[sid].keys(), [ 'tokens', 'code', 'exp_in', 'seed', 'id_token', 'grant_expiration_time' ]) state = aresp['state'] assert _eq(logcap.records[0].msg, '- authorization - code flow -') assert verify_outcome(logcap.records[1].msg, 'QUERY: ', [ 'state={}'.format(state), 'code=<REDACTED>', 'client_id=client1', 'iss=https://example.com/as' ]) expected = { 'iss': 'https://example.com/as', 'state': state, 'code': '<REDACTED>', 'client_id': 'client1' } # Eval here to avoid intermittent failures due to dict ordering assert _eq(eval(logcap.records[2].msg[29:-1]), expected) expected = [ "'client_id': 'client1'", "'iss': 'https://example.com/as'", "'keyjar': <KeyJar(issuers=[])>" ] assert _eq(sorted(logcap.records[3].msg[22:-1].split(', ')), expected)
def get_consumer(self): """ Creates a OAuth 2.0 consumer from a given configuration. :param user_id_hash_type: Tells the OAuth consumer how to ask for user id. In oidc can pairwise and public be used. :type user_id_hash_type: UserIdHashType :rtype: Consumer :return: An OAuth 2.0 consumer. """ consumer = Consumer( session_db=None, client_config=self.config["client_config"], server_info=self.config["server_info"], authz_page=self.config["authz_page"], response_type=self.config["response_type"]) consumer.client_secret = self.config["client_secret"] return consumer
def test_consumer_parse_authz_exception(): _session_db = {} cons = Consumer(_session_db, client_config = CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True environ = BASE_ENVIRON _ = cons.begin(environ, start_response) atr = AuthorizationResponse(code="SplxlOBeZQQYbYS6WxSbIA", state=cons.state) adict = atr.to_dict() del adict["code"] environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = urllib.urlencode(adict) raises(MissingRequiredAttribute, "cons.handle_authorization_response(environ, start_response)")
def test_provider_authenticated_1(): provider = Provider("pyoicserv", sdb.SessionDB(), CDB, FUNCTIONS) _session_db = {} cons = Consumer(_session_db, client_config = CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True environ = BASE_ENVIRON location = cons.begin(environ, start_response) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = location.split("?")[1] _ = provider.authorization_endpoint(environ, start_response) #sid = resp[0][len("FORM with "):] environ2 = create_return_form_env("user", "password", "abcd") resp2 = provider.authenticated(environ2, start_response) print resp2 assert resp2 == ['<html>Unknown session identifier</html>']
def test_consumer_handle_authorization_response(): _session_db = {} cons = Consumer(_session_db, client_config = CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True environ = BASE_ENVIRON _ = cons.begin(environ, start_response) atr = AuthorizationResponse(code="SplxlOBeZQQYbYS6WxSbIA", state=cons.state) environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = atr.to_urlencoded() res = cons.handle_authorization_response(environ, start_response) assert res.type() == "AuthorizationResponse" print cons.grant[cons.state] grant = cons.grant[cons.state] assert grant.code == "SplxlOBeZQQYbYS6WxSbIA"
def test_authenticated(self): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) sid, location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") resp = self.provider.authorization_endpoint(urlparse(location).query) assert resp.status == "302 Found" resp = urlparse(resp.message).query aresp = cons.handle_authorization_response(query=resp) assert isinstance(aresp, AuthorizationResponse) assert _eq(aresp.keys(), ['state', 'code']) assert _eq(cons.grant[sid].keys(), [ 'tokens', 'code', 'exp_in', 'seed', 'id_token', 'grant_expiration_time' ])
def test_consumer_parse_access_token(): # implicit flow test _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True environ = BASE_ENVIRON cons.response_type = ["token"] _ = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AccessTokenResponse(access_token="2YotnFZFEjr1zCsicMWpAA", token_type="example", refresh_token="tGzv3JOkF0XG5Qx2TlKWIA", example_parameter="example_value", state=cons.state) res = cons.handle_authorization_response(query=atr.to_urlencoded()) assert res.type() == "AccessTokenResponse" print cons.grant[cons.state] grant = cons.grant[cons.state] assert len(grant.tokens) == 1 token = grant.tokens[0] assert token.access_token == "2YotnFZFEjr1zCsicMWpAA"
def test_consumer_begin(): _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) loc = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") # state is dynamic params = { "scope": "openid", "state": cons.state, "redirect_uri": "http://localhost:8087/authz", "response_type": "code", "client_id": "number5" } url = "http://localhost:8088/authorization?%s" % urllib.urlencode(params) assert loc == url
def test_provider_authenticated(): provider = Provider("pyoicserv", sdb.SessionDB(), CDB, AUTHN_BROKER, AUTHZ, verify_client, symkey=rndstr(16)) _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization") query_string = location.split("?")[1] resp = provider.authorization_endpoint(query_string) assert resp.status == "302 Found" print resp.headers print resp.message if content_type(resp.headers) == "json": resp = resp.message else: resp = resp.message.split("?")[1] aresp = cons.handle_authorization_response(query=resp) print aresp.keys() assert aresp.type() == "AuthorizationResponse" assert _eq(aresp.keys(), ['state', 'code']) print cons.grant[cons.state].keys() assert _eq(cons.grant[cons.state].keys(), [ 'tokens', 'code', 'exp_in', 'seed', 'id_token', 'grant_expiration_time' ])
def test_provider_authenticated_none(): provider = Provider("pyoicserv", sdb.SessionDB(), CDB, AUTHN, AUTHZ, verify_client) _session_db = {} cons = Consumer(_session_db, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons.debug = True location = cons.begin("http://localhost:8087", "http://localhost:8088/authorization", "none") QUERY_STRING = location.split("?")[1] resp2 = provider.authorization_endpoint(request=QUERY_STRING) location = resp2.message print location assert location.startswith("http://localhost:8087/authz") query = location.split("?")[1] assert query.startswith("state=") assert "&" not in query
def test_init(self): cons = Consumer( DictSessionBackend(), client_config=CLIENT_CONFIG, server_info=SERVER_INFO, settings=CLIENT_SETTINGS, **CONSUMER_CONFIG, ) cons._backup("123456") assert "123456" in cons.sdb cons = Consumer( DictSessionBackend(), client_config=CLIENT_CONFIG, settings=CLIENT_SETTINGS, **CONSUMER_CONFIG, ) assert cons.authorization_endpoint is None cons = Consumer(DictSessionBackend, **CONSUMER_CONFIG) assert cons.authorization_endpoint is None
def register(environ, start_response): _oac = Consumer(SESSION_DB, CLIENT_CONFIG, SERVER_INFO, **CONSUMER_CONFIG) location = _oac.begin(environ, start_response, DEVNULL()) resp = http_util.Redirect(location) return resp(environ, start_response)
class TestConsumer(object): @pytest.fixture(autouse=True) def create_consumer(self): self.consumer = Consumer({}, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) def test_init(self): cons = Consumer({}, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG) cons._backup("123456") assert "123456" in cons.sdb cons = Consumer({}, client_config=CLIENT_CONFIG, **CONSUMER_CONFIG) assert cons.authorization_endpoint is None cons = Consumer({}, **CONSUMER_CONFIG) assert cons.authorization_endpoint is None def test_begin(self): sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") # state is dynamic params = { "scope": "openid", "state": sid, "redirect_uri": "http://localhost:8087/authz", "response_type": "code", "client_id": "number5" } url = "http://localhost:8088/authorization?{}".format( urlencode(params)) assert url_compare(loc, url) def test_handle_authorization_response(self): sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AuthorizationResponse(code="SplxlOBeZQQYbYS6WxSbIA", state=sid) res = self.consumer.handle_authorization_response( query=atr.to_urlencoded()) assert isinstance(res, AuthorizationResponse) assert self.consumer.grant[sid].code == "SplxlOBeZQQYbYS6WxSbIA" def test_parse_authz_without_code(self): sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AuthorizationResponse(code="SplxlOBeZQQYbYS6WxSbIA", state=sid) adict = atr.to_dict() del adict["code"] with pytest.raises(MissingRequiredAttribute): self.consumer.handle_authorization_response(query=urlencode(adict)) def test_parse_authz_access_denied(self): sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AuthorizationErrorResponse(error="access_denied", state=sid) with pytest.raises(AuthzError): self.consumer.handle_authorization_response( query=atr.to_urlencoded()) def test_parse_access_token(self): # implicit flow test self.consumer.response_type = ["token"] sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AccessTokenResponse(access_token="2YotnFZFEjr1zCsicMWpAA", token_type="example", refresh_token="tGzv3JOkF0XG5Qx2TlKWIA", example_parameter="example_value", state=sid) res = self.consumer.handle_authorization_response( query=atr.to_urlencoded()) assert isinstance(res, AccessTokenResponse) grant = self.consumer.grant[sid] assert len(grant.tokens) == 1 token = grant.tokens[0] assert token.access_token == "2YotnFZFEjr1zCsicMWpAA" def test_parse_authz_invalid_client(self): self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = TokenErrorResponse(error="invalid_client") with pytest.raises(AuthzError): self.consumer.handle_authorization_response( query=atr.to_urlencoded()) def test_consumer_client_auth_info(self): self.consumer.client_secret = "secret0" ra, ha, extra = self.consumer.client_auth_info() assert ra == {'client_secret': 'secret0', 'client_id': 'number5'} assert ha == {} assert extra == {'auth_method': 'bearer_body'} def test_client_get_access_token_request(self): self.consumer.client_secret = "secret0" _state = "state" self.consumer.redirect_uris = ["https://www.example.com/oic/cb"] resp1 = AuthorizationResponse(code="auth_grant", state=_state) self.consumer.parse_response(AuthorizationResponse, resp1.to_urlencoded(), "urlencoded") resp2 = AccessTokenResponse(access_token="token1", token_type="Bearer", expires_in=0, state=_state) self.consumer.parse_response(AccessTokenResponse, resp2.to_urlencoded(), "urlencoded") url, body, http_args = self.consumer.get_access_token_request(_state) assert url_compare(url, "http://localhost:8088/token") expected_params = 'redirect_uri=https%3A%2F%2Fwww.example.com%2Foic%2Fcb&client_id=number5&state=state&' \ 'code=auth_grant&grant_type=authorization_code&client_secret=secret0' assert query_string_compare(body, expected_params) assert http_args == { 'headers': { 'Content-Type': 'application/x-www-form-urlencoded' } }
def create_consumer(self): self.consumer = Consumer({}, client_config=CLIENT_CONFIG, server_info=SERVER_INFO, **CONSUMER_CONFIG)
# is run. sys.path.insert(0, ".") # If a specific configuration directory is specified look there first if args.conf_path: sys.path.insert(0, args.conf_path) RP_CONF = importlib.import_module(args.config) # per AS instantiate a consumer for name, info in RP_CONF.AS_CONF.items(): c_conf = {"client_id": info["client_id"]} CONSUMER[name] = Consumer(session_db={}, client_config=c_conf, server_info={ "authorization_endpoint": info["authorization_endpoint"], "token_endpoint": info["token_endpoint"] }, authz_page="authz_cb", response_type="code") CONSUMER[name].client_secret = info["client_secret"] SRV = wsgiserver.CherryPyWSGIServer( ('0.0.0.0', RP_CONF.PORT), SessionMiddleware(application, session_opts)) if RP_CONF.BASE.startswith("https"): from cherrypy.wsgiserver import ssl_pyopenssl SRV.ssl_adapter = ssl_pyopenssl.pyOpenSSLAdapter(
class _OAuthBackend(BackendModule): """ Backend module for OAuth 2.0, should not be directly used. See satosa.backends.oauth.FacebookBackend. """ def __init__(self, outgoing, internal_attributes, config, base_url, name, external_type, user_id_attr): """ :param outgoing: Callback should be called by the module after the authorization in the backend is done. :param internal_attributes: Mapping dictionary between SATOSA internal attribute names and the names returned by underlying IdP's/OP's as well as what attributes the calling SP's and RP's expects namevice. :param config: Configuration parameters for the module. :param base_url: base url of the service :param name: name of the plugin :param external_type: The name for this module in the internal attributes. :type outgoing: (satosa.context.Context, satosa.internal.InternalData) -> satosa.response.Response :type internal_attributes: dict[string, dict[str, str | list[str]]] :type config: dict[str, dict[str, str] | list[str]] :type base_url: str :type name: str :type external_type: str """ super().__init__(outgoing, internal_attributes, base_url, name) self.config = config self.redirect_url = "%s/%s" % (self.config["base_url"], self.config["authz_page"]) self.external_type = external_type self.user_id_attr = user_id_attr self.consumer = Consumer(session_db=None, client_config=self.config["client_config"], server_info=self.config["server_info"], authz_page=self.config["authz_page"], response_type=self.config["response_type"]) self.consumer.client_secret = self.config["client_secret"] def start_auth(self, context, internal_request, get_state=stateID): """ See super class method satosa.backends.base#start_auth :param get_state: Generates a state to be used in the authentication call. :type get_state: Callable[[str, bytes], str] :type context: satosa.context.Context :type internal_request: satosa.internal.InternalData :rtype satosa.response.Redirect """ request_args = self.get_request_args(get_state=get_state) context.state[self.name] = {"state": request_args["state"]} cis = self.consumer.construct_AuthorizationRequest( request_args=request_args) return Redirect(cis.request(self.consumer.authorization_endpoint)) def get_request_args(self, get_state=stateID): oauth_state = get_state(self.config["base_url"], rndstr().encode()) request_args = { "redirect_uri": self.redirect_url, "state": oauth_state, } return request_args def register_endpoints(self): """ Creates a list of all the endpoints this backend module needs to listen to. In this case it's the authentication response from the underlying OP that is redirected from the OP to the proxy. :rtype: Sequence[(str, Callable[[satosa.context.Context], satosa.response.Response]] :return: A list that can be used to map the request to SATOSA to this endpoint. """ return [("^%s$" % self.config["authz_page"], self._authn_response)] def _verify_state(self, resp, state_data, state): """ Will verify the state and throw and error if the state is invalid. :type resp: AuthorizationResponse :type state_data: dict[str, str] :type state: satosa.state.State :param resp: The authorization response from the AS, created by pyoidc. :param state_data: The state data for this backend. :param state: The current state for the proxy and this backend. Only used for raising errors. """ is_known_state = "state" in resp and "state" in state_data and resp[ "state"] == state_data["state"] if not is_known_state: received_state = resp.get("state", "") msg = "Missing or invalid state [{}] in response!".format( received_state) logline = lu.LOG_FMT.format(id=lu.get_session_id(state), message=msg) logger.debug(logline) raise SATOSAAuthenticationError( state, "Missing or invalid state [%s] in response!" % received_state) def _authn_response(self, context): """ Handles the authentication response from the AS. :type context: satosa.context.Context :rtype: satosa.response.Response :param context: The context in SATOSA :return: A SATOSA response. This method is only responsible to call the callback function which generates the Response object. """ state_data = context.state[self.name] aresp = self.consumer.parse_response(AuthorizationResponse, info=json.dumps(context.request)) self._verify_state(aresp, state_data, context.state) rargs = { "code": aresp["code"], "redirect_uri": self.redirect_url, "state": state_data["state"] } atresp = self.consumer.do_access_token_request(request_args=rargs, state=aresp["state"]) if "verify_accesstoken_state" not in self.config or self.config[ "verify_accesstoken_state"]: self._verify_state(atresp, state_data, context.state) user_info = self.user_information(atresp["access_token"]) internal_response = InternalData( auth_info=self.auth_info(context.request)) internal_response.attributes = self.converter.to_internal( self.external_type, user_info) internal_response.subject_id = user_info[self.user_id_attr] del context.state[self.name] return self.auth_callback_func(context, internal_response) def auth_info(self, request): """ Creates the SATOSA authentication information object. :type request: dict[str, str] :rtype: AuthenticationInformation :param request: The request parameters in the authentication response sent by the AS. :return: How, who and when the autentication took place. """ raise NotImplementedError( "Method 'auth_info' must be implemented in the subclass!") def user_information(self, access_token): """ Will retrieve the user information data for the authenticated user. :type access_token: str :rtype: dict[str, str] :param access_token: The access token to be used to retrieve the data. :return: Dictionary with attribute name as key and attribute value as value. """ raise NotImplementedError( "Method 'user_information' must be implemented in the subclass!") def get_metadata_desc(self): """ See satosa.backends.oauth.get_metadata_desc :rtype: satosa.metadata_creation.description.MetadataDescription """ return get_metadata_desc_for_oauth_backend( self.config["server_info"]["authorization_endpoint"], self.config)
class TestConsumer(object): @pytest.fixture(autouse=True) def create_consumer(self): self.consumer = Consumer( DictSessionBackend(), client_config=CLIENT_CONFIG, server_info=SERVER_INFO, settings=CLIENT_SETTINGS, **CONSUMER_CONFIG, ) def test_init(self): cons = Consumer( DictSessionBackend(), client_config=CLIENT_CONFIG, server_info=SERVER_INFO, settings=CLIENT_SETTINGS, **CONSUMER_CONFIG, ) cons._backup("123456") assert "123456" in cons.sdb cons = Consumer( DictSessionBackend(), client_config=CLIENT_CONFIG, settings=CLIENT_SETTINGS, **CONSUMER_CONFIG, ) assert cons.authorization_endpoint is None cons = Consumer(DictSessionBackend, **CONSUMER_CONFIG) assert cons.authorization_endpoint is None def test_begin(self): sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") # state is dynamic params = { "scope": "openid", "state": sid, "redirect_uri": "http://localhost:8087/authz", "response_type": "code", "client_id": "number5", } url = "http://localhost:8088/authorization?{}".format( urlencode(params)) assert url_compare(loc, url) def test_handle_authorization_response(self): sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AuthorizationResponse(code="SplxlOBeZQQYbYS6WxSbIA", state=sid) res = self.consumer.handle_authorization_response( query=atr.to_urlencoded()) assert isinstance(res, AuthorizationResponse) assert self.consumer.grant[sid].code == "SplxlOBeZQQYbYS6WxSbIA" def test_parse_authz_without_code(self): sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AuthorizationResponse(code="SplxlOBeZQQYbYS6WxSbIA", state=sid) adict = atr.to_dict() del adict["code"] with pytest.raises(MissingRequiredAttribute): self.consumer.handle_authorization_response(query=urlencode(adict)) def test_parse_authz_access_denied(self): sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AuthorizationErrorResponse(error="access_denied", state=sid) with pytest.raises(AuthzError): self.consumer.handle_authorization_response( query=atr.to_urlencoded()) def test_parse_access_token(self): # implicit flow test self.consumer.response_type = ["token"] sid, loc = self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = AccessTokenResponse( access_token="2YotnFZFEjr1zCsicMWpAA", token_type="example", refresh_token="tGzv3JOkF0XG5Qx2TlKWIA", example_parameter="example_value", state=sid, ) res = self.consumer.handle_authorization_response( query=atr.to_urlencoded()) assert isinstance(res, AccessTokenResponse) grant = self.consumer.grant[sid] assert len(grant.tokens) == 1 token = grant.tokens[0] assert token.access_token == "2YotnFZFEjr1zCsicMWpAA" def test_parse_authz_invalid_client(self): self.consumer.begin("http://localhost:8087", "http://localhost:8088/authorization") atr = TokenErrorResponse(error="invalid_client") with pytest.raises(AuthzError): self.consumer.handle_authorization_response( query=atr.to_urlencoded()) def test_consumer_client_auth_info(self): self.consumer.client_secret = "secret0" ra, ha, extra = self.consumer.client_auth_info() assert ra == {"client_secret": "secret0", "client_id": "number5"} assert ha == {} assert extra == {"auth_method": "bearer_body"} def test_provider_config(self): c = Consumer(None, None) response = ASConfigurationResponse( **{ "issuer": "https://example.com", "end_session_endpoint": "https://example.com/end_session", }) with responses.RequestsMock() as rsps: rsps.add( responses.GET, "https://example.com/.well-known/openid-configuration", json=response.to_dict(), ) info = c.provider_config("https://example.com") assert isinstance(info, ASConfigurationResponse) assert _eq(info.keys(), ["issuer", "version", "end_session_endpoint"]) assert info[ "end_session_endpoint"] == "https://example.com/end_session" def test_client_get_access_token_request(self): self.consumer.client_secret = "secret0" _state = "state" self.consumer.redirect_uris = ["https://www.example.com/oic/cb"] resp1 = AuthorizationResponse(code="auth_grant", state=_state) self.consumer.parse_response(AuthorizationResponse, resp1.to_urlencoded(), "urlencoded") resp2 = AccessTokenResponse(access_token="token1", token_type="Bearer", expires_in=0, state=_state) self.consumer.parse_response(AccessTokenResponse, resp2.to_urlencoded(), "urlencoded") url, body, http_args = self.consumer.get_access_token_request(_state) assert url_compare(url, "http://localhost:8088/token") expected_params = ( "redirect_uri=https%3A%2F%2Fwww.example.com%2Foic%2Fcb&client_id=number5&state=state&" "code=auth_grant&grant_type=authorization_code&client_secret=secret0" ) assert query_string_compare(body, expected_params) assert http_args == { "headers": { "Content-Type": "application/x-www-form-urlencoded" } } def test_access_token_storage_with_custom_response_class(self): _state = "state" # AccessTokenResponse custom class class AccessTokenResponseWrapper(AccessTokenResponse): """Response wrapper to get "expires_in" in hours.""" c_param = AccessTokenResponse.c_param.copy() c_param.update({"expires_in_hours": SINGLE_OPTIONAL_INT}) def __init__(self, **kwargs): super(AccessTokenResponseWrapper, self).__init__(**kwargs) if "expires_in" in self and self["expires_in"]: self["expires_in_hours"] = self["expires_in"] // 3600 resp = AccessTokenResponseWrapper( access_token="2YotnFZFEjr1zCsiAB", token_type="Bearer", expires_in=3600, state=_state, ) self.consumer.parse_response(AccessTokenResponseWrapper, resp.to_urlencoded(), "urlencoded") grant = self.consumer.grant[_state] assert len(grant.tokens) == 1 assert grant.tokens[0].access_token == "2YotnFZFEjr1zCsiAB" assert grant.tokens[ 0].token_expiration_time > time_util.time_sans_frac() assert grant.tokens[0].expires_in_hours == 1 # type: ignore