Skip to content

Commit

Permalink
[rb] Add FedCM support to the ruby selenium client (#13796)
Browse files Browse the repository at this point in the history
Co-authored-by: aguspe <agustin.pe94@gmail.com>
  • Loading branch information
aguspe and aguspe authored Jun 24, 2024
1 parent 0770acd commit 826a6bd
Show file tree
Hide file tree
Showing 16 changed files with 599 additions and 3 deletions.
2 changes: 1 addition & 1 deletion common/src/web/fedcm/fedcm.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<script>

let configURL = `https://${location.host}/fedcm/fedcm.json`;
let configURL = `http://${location.host}/fedcm/fedcm.json`;
let promise = null;

function triggerFedCm() {
Expand Down
3 changes: 2 additions & 1 deletion common/src/web/fedcm/fedcm.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"accounts_endpoint": "accounts.json",
"client_metadata_endpoint": "client_metadata.json",
"id_assertion_endpoint": "id_assertion",
"signin_url": "/signin"
"signin_url": "/signin",
"login_url": "/login"
}
1 change: 1 addition & 0 deletions rb/lib/selenium/webdriver/chromium/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Driver < WebDriver::Driver
EXTENSIONS = [DriverExtensions::HasCDP,
DriverExtensions::HasBiDi,
DriverExtensions::HasCasting,
DriverExtensions::HasFedCmDialog,
DriverExtensions::HasNetworkConditions,
DriverExtensions::HasNetworkInterception,
DriverExtensions::HasWebStorage,
Expand Down
3 changes: 3 additions & 0 deletions rb/lib/selenium/webdriver/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
require 'selenium/webdriver/common/driver_extensions/has_cdp'
require 'selenium/webdriver/common/driver_extensions/has_casting'
require 'selenium/webdriver/common/driver_extensions/has_launching'
require 'selenium/webdriver/common/driver_extensions/has_fedcm_dialog'
require 'selenium/webdriver/common/keys'
require 'selenium/webdriver/common/profile_helper'
require 'selenium/webdriver/common/options'
Expand All @@ -99,3 +100,5 @@
require 'selenium/webdriver/common/websocket_connection'
require 'selenium/webdriver/common/child_process'
require 'selenium/webdriver/common/script'
require 'selenium/webdriver/common/fedcm/account'
require 'selenium/webdriver/common/fedcm/dialog'
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

# 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.

module Selenium
module WebDriver
module DriverExtensions
module HasFedCmDialog
# Disables the promise rejection delay for FedCm.
#
# FedCm by default delays promise resolution in failure cases for privacy reasons.
# This method allows turning it off to let tests run faster where this is not relevant.
def enable_fedcm_delay=(enable)
@bridge.fedcm_delay(enable)
end

# Resets the FedCm dialog cooldown.
#
# If a user agent triggers a cooldown when the account chooser is dismissed,
# this method resets that cooldown so that the dialog can be triggered again immediately.
def reset_fedcm_cooldown
@bridge.reset_fedcm_cooldown
end

def fedcm_dialog
@fedcm_dialog ||= FedCM::Dialog.new(@bridge)
end

def wait_for_fedcm_dialog(timeout: 5, interval: 0.2, message: nil, ignore: nil)
wait = Wait.new(timeout: timeout, interval: interval, message: message, ignore: ignore)
wait.until do
fedcm_dialog if fedcm_dialog.type
rescue Error::NoSuchAlertError
nil
end
end
end # HasFedCmDialog
end # DriverExtensions
end # WebDriver
end # Selenium
27 changes: 27 additions & 0 deletions rb/lib/selenium/webdriver/common/fedcm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

# 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.

module Selenium
module WebDriver
module FedCM
autoload :Account, 'fedcm/account'
autoload :Dialog, 'fedcm/dialog'
end # FedCM
end # WebDriver
end # Selenium
50 changes: 50 additions & 0 deletions rb/lib/selenium/webdriver/common/fedcm/account.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

# 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.

module Selenium
module WebDriver
module FedCM
# Represents an account displayed in a FedCm account list.
# See: https://fedidcg.github.io/FedCM/#dictdef-identityprovideraccount
# https://fedidcg.github.io/FedCM/#webdriver-accountlist
class Account
LOGIN_STATE_SIGNIN = 'SignIn'
LOGIN_STATE_SIGNUP = 'SignUp'

attr_reader :account_id, :email, :name, :given_name, :picture_url,
:idp_config_url, :login_state, :terms_of_service_url, :privacy_policy_url

# Initializes a new account with the provided attributes.
#
# @param [Hash]
def initialize(**args)
@account_id = args['accountId']
@email = args['email']
@name = args['name']
@given_name = args['givenName']
@picture_url = args['pictureUrl']
@idp_config_url = args['idpConfigUrl']
@login_state = args['loginState']
@terms_of_service_url = args['termsOfServiceUrl']
@privacy_policy_url = args['privacyPolicyUrl']
end
end # Account
end # FedCM
end # WebDriver
end # Selenium
74 changes: 74 additions & 0 deletions rb/lib/selenium/webdriver/common/fedcm/dialog.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# frozen_string_literal: true

# 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.

module Selenium
module WebDriver
module FedCM
class Dialog
def initialize(bridge)
@bridge = bridge
end

DIALOG_TYPE_ACCOUNT_LIST = 'AccountChooser'
DIALOG_TYPE_AUTO_REAUTH = 'AutoReauthn'

# Closes the dialog as if the user had clicked X.
def click
@bridge.click_fedcm_dialog_button
end

# Closes the dialog as if the user had clicked X.
def cancel
@bridge.cancel_fedcm_dialog
end

# Selects an account as if the user had clicked on it.
#
# @param [Integer] index The index of the account to select from the list returned by get_accounts.
def select_account(index)
@bridge.select_fedcm_account index
end

# Returns the type of the open dialog.
#
# One of DIALOG_TYPE_ACCOUNT_LIST and DIALOG_TYPE_AUTO_REAUTH.
def type
@bridge.fedcm_dialog_type
end

# Returns the title of the dialog.
def title
@bridge.fedcm_title
end

# Returns the subtitle of the dialog or nil if none.
def subtitle
@bridge.fedcm_subtitle
end

# Returns the accounts shown in the account chooser.
#
# If this is an auto reauth dialog, returns the single account that is being signed in.
def accounts
@bridge.fedcm_account_list.map { |account| Account.new(**account) }
end
end # Dialog
end # FedCM
end # WebDriver
end # Selenium
40 changes: 40 additions & 0 deletions rb/lib/selenium/webdriver/remote/bridge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,46 @@ def user_verified(verified, authenticator_id)
execute :set_user_verified, {authenticatorId: authenticator_id}, {isUserVerified: verified}
end

#
# federated-credential management
#

def cancel_fedcm_dialog
execute :cancel_fedcm_dialog
end

def select_fedcm_account(index)
execute :select_fedcm_account, {}, {accountIndex: index}
end

def fedcm_dialog_type
execute :get_fedcm_dialog_type
end

def fedcm_title
execute(:get_fedcm_title).fetch('title')
end

def fedcm_subtitle
execute(:get_fedcm_title).fetch('subtitle', nil)
end

def fedcm_account_list
execute :get_fedcm_account_list
end

def fedcm_delay(enabled)
execute :set_fedcm_delay, {}, {enabled: enabled}
end

def reset_fedcm_cooldown
execute :reset_fedcm_cooldown
end

def click_fedcm_dialog_button
execute :click_fedcm_dialog_button, {}, {dialogButton: 'ConfirmIdpLoginContinue'}
end

def bidi
msg = 'BiDi must be enabled by setting #web_socket_url to true in options class'
raise(WebDriver::Error::WebDriverError, msg)
Expand Down
14 changes: 13 additions & 1 deletion rb/lib/selenium/webdriver/remote/bridge/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,20 @@ class Bridge
remove_credential: [:delete,
'session/:session_id/webauthn/authenticator/:authenticatorId/credentials/:credentialId'],
remove_all_credentials: [:delete, 'session/:session_id/webauthn/authenticator/:authenticatorId/credentials'],
set_user_verified: [:post, 'session/:session_id/webauthn/authenticator/:authenticatorId/uv']
set_user_verified: [:post, 'session/:session_id/webauthn/authenticator/:authenticatorId/uv'],

#
# federated-credential management
#

get_fedcm_title: [:get, 'session/:session_id/fedcm/gettitle'],
get_fedcm_dialog_type: [:get, 'session/:session_id/fedcm/getdialogtype'],
get_fedcm_account_list: [:get, 'session/:session_id/fedcm/accountlist'],
click_fedcm_dialog_button: [:post, 'session/:session_id/fedcm/clickdialogbutton'],
cancel_fedcm_dialog: [:post, 'session/:session_id/fedcm/canceldialog'],
select_fedcm_account: [:post, 'session/:session_id/fedcm/selectaccount'],
set_fedcm_delay: [:post, 'session/:session_id/fedcm/setdelayenabled'],
reset_fedcm_cooldown: [:post, 'session/:session_id/fedcm/resetcooldown']
}.freeze
end # Bridge
end # Remote
Expand Down
52 changes: 52 additions & 0 deletions rb/sig/lib/selenium/webdriver/fedcm/account.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module Selenium
module WebDriver
module FedCM
# Represents an account displayed in a FedCm account list.
# See: https://fedidcg.github.io/FedCM/#dictdef-identityprovideraccount
# https://fedidcg.github.io/FedCM/#webdriver-accountlist
class Account
@account_id: String

@email: String

@name: String

@given_name: String

@picture_url: String

@idp_config_url: String

@login_state: String

@terms_of_service_url: String

@privacy_policy_url: String

LOGIN_STATE_SIGNIN: String

LOGIN_STATE_SIGNUP: String

attr_reader account_id: String

attr_reader email: String

attr_reader name: String

attr_reader given_name: String

attr_reader picture_url: String

attr_reader idp_config_url: String

attr_reader login_state: String

attr_reader terms_of_service_url: String

attr_reader privacy_policy_url: String

def initialize: (**untyped args) -> void
end
end
end
end
26 changes: 26 additions & 0 deletions rb/sig/lib/selenium/webdriver/fedcm/dialog.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Selenium
module WebDriver
module FedCM
class Dialog
DIALOG_TYPE_ACCOUNT_LIST: String
DIALOG_TYPE_AUTO_REAUTH: String

@bridge: Remote::Bridge

def accounts: -> Array[Account]

def cancel: -> Remote::Response?

def click: -> untyped

def select_account: (Integer index) -> Remote::Response?

def subtitle: -> (String | Remote::Response)?

def title: -> (String | Remote::Response)

def type: -> (String | Remote::Response)
end
end
end
end
Loading

0 comments on commit 826a6bd

Please sign in to comment.