diff --git a/common/src/web/fedcm/fedcm.html b/common/src/web/fedcm/fedcm.html index bdf10e2fa8f2d..2c10510ad9caa 100644 --- a/common/src/web/fedcm/fedcm.html +++ b/common/src/web/fedcm/fedcm.html @@ -1,20 +1,37 @@ - + async function triggerFedCm() { + console.log("Config URL:", configURL); + try { + let promise = await navigator.credentials.get({ + identity: { + providers: [{ + configURL: configURL, + clientId: '1', + }] + } + }); + result = promise; + document.getElementById('result').innerText = JSON.stringify(result); + } catch (error) { + console.error("FedCM Error:", error); + result = { error: error.message }; + document.getElementById('result').innerText = JSON.stringify(result); + } + } + + + \ No newline at end of file diff --git a/common/src/web/fedcm/login.html b/common/src/web/fedcm/login.html new file mode 100644 index 0000000000000..10329a440bc66 --- /dev/null +++ b/common/src/web/fedcm/login.html @@ -0,0 +1,11 @@ + + + + Login + + +

Login Page

+

Login successful! This is a placeholder for the login process.

+ Return to Home + + \ No newline at end of file diff --git a/common/src/web/fedcm/signin.html b/common/src/web/fedcm/signin.html new file mode 100644 index 0000000000000..90fd68e1a6dc5 --- /dev/null +++ b/common/src/web/fedcm/signin.html @@ -0,0 +1,19 @@ + + + + Sign In + + +

Sign In Page

+

This is a placeholder for the sign-in page.

+
+ + +

+ + +

+ +
+ + \ No newline at end of file diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java index e3568900abe98..45478d1f618f3 100644 --- a/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java +++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java @@ -52,6 +52,8 @@ public interface FederatedCredentialManagementDialog { /** Returns the subtitle of the dialog or null if none. */ String getSubtitle(); + void clickDialog(); + /** * Returns the accounts shown in the account chooser. * diff --git a/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java b/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java index d61b1a1acccbb..59e832f84e367 100644 --- a/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java +++ b/java/src/org/openqa/selenium/remote/FedCmDialogImpl.java @@ -45,6 +45,12 @@ public String getDialogType() { return (String) executeMethod.execute(DriverCommand.GET_FEDCM_DIALOG_TYPE, null); } + @Override + public void clickDialog() { + executeMethod.execute( + DriverCommand.CLICK_DIALOG, Map.of("dialogButton", "ConfirmIdpLoginContinue")); + } + @Override public String getTitle() { Map result = diff --git a/java/test/org/openqa/selenium/environment/webserver/FedCmConfigHandler.java b/java/test/org/openqa/selenium/environment/webserver/FedCmConfigHandler.java new file mode 100644 index 0000000000000..9c6f3ab29fefb --- /dev/null +++ b/java/test/org/openqa/selenium/environment/webserver/FedCmConfigHandler.java @@ -0,0 +1,47 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.environment.webserver; + + +import java.io.UncheckedIOException; +import java.util.Map; +import org.openqa.selenium.remote.http.Contents; +import org.openqa.selenium.remote.http.HttpHandler; +import org.openqa.selenium.remote.http.HttpRequest; +import org.openqa.selenium.remote.http.HttpResponse; + +class FedCmConfigHandler implements HttpHandler { + + @Override + public HttpResponse execute(HttpRequest req) throws UncheckedIOException { + HttpResponse response = new HttpResponse(); + response.setHeader("Content-Type", "application/json"); + response.setHeader("Cache-Control", "no-store"); + + response.setContent( + Contents.asJson( + Map.of( + "accounts_endpoint", "accounts.json", + "client_metadata_endpoint", "client_metadata.json", + "id_assertion_endpoint", "id_assertion.json", + "signin_url", "signin", + "login_url", "login"))); + + return response; + } +} diff --git a/java/test/org/openqa/selenium/environment/webserver/HandlersForTests.java b/java/test/org/openqa/selenium/environment/webserver/HandlersForTests.java index cb99cc3a334e5..72ac648d37c8f 100644 --- a/java/test/org/openqa/selenium/environment/webserver/HandlersForTests.java +++ b/java/test/org/openqa/selenium/environment/webserver/HandlersForTests.java @@ -56,9 +56,10 @@ public HandlersForTests(String hostname, int port, Path tempPageDir) { .setContent(Contents.string("

authorized

", UTF_8))) .with(new BasicAuthenticationFilter("test", "test")), Route.get("/.well-known/web-identity").to(WellKnownWebIdentityHandler::new), + Route.get("/fedcm/config.json").to(FedCmConfigHandler::new), Route.get("/echo").to(EchoHandler::new), Route.get("/cookie").to(CookieHandler::new), - Route.post("/fedcm/id_assertion").to(FedCmIdAssertion::new), + Route.post("/fedcm/id_assertion.json").to(FedCmIdAssertion::new), Route.get("/encoding").to(EncodingHandler::new), Route.matching(req -> req.getUri().startsWith("/generated/")) .to(() -> new GeneratedJsTestHandler("/generated")), diff --git a/java/test/org/openqa/selenium/environment/webserver/WellKnownWebIdentityHandler.java b/java/test/org/openqa/selenium/environment/webserver/WellKnownWebIdentityHandler.java index ad6372767d5e8..d474c66fdf2e1 100644 --- a/java/test/org/openqa/selenium/environment/webserver/WellKnownWebIdentityHandler.java +++ b/java/test/org/openqa/selenium/environment/webserver/WellKnownWebIdentityHandler.java @@ -35,7 +35,7 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException { HttpResponse response = new HttpResponse(); response.setHeader("Content-Type", "application/json"); response.setHeader("Cache-Control", "no-store"); - String targetLocation = UrlPath.relativeToContext(req, "/fedcm/fedcm.json"); + String targetLocation = UrlPath.relativeToContext(req, "https://idp.com"); response.setContent(Contents.string(String.format(RESPONSE_STRING, targetLocation), UTF_8)); diff --git a/java/test/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementTest.java b/java/test/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementTest.java index 9f63fc69c91ba..22ffcfda188a0 100644 --- a/java/test/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementTest.java +++ b/java/test/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementTest.java @@ -19,8 +19,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assumptions.assumeThat; -import static org.assertj.core.api.InstanceOfAssertFactories.MAP; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.openqa.selenium.testing.drivers.Browser.CHROME; @@ -28,44 +26,52 @@ import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; +import java.util.List; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openqa.selenium.InvalidSelectorException; -import org.openqa.selenium.JavascriptException; -import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.By; +import org.openqa.selenium.NoAlertPresentException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.environment.InProcessTestEnvironment; +import org.openqa.selenium.environment.webserver.AppServer; import org.openqa.selenium.support.ui.WebDriverWait; -import org.openqa.selenium.testing.JupiterTestBase; - import org.openqa.selenium.testing.NeedsSecureServer; @NeedsSecureServer -class FederatedCredentialManagementTest extends JupiterTestBase { +class FederatedCredentialManagementTest { - private JavascriptExecutor jsAwareDriver; private HasFederatedCredentialManagement fedcmDriver; + private WebDriver localDriver; + InProcessTestEnvironment environment = new InProcessTestEnvironment(true); + AppServer appServer = environment.getAppServer(); @BeforeEach public void setup() { ChromeOptions options = (ChromeOptions) CHROME.getCapabilities(); - // options.setAcceptInsecureCerts(true); + options.setAcceptInsecureCerts(true); options.addArguments( String.format("host-resolver-rules=MAP localhost:443 localhost:%d", getSecurePort())); options.addArguments("ignore-certificate-errors"); - localDriver = seleniumExtension.createNewDriver(options); + options.addArguments("--enable-fedcm-without-well-known-enforcement"); + localDriver = new ChromeDriver(options); assumeThat(localDriver).isInstanceOf(HasFederatedCredentialManagement.class); - jsAwareDriver = (JavascriptExecutor) localDriver; fedcmDriver = (HasFederatedCredentialManagement) localDriver; localDriver.get(appServer.whereIsSecure("/fedcm/fedcm.html")); } - private Object triggerFedCm() { - return jsAwareDriver.executeScript("triggerFedCm()"); + @AfterEach + public void teardown() { + localDriver.quit(); + appServer.stop(); } private void waitForDialog() { - WebDriverWait wait = new WebDriverWait(localDriver, Duration.ofSeconds(5)); + WebDriverWait wait = new WebDriverWait(localDriver, Duration.ofSeconds(20)); wait.until( driver -> ((HasFederatedCredentialManagement) driver).getFederatedCredentialManagementDialog() @@ -87,51 +93,85 @@ void testDismissDialog() { fedcmDriver.setDelayEnabled(false); assertNull(fedcmDriver.getFederatedCredentialManagementDialog()); - Object response = triggerFedCm(); + WebElement triggerButton = localDriver.findElement(By.id("triggerButton")); + triggerButton.click(); waitForDialog(); FederatedCredentialManagementDialog dialog = fedcmDriver.getFederatedCredentialManagementDialog(); - assertEquals("Sign in to localhost with localhost", dialog.getTitle()); - assertEquals("AccountChooser", dialog.getDialogType()); - + assertThat(dialog.getTitle()).isEqualTo("Sign in to localhost with localhost"); + assertThat(dialog.getDialogType()).isEqualTo("AccountChooser"); dialog.cancelDialog(); - // Check that the dialog was indeed closed (i.e. the promise now resolves). - assertThrows( - JavascriptException.class, - () -> { - try { - jsAwareDriver.executeScript("await promise"); - } catch (InvalidSelectorException ex) { - // Due to a bug in Chromedriver (https://crbug.com/1454586), we may - // get an invalid selector exception here instead of a JavascriptException. - // Turn it into a JavascriptException to make the test pass for now. - throw new JavascriptException(ex.getMessage(), ex); - } - }); + // Check that the dialog was indeed closed. Unable to get the dialog type since the dialog was + // closed. + assertThrows(NoAlertPresentException.class, dialog::getDialogType); } @Test void testSelectAccount() { - fedcmDriver.setDelayEnabled(false); assertNull(fedcmDriver.getFederatedCredentialManagementDialog()); - Object response = triggerFedCm(); + WebElement triggerButton = localDriver.findElement(By.id("triggerButton")); + triggerButton.click(); waitForDialog(); FederatedCredentialManagementDialog dialog = fedcmDriver.getFederatedCredentialManagementDialog(); - assertEquals("Sign in to localhost with localhost", dialog.getTitle()); - assertEquals("AccountChooser", dialog.getDialogType()); + assertThat(dialog.getTitle()).isEqualTo("Sign in to localhost with localhost"); + assertThat(dialog.getDialogType()).isEqualTo("AccountChooser"); - dialog.selectAccount(0); + List accountList = dialog.getAccounts(); + assertThat(accountList.size()).isEqualTo(2); + dialog.selectAccount(1); + } + + @Test + void testGetAccounts() { + assertNull(fedcmDriver.getFederatedCredentialManagementDialog()); + + WebElement triggerButton = localDriver.findElement(By.id("triggerButton")); + triggerButton.click(); + + waitForDialog(); + + FederatedCredentialManagementDialog dialog = + fedcmDriver.getFederatedCredentialManagementDialog(); - response = jsAwareDriver.executeScript("return await promise"); - assertThat(response).asInstanceOf(MAP).containsEntry("token", "a token"); + assertThat(dialog.getTitle()).isEqualTo("Sign in to localhost with localhost"); + assertThat(dialog.getDialogType()).isEqualTo("AccountChooser"); + + List accountList = dialog.getAccounts(); + assertThat(accountList.size()).isEqualTo(2); + + FederatedCredentialManagementAccount account1 = accountList.get(0); + + assertThat(account1.getName()).isEqualTo("John Doe"); + assertThat(account1.getEmail()).isEqualTo("john_doe@idp.example"); + assertThat(account1.getAccountid()).isEqualTo("1234"); + assertThat(account1.getGivenName()).isEqualTo("John"); + assertThat(account1.getIdpConfigUrl()).contains("/fedcm/config.json"); + assertThat(account1.getPictureUrl()).isEqualTo("https://idp.example/profile/123"); + assertThat(account1.getLoginState()).isEqualTo("SignUp"); + assertThat(account1.getTermsOfServiceUrl()) + .isEqualTo("https://rp.example/terms_of_service.html"); + assertThat(account1.getPrivacyPolicyUrl()).isEqualTo("https://rp.example/privacy_policy.html"); + + FederatedCredentialManagementAccount account2 = accountList.get(1); + + assertThat(account2.getName()).isEqualTo("Aisha Ahmad"); + assertThat(account2.getEmail()).isEqualTo("aisha@idp.example"); + assertThat(account2.getAccountid()).isEqualTo("5678"); + assertThat(account2.getGivenName()).isEqualTo("Aisha"); + assertThat(account2.getIdpConfigUrl()).contains("/fedcm/config.json"); + assertThat(account2.getPictureUrl()).isEqualTo("https://idp.example/profile/567"); + assertThat(account2.getLoginState()).isEqualTo("SignUp"); + assertThat(account2.getTermsOfServiceUrl()) + .isEqualTo("https://rp.example/terms_of_service.html"); + assertThat(account2.getPrivacyPolicyUrl()).isEqualTo("https://rp.example/privacy_policy.html"); } }