def test_static_client_registration(server_url, provider_info, browser): redirect_uri = "http://localhost:8090" browser.get(server_url + "/client_registration") new_url_input = browser.find_element_by_xpath("/html/body/div/div/div[1]/div[1]/form/div/input") new_url_input.send_keys(redirect_uri) add_btn = browser.find_element_by_xpath("/html/body/div/div/div[1]/div[1]/form/div/span/button") add_btn.click() submit_btn = browser.find_element_by_xpath("/html/body/div/div/div[2]/button") submit_btn.click() client_credentials = get_client_credentials_from_page(browser) args = { "client_id": client_credentials["client_id"], "scope": "openid", "response_type": "id_token", "redirect_uri": redirect_uri, "state": "state0", "nonce": "nonce0" } auth_req = AuthorizationRequest(**args) request = auth_req.request(provider_info["authorization_endpoint"]) browser.get(request) fill_login_details(browser) 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 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 test_static_client_registration(server_url, provider_info, browser): redirect_uri = "http://localhost:8090" browser.get(server_url + "/client_registration") new_url_input = browser.find_element_by_xpath( "/html/body/div/div/div[1]/div[1]/form/div/input") new_url_input.send_keys(redirect_uri) add_btn = browser.find_element_by_xpath( "/html/body/div/div/div[1]/div[1]/form/div/span/button") add_btn.click() submit_btn = browser.find_element_by_xpath( "/html/body/div/div/div[2]/button") submit_btn.click() client_credentials = get_client_credentials_from_page(browser) args = { "client_id": client_credentials["client_id"], "scope": "openid", "response_type": "id_token", "redirect_uri": redirect_uri, "state": "state0", "nonce": "nonce0" } auth_req = AuthorizationRequest(**args) request = auth_req.request(provider_info["authorization_endpoint"]) browser.get(request) fill_login_details(browser) 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 do_auth_flow(self): # get provider configuration information resp = self.app.get('/.well-known/openid-configuration') assert resp.status_code == 200 provider_config = json.loads(resp.data.decode('utf-8')) # incoming auth request from client, verify response is redirect to discovery service auth_req = AuthorizationRequest(client_id='client1', response_type='id_token', scope='openid student', redirect_uri='http://localhost:8090/authz_cb', nonce='nonce') auth_url = auth_req.request(urlparse(provider_config['authorization_endpoint']).path) resp = self.app.get(auth_url) assert resp.status_code == 303 disco_url = dict(resp.headers)['Location'] assert disco_url.startswith('http://localhost:8080/role/idp.ds') # incoming disco response, verify response is auth request to IdP disco_resp_url = dict(parse_qsl(urlparse(disco_url).query))['return'] with responses.RequestsMock() as rsps: idp_metadata = """<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="testProvider"> <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:9000/sso/redirect"/> </md:IDPSSODescriptor> </md:EntityDescriptor> """ rsps.add(responses.GET, 'http://pyff:8080/entities/%7Bsha1%7D' + hashlib.sha1('testProvider'.encode('utf-8')).hexdigest(), body=idp_metadata, status=200) resp = self.app.get(urlparse(disco_resp_url).path + '?' + urlencode({'entityID': 'testProvider'})) assert resp.status_code == 303 idp_url = dict(resp.headers)['Location'] assert idp_url.startswith('http://localhost:9000/sso/redirect') # incoming authn response from IdP, verify response is consent page saml_response = """<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6" Version="2.0" IssueInstant="{0:%Y-%m-%dT%H:%M:%SZ}" Destination="" InResponseTo=""> <saml:Issuer>testProvider</saml:Issuer> <samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> </samlp:Status> <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_d71a3a8e9fcc45c9e9d248ef7049393fc8f04e5f75" Version="2.0" IssueInstant="{0:%Y-%m-%dT%H:%M:%SZ}"> <saml:Issuer>testProvider</saml:Issuer> <saml:Subject> <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID> <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://localhost:10000/SAML2Transient/acs/post" InResponseTo=""/> </saml:SubjectConfirmation> </saml:Subject> <saml:Conditions> <saml:AudienceRestriction> <saml:Audience>http://localhost:10000/SAML2Transient/acs/post</saml:Audience> </saml:AudienceRestriction> </saml:Conditions> <saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93"> <saml:AuthnContext> <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef> </saml:AuthnContext> </saml:AuthnStatement> <saml:AttributeStatement> <saml:Attribute FriendlyName="eduPersonAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"> <saml:AttributeValue xsi:type="xs:string">student</saml:AttributeValue> </saml:Attribute> </saml:AttributeStatement> </saml:Assertion> </samlp:Response> """.format(datetime.datetime.utcnow()) resp = self.app.post('/SAML2Transient/acs/post', data={'SAMLResponse': base64.b64encode(saml_response.encode('utf-8')), 'RelayState': dict(parse_qsl(urlparse(idp_url).query))['RelayState']}) assert resp.status_code == 200
def do_auth_flow(self): # get provider configuration information resp = self.app.get('/.well-known/openid-configuration') assert resp.status_code == 200 provider_config = json.loads(resp.data.decode('utf-8')) # incoming auth request from client, verify response is redirect to discovery service auth_req = AuthorizationRequest( client_id='client1', response_type='id_token', scope='openid student', redirect_uri='http://localhost:8090/authz_cb', nonce='nonce') auth_url = auth_req.request( urlparse(provider_config['authorization_endpoint']).path) resp = self.app.get(auth_url) assert resp.status_code == 303 disco_url = dict(resp.headers)['Location'] assert disco_url.startswith('http://localhost:8080/role/idp.ds') # incoming disco response, verify response is auth request to IdP disco_resp_url = dict(parse_qsl(urlparse(disco_url).query))['return'] with responses.RequestsMock() as rsps: idp_metadata = """<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="testProvider"> <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:9000/sso/redirect"/> </md:IDPSSODescriptor> </md:EntityDescriptor> """ rsps.add(responses.GET, 'http://pyff:8080/entities/%7Bsha1%7D' + hashlib.sha1('testProvider'.encode('utf-8')).hexdigest(), body=idp_metadata, status=200) resp = self.app.get( urlparse(disco_resp_url).path + '?' + urlencode({'entityID': 'testProvider'})) assert resp.status_code == 303 idp_url = dict(resp.headers)['Location'] assert idp_url.startswith('http://localhost:9000/sso/redirect') # incoming authn response from IdP, verify response is consent page saml_response = """<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_8e8dc5f69a98cc4c1ff3427e5ce34606fd672f91e6" Version="2.0" IssueInstant="{0:%Y-%m-%dT%H:%M:%SZ}" Destination="" InResponseTo=""> <saml:Issuer>testProvider</saml:Issuer> <samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> </samlp:Status> <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="_d71a3a8e9fcc45c9e9d248ef7049393fc8f04e5f75" Version="2.0" IssueInstant="{0:%Y-%m-%dT%H:%M:%SZ}"> <saml:Issuer>testProvider</saml:Issuer> <saml:Subject> <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">_ce3d2948b4cf20146dee0a0b3dd6f69b6cf86f62d7</saml:NameID> <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <saml:SubjectConfirmationData NotOnOrAfter="2024-01-18T06:21:48Z" Recipient="http://localhost:10000/SAML2Transient/acs/post" InResponseTo=""/> </saml:SubjectConfirmation> </saml:Subject> <saml:Conditions> <saml:AudienceRestriction> <saml:Audience>http://localhost:10000/SAML2Transient/acs/post</saml:Audience> </saml:AudienceRestriction> </saml:Conditions> <saml:AuthnStatement AuthnInstant="2014-07-17T01:01:48Z" SessionNotOnOrAfter="2024-07-17T09:01:48Z" SessionIndex="_be9967abd904ddcae3c0eb4189adbe3f71e327cf93"> <saml:AuthnContext> <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef> </saml:AuthnContext> </saml:AuthnStatement> <saml:AttributeStatement> <saml:Attribute FriendlyName="eduPersonAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"> <saml:AttributeValue xsi:type="xs:string">student</saml:AttributeValue> </saml:Attribute> </saml:AttributeStatement> </saml:Assertion> </samlp:Response> """.format(datetime.datetime.utcnow()) resp = self.app.post( '/SAML2Transient/acs/post', data={ 'SAMLResponse': base64.b64encode(saml_response.encode('utf-8')), 'RelayState': dict(parse_qsl(urlparse(idp_url).query))['RelayState'] }) assert resp.status_code == 200