Skip to content

Commit

Permalink
Add support for FedCM commands
Browse files Browse the repository at this point in the history
As specified in:
https://fedidcg.github.io/FedCM/#automation

Currently implemented in Chromium.
  • Loading branch information
cbiesinger committed May 23, 2023
1 parent f275319 commit 5b42d87
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 0 deletions.
98 changes: 98 additions & 0 deletions java/src/org/openqa/selenium/chromium/AddHasFedCm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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.chromium;

import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableMap;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.internal.Require;
import org.openqa.selenium.NoAlertPresentException;
import org.openqa.selenium.remote.AdditionalHttpCommands;
import org.openqa.selenium.remote.AugmenterProvider;
import org.openqa.selenium.remote.CommandInfo;
import org.openqa.selenium.remote.ExecuteMethod;
import org.openqa.selenium.remote.http.HttpMethod;

import java.time.Duration;
import java.util.Map;
import java.util.function.Predicate;

import static org.openqa.selenium.chromium.ChromiumDriver.IS_CHROMIUM_BROWSER;

@AutoService({AdditionalHttpCommands.class, AugmenterProvider.class})
public class AddHasFedCm implements AugmenterProvider<HasFedCm>, AdditionalHttpCommands {

public static final String CANCEL_DIALOG = "cancelDialog";
public static final String SELECT_ACCOUNT = "selectAccount";
public static final String GET_ACCOUNTS = "getAccounts";
public static final String GET_FEDCM_TITLE = "getFedCmTitle";
public static final String GET_FEDCM_DIALOG_TYPE = "getFedCmTitle";
public static final String SET_DELAY_ENABLED = "setDelayEnabled";
public static final String RESET_COOLDOWN = "resetCooldown";

private static final Map<String, CommandInfo> COMMANDS = ImmutableMap.of(
CANCEL_DIALOG, new CommandInfo("/session/:sessionId/fedcm/canceldialog", HttpMethod.POST),
SELECT_ACCOUNT, new CommandInfo("/session/:sessionId/fedcm/selectaccount", HttpMethod.POST),
GET_ACCOUNTS, new CommandInfo("/session/:sessionId/fedcm/accountlist", HttpMethod.GET),
GET_FEDCM_TITLE, new CommandInfo("/session/:sessionId/fedcm/gettitle", HttpMethod.GET),
GET_FEDCM_DIALOG_TYPE, new CommandInfo("/session/:sessionId/fedcm/getdialogtype", HttpMethod.GET),
SET_DELAY_ENABLED, new CommandInfo("/session/:sessionId/fedcm/setdelayenabled", HttpMethod.POST),
RESET_COOLDOWN, new CommandInfo("/session/:sessionId/fedcm/resetcooldown", HttpMethod.POST));

@Override
public Map<String, CommandInfo> getAdditionalCommands() {
return COMMANDS;
}

@Override
public Predicate<Capabilities> isApplicable() {
return caps -> IS_CHROMIUM_BROWSER.test(caps.getBrowserName());
}

@Override
public Class<HasFedCm> getDescribedInterface() {
return HasFedCm.class;
}

@Override
public HasFedCm getImplementation(Capabilities capabilities, ExecuteMethod executeMethod) {
return new HasFedCm() {
@Override
public void setDelayEnabled(boolean enabled) {
executeMethod.execute(SET_DELAY_ENABLED, ImmutableMap.of("enabled", enabled));
}

@Override
public void resetCooldown() {
executeMethod.execute(RESET_COOLDOWN, null);
}

@Override
public FedCmDialog getFedCmDialog() {
FedCmDialog dialog = new FedCmDialog(executeMethod);
try {
// As long as this does not throw, we're good.
dialog.getDialogType();
return dialog;
} catch (NoAlertPresentException e) {
return null;
}
}
};
}
}
18 changes: 18 additions & 0 deletions java/src/org/openqa/selenium/chromium/ChromiumDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class ChromiumDriver extends RemoteWebDriver implements
HasCasting,
HasCdp,
HasDevTools,
HasFedCm,
HasLaunchApp,
HasLogEvents,
HasNetworkConditions,
Expand All @@ -92,6 +93,7 @@ public class ChromiumDriver extends RemoteWebDriver implements
private final HasNetworkConditions networkConditions;
private final HasPermissions permissions;
private final HasLaunchApp launch;
private final HasFedCm fedcm;
private final Optional<Connection> connection;
private final Optional<DevTools> devTools;
protected HasCasting casting;
Expand All @@ -105,6 +107,7 @@ protected ChromiumDriver(CommandExecutor commandExecutor, Capabilities capabilit
networkConnection = new RemoteNetworkConnection(getExecuteMethod());
networkConditions = new AddHasNetworkConditions().getImplementation(getCapabilities(), getExecuteMethod());
launch = new AddHasLaunchApp().getImplementation(getCapabilities(), getExecuteMethod());
fedcm = new AddHasFedCm().getImplementation(getCapabilities(), getExecuteMethod());

HttpClient.Factory factory = HttpClient.Factory.createDefault();
Capabilities originalCapabilities = super.getCapabilities();
Expand Down Expand Up @@ -206,6 +209,21 @@ public void launchApp(String id) {
launch.launchApp(id);
}

@Override
public void setDelayEnabled(boolean enabled) {
fedcm.setDelayEnabled(enabled);
}

@Override
public void resetCooldown() {
fedcm.resetCooldown();
}

@Override
public FedCmDialog getFedCmDialog() {
return fedcm.getFedCmDialog();
}

@Override
public Map<String, Object> executeCdpCommand(String commandName, Map<String, Object> parameters) {
Require.nonNull("Command Name", commandName);
Expand Down
83 changes: 83 additions & 0 deletions java/src/org/openqa/selenium/chromium/FedCmAccount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// 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.chromium;

import java.util.Map;

public class FedCmAccount {
private final String accountId;
private final String email;
private final String name;
private final String givenName;
private final String pictureUrl;
private final String idpConfigUrl;
private final String loginState;
private final String termsOfServiceUrl;
private final String privacyPolicyUrl;

public static final String LOGIN_STATE_SIGNIN = "SignIn";
public static final String LOGIN_STATE_SIGNUP = "SignUp";

FedCmAccount(Map<String, String> dict) {
accountId = (String) dict.getOrDefault("accountId", null);
email = (String) dict.getOrDefault("email", null);
name = (String) dict.getOrDefault("name", null);
givenName = (String) dict.getOrDefault("givenName", null);
pictureUrl = (String) dict.getOrDefault("pictureUrl", null);
idpConfigUrl = (String) dict.getOrDefault("idpConfigUrl", null);
loginState = (String) dict.getOrDefault("loginState", null);
termsOfServiceUrl = (String) dict.getOrDefault("termsOfServiceUrl", null);
privacyPolicyUrl = (String) dict.getOrDefault("privacyPolicyUrl", null);
}

public String getAccountid() {
return accountId;
}

public String getEmail() {
return email;
}

public String getName() {
return name;
}

public String getGivenName() {
return givenName;
}

public String getPictureUrl() {
return pictureUrl;
}

public String getIdpConfigUrl() {
return idpConfigUrl;
}

public String getLoginState() {
return loginState;
}

public String getTermsOfServiceUrl() {
return termsOfServiceUrl;
}

public String getPrivacyPolicyUrl() {
return privacyPolicyUrl;
}
};
67 changes: 67 additions & 0 deletions java/src/org/openqa/selenium/chromium/FedCmDialog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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.chromium;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.google.common.collect.ImmutableMap;
import org.openqa.selenium.remote.ExecuteMethod;

public class FedCmDialog {
private final ExecuteMethod executeMethod;

public static final String DIALOG_TYPE_ACCOUNT_LIST = "AccountChooser";
public static final String DIALOG_TYPE_AUTH_REAUTH = "AutoReauthn";

FedCmDialog(ExecuteMethod executeMethod) {
this.executeMethod = executeMethod;
}

public void cancelDialog() {
executeMethod.execute(AddHasFedCm.CANCEL_DIALOG, null);
}

public void selectAccount(int index) {
executeMethod.execute(AddHasFedCm.SELECT_ACCOUNT, ImmutableMap.of("accountIndex", index));
}

public String getDialogType() {
return (String) executeMethod.execute(AddHasFedCm.GET_FEDCM_DIALOG_TYPE, null);
}

public String getTitle() {
Map<String, Object> result = (Map<String, Object>) executeMethod.execute(AddHasFedCm.GET_FEDCM_TITLE, null);
return (String) result.getOrDefault("title", null);
}

public String getSubtitle() {
Map<String, Object> result = (Map<String, Object>) executeMethod.execute(AddHasFedCm.GET_FEDCM_TITLE, null);
return (String) result.getOrDefault("subtitle", null);
}

public List<FedCmAccount> getAccounts() {
List<Map<String, String>> list = (List<Map<String, String>>) executeMethod.execute(AddHasFedCm.GET_ACCOUNTS, null);
ArrayList<FedCmAccount> accounts = new ArrayList<FedCmAccount>();
for (Map<String, String> map : list) {
accounts.add(new FedCmAccount(map));
}
return accounts;
}
}
41 changes: 41 additions & 0 deletions java/src/org/openqa/selenium/chromium/HasFedCm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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.chromium;

import org.openqa.selenium.Beta;

/**
* Used by classes to indicate that they can interact with FedCM dialogs.
*/
@Beta
public interface HasFedCm {
// FedCM by default delays promise resolution in failure cases for privacy
// reasons (https://fedidcg.github.io/FedCM/#ref-for-setdelayenabled);
// this function allows turning it off to let tests run faster where this
// is not relevant.
void setDelayEnabled(boolean enabled);

// If a user agent triggers a cooldown when the account chooser is dismissed,
// this function resets that cooldown so that the dialog can be triggered
// again immediately.
void resetCooldown();

// Gets the currently open FedCM dialog, or null if there is no dialog.
FedCmDialog getFedCmDialog();
}

0 comments on commit 5b42d87

Please sign in to comment.