def phaseN(self, environ, info, server_env, sid): session = server_env["CACHE"][sid] callback = server_env["base_url"] + self.social_endpoint client = Client(client_id=self.client_id, client_authn_method=CLIENT_AUTHN_METHOD) response = client.parse_response(AuthorizationResponse, info, "dict") logger.info("Response: %s" % response) if isinstance(response, ErrorResponse): logger.info("%s" % response) session["authentication"] = "FAILED" return False, "Authentication failed or permission not granted" req_args = { "redirect_uri": callback, "client_secret": self.client_secret, } client.token_endpoint = self.extra["token_endpoint"] tokenresp = client.do_access_token_request( scope=self._scope, body_type=self.token_response_body_type, request_args=req_args, authn_method="client_secret_post", state=response["state"], response_cls=self.access_token_response) if isinstance(tokenresp, ErrorResponse): logger.info("%s" % tokenresp) session["authentication"] = "FAILED" return False, "Authentication failed or permission not granted" # Download the user profile and cache a local instance of the # basic profile info result = client.fetch_protected_resource( self.userinfo_endpoint(tokenresp), token=tokenresp["access_token"]) logger.info("Userinfo: %s" % result.text) root = ET.fromstring(result.text) jsontext = json.dumps(root.attrib) profile = json.loads(jsontext) profile = self.convert(profile) logger.info("PROFILE: %s" % (profile, )) session["service"] = self.name session["authentication"] = "OK" session["status"] = "SUCCESS" session["authn_auth"] = self.authenticating_authority session["permanent_id"] = profile["uid"] server_env["CACHE"][sid] = session return True, profile, session
def phaseN(self, environ, info, server_env, sid): session = server_env["CACHE"][sid] callback = server_env["base_url"] + self.social_endpoint client = Client(client_id=self.client_id, client_authn_method=CLIENT_AUTHN_METHOD) response = client.parse_response(AuthorizationResponse, info, "dict") logger.info("Response: %s" % response) if isinstance(response, ErrorResponse): logger.info("%s" % response) session["authentication"] = "FAILED" return False, "Authentication failed or permission not granted" req_args = { "redirect_uri": callback, "client_secret": self.client_secret, } client.token_endpoint = self.extra["token_endpoint"] tokenresp = client.do_access_token_request( scope=self._scope, body_type=self.token_response_body_type, request_args=req_args, authn_method="client_secret_post", state=response["state"], response_cls=self.access_token_response) if isinstance(tokenresp, ErrorResponse): logger.info("%s" % tokenresp) session["authentication"] = "FAILED" return False, "Authentication failed or permission not granted" # Download the user profile and cache a local instance of the # basic profile info result = client.fetch_protected_resource( self.userinfo_endpoint(tokenresp), token=tokenresp["access_token"]) logger.info("Userinfo: %s" % result.text) profile = json.loads(result.text) logger.info("PROFILE: %s" % (profile, )) session["service"] = self.name session["authentication"] = "OK" session["status"] = "SUCCESS" session["authn_auth"] = self.authenticating_authority session["permanent_id"] = profile["id"] server_env["CACHE"][sid] = session return True, self.convert(profile), session
def phaseN(self, environ, query, server_env, session): callback = server_env["base_url"] + self.opKey client = Client(client_id=self.client_id, client_authn_method=CLIENT_AUTHN_METHOD) response = client.parse_response(AuthorizationResponse, query, "dict") logger.info("Response: %s" % response) if isinstance(response, ErrorResponse): logger.info("%s" % response) return (False, "Authentication failed or permission not granted") req_args = { "redirect_uri": callback, "client_secret": self.client_secret, } client.token_endpoint = self.extra["token_endpoint"] tokenresp = client.do_access_token_request( scope=self._scope, body_type=self.token_response_body_type, request_args=req_args, authn_method="client_secret_post", state=response["state"], response_cls=self.access_token_response) if isinstance(tokenresp, ErrorResponse): logger.info("%s" % tokenresp) return (False, "Authentication failed or permission not granted") # Download the user profile and cache a local instance of the # basic profile info result = client.fetch_protected_resource( self.userinfo_endpoint(tokenresp), token=tokenresp["access_token"]) logger.info("Userinfo: %s" % result.text) profile = json.loads(result.text) return True, profile, tokenresp["access_token"], client
class TestClient(object): @pytest.fixture(autouse=True) def create_client(self): self.redirect_uri = "https://example.com/redirect" self.authorization_endpoint = "https://example.com/authz" self.token_endpoint = "https://example.com/token" self.client = Client("1", config={"issuer": "https://example.com/as"}) self.client.redirect_uris = [self.redirect_uri] self.client.authorization_endpoint = self.authorization_endpoint self.client.token_endpoint = self.token_endpoint def test_construct_authz_req_no_optional_params(self): areq = self.client.construct_AuthorizationRequest( request_args={"response_type": ["code"]}) assert areq["redirect_uri"] == self.redirect_uri assert areq["response_type"] == ["code"] assert areq["client_id"] == "1" assert "state" not in areq assert "scope" not in areq def test_construct_authz_req_no_input(self): atr = self.client.construct_AuthorizationRequest() assert atr["redirect_uri"] == self.redirect_uri assert atr["response_type"] == ["code"] assert atr["client_id"] == "1" def test_construct_authz_req_optional_params(self): req_args = { "response_type": ["code"], "scope": ["foo", "bar"], "state": "abc" } areq = self.client.construct_AuthorizationRequest( request_args=req_args) assert areq["redirect_uri"] == self.redirect_uri assert areq["response_type"] == ["code"] assert areq["client_id"] == "1" assert areq["state"] == "abc" assert areq["scope"] == ["foo", "bar"] def test_construct_authz_req_replace_default_state(self): req_args = { "response_type": ["code"], "scope": ["foo", "bar"], "state": "efg" } areq = self.client.construct_AuthorizationRequest( request_args=req_args) assert areq["redirect_uri"] == self.redirect_uri assert areq["response_type"] == ["code"] assert areq["client_id"] == "1" assert areq["state"] == "efg" assert areq["scope"] == ["foo", "bar"] def test_parse_authz_resp_url(self): code = "SplxlOBeZQQYbYS6WxSbIA" state = "ghi" url = "{}?code={}&state={}".format(self.redirect_uri, code, state) aresp = self.client.parse_response(AuthorizationResponse, info=url, sformat="urlencoded") assert aresp["code"] == code assert aresp["state"] == state assert self.client.grant[state].code == aresp["code"] assert self.client.grant[state].grant_expiration_time def test_parse_authz_resp_query(self): query = "code=SplxlOBeZQQYbYS6WxSbIA&state=hij" aresp = self.client.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") assert aresp["code"] == "SplxlOBeZQQYbYS6WxSbIA" assert aresp["state"] == "hij" assert self.client.grant["hij"] assert self.client.grant["hij"].code == aresp["code"] assert self.client.grant["hij"].grant_expiration_time def test_parse_authz_resp_query_multi_scope(self): code = "SplxlOBeZQQYbYS6WxSbIA" states = ["ghi", "hij", "klm"] for state in states: self.client.parse_response( AuthorizationResponse, info="code={}&state={}".format(code, state), sformat="urlencoded", ) for state in states: assert self.client.grant[state].code == code assert _eq(self.client.grant.keys(), states) def test_parse_authz_resp_query_unknown_parameter(self): query = "code=SplxlOBeZQQYbYS6WxSbIA&state=xyz&foo=bar" aresp = self.client.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") assert aresp["code"] == "SplxlOBeZQQYbYS6WxSbIA" assert aresp["state"] == "xyz" # assert "foo" not in aresp # TODO unknown parameter not discarded assert self.client.grant["xyz"] assert self.client.grant["xyz"].code == aresp["code"] assert self.client.grant["xyz"].grant_expiration_time def test_construct_access_token_req(self): grant = Grant() grant.code = "AbCdEf" grant.grant_expiration_time = time_util.utc_time_sans_frac() + 30 self.client.grant = {"stat": grant} # scope is default="" atr = self.client.construct_AccessTokenRequest(state="stat") assert atr["grant_type"] == "authorization_code" assert atr["code"] == "AbCdEf" assert atr["redirect_uri"] == self.redirect_uri def test_construct_access_token_req_client_credentials(self): # scope is default="" request_args = {"grant_type": "client_credentials"} atr = self.client.construct_AccessTokenRequest( state="stat", request=CCAccessTokenRequest, request_args=request_args) assert atr["grant_type"] == "client_credentials" assert atr["state"] == "stat" def test_construct_access_token_req_extension_grant(self): request_args = { "grant_type": "urn:ietf:params:oauth:grant-type:saml2-bearer", "assertion": "saml assertion", } atr = self.client.construct_AccessTokenRequest( request=ExtensionTokenRequest, request_args=request_args) assert atr[ "grant_type"] == "urn:ietf:params:oauth:grant-type:saml2-bearer" assert atr["assertion"] == "saml assertion" def test_construct_access_token_request_fail(self): with pytest.raises(GrantError): self.client.construct_AccessTokenRequest(state="unknown") def test_construct_access_token_req_override(self): grant = Grant() grant.code = "AbCdEf" grant.grant_expiration_time = time_util.utc_time_sans_frac() + 30 self.client.grant = {"xyz": grant} atr = self.client.construct_AccessTokenRequest(state="xyz") assert atr["grant_type"] == "authorization_code" assert atr["code"] == "AbCdEf" assert atr["redirect_uri"] == self.redirect_uri def test_parse_access_token_resp(self): atr = AccessTokenResponse( access_token="2YotnFZFEjr1zCsicMWpAA", token_type="example", expires_in=3600, refresh_token="tGzv3JOkF0XG5Qx2TlKWIA", example_parameter="example_value", ) self.client.parse_response(AccessTokenResponse, info=json.dumps(atr.to_dict())) _grant = self.client.grant[""] assert len(_grant.tokens) == 1 token = _grant.tokens[0] assert token.access_token == "2YotnFZFEjr1zCsicMWpAA" assert token.token_type == "example" assert token.token_expiration_time > time_util.time_sans_frac() assert token.refresh_token == "tGzv3JOkF0XG5Qx2TlKWIA" def test_get_access_token_refresh_with_refresh_token(self): self.client.grant["foo"] = Grant() _get = time_util.utc_time_sans_frac() + 60 self.client.grant["foo"].grant_expiration_time = _get self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(refresh_token="refresh_with_me", access_token="access") token = Token(resp) self.client.grant["foo"].tokens.append(token) # Uses refresh_token from previous response atr = self.client.construct_RefreshAccessTokenRequest(token=token) assert atr["grant_type"] == "refresh_token" assert atr["refresh_token"] == "refresh_with_me" def test_get_access_token_refresh_from_state(self): self.client.grant["foo"] = Grant() _get = time_util.utc_time_sans_frac() + 60 self.client.grant["foo"].grant_expiration_time = _get self.client.grant["foo"].code = "access_code" resp = AccessTokenResponse(refresh_token="refresh_with_me", access_token="access") self.client.grant["foo"].tokens.append(Token(resp)) # Uses refresh_token from previous response atr = self.client.construct_RefreshAccessTokenRequest(state="foo") assert isinstance(atr, RefreshAccessTokenRequest) assert atr["grant_type"] == "refresh_token" assert atr["refresh_token"] == "refresh_with_me" def test_parse_authz_err_resp(self): error = "access_denied" state = "xyz" ruri = "{}?error={}&state={}".format(self.redirect_uri, error, state) resp = self.client.parse_response(AuthorizationResponse, info=ruri, sformat="urlencoded") assert isinstance(resp, AuthorizationErrorResponse) assert resp["error"] == error assert resp["state"] == state def test_return_non_existant_grant(self): assert self.client.grant_from_state("123456abcdef") is None def test_get_grant(self): resp = AuthorizationResponse(code="code", state="state") grant = Grant() grant.add_code(resp) self.client.grant["state"] = grant new_grant = self.client.grant_from_state("state") assert new_grant is not None assert new_grant.code == "code" def test_construct_access_token_req_with_extra_args(self): query = "code=SplxlOBeZQQYbYS6WxSbIA&state=abc" self.client.parse_response(AuthorizationResponse, info=query, sformat="urlencoded") req = self.client.construct_AccessTokenRequest( state="abc", extra_args={"foo": "bar"}) assert _eq( req.keys(), [ "code", "grant_type", "client_id", "redirect_uri", "foo", "state" ], ) assert req["foo"] == "bar" def test_request_info_simple(self): req_args = {"state": "hmm", "response_type": "code"} uri, body, h_args, cis = self.client.request_info( AuthorizationRequest, request_args=req_args) assert uri == self.authorization_endpoint body_elts = body.split("&") expected_body = ( "state=hmm&redirect_uri={}&response_type=code&client_id=1".format( quote(self.redirect_uri, safe=""))) expected_body_elts = expected_body.split("&") assert set(body_elts) == set(expected_body_elts) assert h_args == { "headers": { "Content-Type": "application/x-www-form-urlencoded" } } assert isinstance(cis, AuthorizationRequest) def test_request_info_simple_get(self): uri, body, h_args, cis = self.client.request_info(AuthorizationRequest, method="GET") assert url_compare( uri, "{}?redirect_uri={}&response_type=code&client_id=1".format( self.authorization_endpoint, quote(self.redirect_uri, safe="")), ) assert body is None assert h_args == {} def test_request_info_simple_get_with_req_args(self): uri, body, h_args, cis = self.client.request_info( AuthorizationRequest, method="GET", request_args={"state": "init"}) assert url_compare( uri, "{}?state=init&redirect_uri={}&response_type=code&client_id=1". format(self.authorization_endpoint, quote(self.redirect_uri, safe="")), ) assert body is None assert h_args == {} assert isinstance(cis, AuthorizationRequest) def test_request_info_simple_get_with_extra_args(self): uri, body, h_args, cis = self.client.request_info( AuthorizationRequest, method="GET", extra_args={"rock": "little"}) assert url_compare( uri, "{}?redirect_uri={}&response_type=code&client_id=1&rock=little". format(self.authorization_endpoint, quote(self.redirect_uri, safe="")), ) assert body is None assert h_args == {} assert isinstance(cis, AuthorizationRequest) def test_request_info_with_req_and_extra_args(self): uri, body, h_args, cis = self.client.request_info( AuthorizationRequest, method="GET", request_args={"state": "init"}, extra_args={"rock": "little"}, ) expected = ( "{}?state=init&redirect_uri={}&response_type=code&client_id=1&rock=little" ) assert url_compare( uri, expected.format(self.authorization_endpoint, quote(self.redirect_uri, safe="")), ) assert body is None assert h_args == {} assert isinstance(cis, AuthorizationRequest) def test_construct_access_token_req_expired_grant(self): resp = AuthorizationResponse(code="code", state="state") grant = Grant(-10) # expired grant grant.add_code(resp) client = Client() client.grant["openid"] = grant with pytest.raises(GrantExpired): client.construct_AccessTokenRequest(state="openid") def test_parse_access_token_resp_json(self): atr = self.client.parse_response(AccessTokenResponse, info=ACC_TOK_RESP.to_json()) assert _eq(atr.keys(), ["token_type", "scope", "access_token", "refresh_token"]) def test_parse_access_token_resp_urlencoded(self): uatr = self.client.parse_response(AccessTokenResponse, info=ACC_TOK_RESP.to_urlencoded(), sformat="urlencoded") assert _eq(uatr.keys(), ["token_type", "scope", "access_token", "refresh_token"]) def test_parse_access_token_resp_url(self): url = "{}?{}".format("https://example.com/token", ACC_TOK_RESP.to_urlencoded()) uatr = self.client.parse_response(AccessTokenResponse, info=url, sformat="urlencoded") assert _eq(uatr.keys(), ["token_type", "scope", "access_token", "refresh_token"]) def test_parse_error_resp(self): err = ErrorResponse( error="invalid_request", error_description="Something was missing", error_uri="http://example.com/error_message.html", ) jerr = err.to_json() uerr = err.to_urlencoded() self.client.parse_response(AccessTokenResponse, info=jerr) self.client.parse_response(AccessTokenResponse, info=uerr, sformat="urlencoded") with pytest.raises(ResponseError): self.client.parse_response(AccessTokenResponse, info=jerr, sformat="urlencoded") with pytest.raises(DecodeError): self.client.parse_response(AccessTokenResponse, info=uerr) with pytest.raises(FormatError): self.client.parse_response( AccessTokenResponse, info=jerr, sformat="focus" # type: ignore ) def test_parse_access_token_resp_missing_attribute(self): atresp = AccessTokenResponse( access_token="SlAV32hkKG", token_type="Bearer", refresh_token="8xLOxBtZp8", expire_in=3600, ) atdict = atresp.to_dict() del atdict["access_token"] # remove required access_token atj = json.dumps(atdict) with pytest.raises(MissingRequiredAttribute): self.client.parse_response(AccessTokenResponse, info=atj) with pytest.raises(MissingRequiredAttribute): self.client.parse_response(AccessTokenResponse, info=urlencode(atdict), sformat="urlencoded") def test_client_parse_args(self): args = { "response_type": "", "client_id": "client_id", "redirect_uri": "http://example.com/authz", "scope": "scope", "state": "state", } ar_args = self.client._parse_args(AuthorizationRequest, **args) assert _eq( ar_args.keys(), ["scope", "state", "redirect_uri", "response_type", "client_id"], ) def test_client_parse_extra_args(self): args = { "response_type": "", "client_id": "client_id", "redirect_uri": "http://example.com/authz", "scope": "scope", "state": "state", "extra_session": "home", } ar_args = self.client._parse_args(AuthorizationRequest, **args) assert _eq( ar_args.keys(), [ "state", "redirect_uri", "response_type", "client_id", "scope", "extra_session", ], ) def test_client_endpoint(self): self.client.authorization_endpoint = "https://example.org/oauth2/as" self.client.token_endpoint = "https://example.org/oauth2/token" self.client.token_revocation_endpoint = "https://example.org/oauth2/token_rev" assert (self.client._endpoint("authorization_endpoint") == "https://example.org/oauth2/as") assert (self.client._endpoint("token_endpoint") == "https://example.org/oauth2/token") assert (self.client._endpoint("token_revocation_endpoint") == "https://example.org/oauth2/token_rev") auth_endpoint = self.client._endpoint( "authorization_endpoint", **{"authorization_endpoint": "https://example.com/as"}) assert auth_endpoint == "https://example.com/as" self.client.token_endpoint = "" with pytest.raises(MissingEndpoint): self.client._endpoint("token_endpoint") self.client._endpoint("foo_endpoint") def test_do_access_token_request_client_credentials(self): class CCMessageFactory(OauthMessageFactory): """We are doing client credentials.""" token_endpoint = MessageTuple(CCAccessTokenRequest, AccessTokenResponse) self.client.message_factory = CCMessageFactory with responses.RequestsMock() as rsps: rsps.add( rsps.POST, self.token_endpoint, json={ "access_token": "Token", "token_type": "bearer" }, ) resp = self.client.do_access_token_request() assert rsps.calls[ 0].request.body == "grant_type=client_credentials" assert isinstance(resp, AccessTokenResponse) assert resp["access_token"] == "Token" def test_do_access_token_request_extension_grant(self): class ExtensionMessageFactory(OauthMessageFactory): """We are doing Extension grant.""" token_endpoint = MessageTuple(ExtensionTokenRequest, AccessTokenResponse) self.client.message_factory = ExtensionMessageFactory request_args = { "assertion": "saml assertion", "grant_type": "urn:ietf:params:oauth:grant-type:saml2-bearer", } with responses.RequestsMock() as rsps: rsps.add( rsps.POST, self.token_endpoint, json={ "access_token": "Token", "token_type": "bearer" }, ) resp = self.client.do_access_token_request( request_args=request_args) request = parse_qs(rsps.calls[0].request.body) assert request["assertion"][0] == "saml assertion" assert (request["grant_type"][0] == "urn:ietf:params:oauth:grant-type:saml2-bearer") assert isinstance(resp, AccessTokenResponse) assert resp["access_token"] == "Token"