Skip to content

Commit 826a6bd

Browse files
authored
[rb] Add FedCM support to the ruby selenium client (#13796)
Co-authored-by: aguspe <[email protected]>
1 parent 0770acd commit 826a6bd

File tree

16 files changed

+599
-3
lines changed

16 files changed

+599
-3
lines changed

common/src/web/fedcm/fedcm.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!DOCTYPE html>
22
<script>
33

4-
let configURL = `https://${location.host}/fedcm/fedcm.json`;
4+
let configURL = `http://${location.host}/fedcm/fedcm.json`;
55
let promise = null;
66

77
function triggerFedCm() {

common/src/web/fedcm/fedcm.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"accounts_endpoint": "accounts.json",
33
"client_metadata_endpoint": "client_metadata.json",
44
"id_assertion_endpoint": "id_assertion",
5-
"signin_url": "/signin"
5+
"signin_url": "/signin",
6+
"login_url": "/login"
67
}

rb/lib/selenium/webdriver/chromium/driver.rb

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Driver < WebDriver::Driver
2929
EXTENSIONS = [DriverExtensions::HasCDP,
3030
DriverExtensions::HasBiDi,
3131
DriverExtensions::HasCasting,
32+
DriverExtensions::HasFedCmDialog,
3233
DriverExtensions::HasNetworkConditions,
3334
DriverExtensions::HasNetworkInterception,
3435
DriverExtensions::HasWebStorage,

rb/lib/selenium/webdriver/common.rb

+3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
require 'selenium/webdriver/common/driver_extensions/has_cdp'
9090
require 'selenium/webdriver/common/driver_extensions/has_casting'
9191
require 'selenium/webdriver/common/driver_extensions/has_launching'
92+
require 'selenium/webdriver/common/driver_extensions/has_fedcm_dialog'
9293
require 'selenium/webdriver/common/keys'
9394
require 'selenium/webdriver/common/profile_helper'
9495
require 'selenium/webdriver/common/options'
@@ -99,3 +100,5 @@
99100
require 'selenium/webdriver/common/websocket_connection'
100101
require 'selenium/webdriver/common/child_process'
101102
require 'selenium/webdriver/common/script'
103+
require 'selenium/webdriver/common/fedcm/account'
104+
require 'selenium/webdriver/common/fedcm/dialog'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# frozen_string_literal: true
2+
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
module Selenium
21+
module WebDriver
22+
module DriverExtensions
23+
module HasFedCmDialog
24+
# Disables the promise rejection delay for FedCm.
25+
#
26+
# FedCm by default delays promise resolution in failure cases for privacy reasons.
27+
# This method allows turning it off to let tests run faster where this is not relevant.
28+
def enable_fedcm_delay=(enable)
29+
@bridge.fedcm_delay(enable)
30+
end
31+
32+
# Resets the FedCm dialog cooldown.
33+
#
34+
# If a user agent triggers a cooldown when the account chooser is dismissed,
35+
# this method resets that cooldown so that the dialog can be triggered again immediately.
36+
def reset_fedcm_cooldown
37+
@bridge.reset_fedcm_cooldown
38+
end
39+
40+
def fedcm_dialog
41+
@fedcm_dialog ||= FedCM::Dialog.new(@bridge)
42+
end
43+
44+
def wait_for_fedcm_dialog(timeout: 5, interval: 0.2, message: nil, ignore: nil)
45+
wait = Wait.new(timeout: timeout, interval: interval, message: message, ignore: ignore)
46+
wait.until do
47+
fedcm_dialog if fedcm_dialog.type
48+
rescue Error::NoSuchAlertError
49+
nil
50+
end
51+
end
52+
end # HasFedCmDialog
53+
end # DriverExtensions
54+
end # WebDriver
55+
end # Selenium
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
module Selenium
21+
module WebDriver
22+
module FedCM
23+
autoload :Account, 'fedcm/account'
24+
autoload :Dialog, 'fedcm/dialog'
25+
end # FedCM
26+
end # WebDriver
27+
end # Selenium
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# frozen_string_literal: true
2+
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
module Selenium
21+
module WebDriver
22+
module FedCM
23+
# Represents an account displayed in a FedCm account list.
24+
# See: https://fedidcg.github.io/FedCM/#dictdef-identityprovideraccount
25+
# https://fedidcg.github.io/FedCM/#webdriver-accountlist
26+
class Account
27+
LOGIN_STATE_SIGNIN = 'SignIn'
28+
LOGIN_STATE_SIGNUP = 'SignUp'
29+
30+
attr_reader :account_id, :email, :name, :given_name, :picture_url,
31+
:idp_config_url, :login_state, :terms_of_service_url, :privacy_policy_url
32+
33+
# Initializes a new account with the provided attributes.
34+
#
35+
# @param [Hash]
36+
def initialize(**args)
37+
@account_id = args['accountId']
38+
@email = args['email']
39+
@name = args['name']
40+
@given_name = args['givenName']
41+
@picture_url = args['pictureUrl']
42+
@idp_config_url = args['idpConfigUrl']
43+
@login_state = args['loginState']
44+
@terms_of_service_url = args['termsOfServiceUrl']
45+
@privacy_policy_url = args['privacyPolicyUrl']
46+
end
47+
end # Account
48+
end # FedCM
49+
end # WebDriver
50+
end # Selenium
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# frozen_string_literal: true
2+
3+
# Licensed to the Software Freedom Conservancy (SFC) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The SFC licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
20+
module Selenium
21+
module WebDriver
22+
module FedCM
23+
class Dialog
24+
def initialize(bridge)
25+
@bridge = bridge
26+
end
27+
28+
DIALOG_TYPE_ACCOUNT_LIST = 'AccountChooser'
29+
DIALOG_TYPE_AUTO_REAUTH = 'AutoReauthn'
30+
31+
# Closes the dialog as if the user had clicked X.
32+
def click
33+
@bridge.click_fedcm_dialog_button
34+
end
35+
36+
# Closes the dialog as if the user had clicked X.
37+
def cancel
38+
@bridge.cancel_fedcm_dialog
39+
end
40+
41+
# Selects an account as if the user had clicked on it.
42+
#
43+
# @param [Integer] index The index of the account to select from the list returned by get_accounts.
44+
def select_account(index)
45+
@bridge.select_fedcm_account index
46+
end
47+
48+
# Returns the type of the open dialog.
49+
#
50+
# One of DIALOG_TYPE_ACCOUNT_LIST and DIALOG_TYPE_AUTO_REAUTH.
51+
def type
52+
@bridge.fedcm_dialog_type
53+
end
54+
55+
# Returns the title of the dialog.
56+
def title
57+
@bridge.fedcm_title
58+
end
59+
60+
# Returns the subtitle of the dialog or nil if none.
61+
def subtitle
62+
@bridge.fedcm_subtitle
63+
end
64+
65+
# Returns the accounts shown in the account chooser.
66+
#
67+
# If this is an auto reauth dialog, returns the single account that is being signed in.
68+
def accounts
69+
@bridge.fedcm_account_list.map { |account| Account.new(**account) }
70+
end
71+
end # Dialog
72+
end # FedCM
73+
end # WebDriver
74+
end # Selenium

rb/lib/selenium/webdriver/remote/bridge.rb

+40
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,46 @@ def user_verified(verified, authenticator_id)
602602
execute :set_user_verified, {authenticatorId: authenticator_id}, {isUserVerified: verified}
603603
end
604604

605+
#
606+
# federated-credential management
607+
#
608+
609+
def cancel_fedcm_dialog
610+
execute :cancel_fedcm_dialog
611+
end
612+
613+
def select_fedcm_account(index)
614+
execute :select_fedcm_account, {}, {accountIndex: index}
615+
end
616+
617+
def fedcm_dialog_type
618+
execute :get_fedcm_dialog_type
619+
end
620+
621+
def fedcm_title
622+
execute(:get_fedcm_title).fetch('title')
623+
end
624+
625+
def fedcm_subtitle
626+
execute(:get_fedcm_title).fetch('subtitle', nil)
627+
end
628+
629+
def fedcm_account_list
630+
execute :get_fedcm_account_list
631+
end
632+
633+
def fedcm_delay(enabled)
634+
execute :set_fedcm_delay, {}, {enabled: enabled}
635+
end
636+
637+
def reset_fedcm_cooldown
638+
execute :reset_fedcm_cooldown
639+
end
640+
641+
def click_fedcm_dialog_button
642+
execute :click_fedcm_dialog_button, {}, {dialogButton: 'ConfirmIdpLoginContinue'}
643+
end
644+
605645
def bidi
606646
msg = 'BiDi must be enabled by setting #web_socket_url to true in options class'
607647
raise(WebDriver::Error::WebDriverError, msg)

rb/lib/selenium/webdriver/remote/bridge/commands.rb

+13-1
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,20 @@ class Bridge
155155
remove_credential: [:delete,
156156
'session/:session_id/webauthn/authenticator/:authenticatorId/credentials/:credentialId'],
157157
remove_all_credentials: [:delete, 'session/:session_id/webauthn/authenticator/:authenticatorId/credentials'],
158-
set_user_verified: [:post, 'session/:session_id/webauthn/authenticator/:authenticatorId/uv']
158+
set_user_verified: [:post, 'session/:session_id/webauthn/authenticator/:authenticatorId/uv'],
159159

160+
#
161+
# federated-credential management
162+
#
163+
164+
get_fedcm_title: [:get, 'session/:session_id/fedcm/gettitle'],
165+
get_fedcm_dialog_type: [:get, 'session/:session_id/fedcm/getdialogtype'],
166+
get_fedcm_account_list: [:get, 'session/:session_id/fedcm/accountlist'],
167+
click_fedcm_dialog_button: [:post, 'session/:session_id/fedcm/clickdialogbutton'],
168+
cancel_fedcm_dialog: [:post, 'session/:session_id/fedcm/canceldialog'],
169+
select_fedcm_account: [:post, 'session/:session_id/fedcm/selectaccount'],
170+
set_fedcm_delay: [:post, 'session/:session_id/fedcm/setdelayenabled'],
171+
reset_fedcm_cooldown: [:post, 'session/:session_id/fedcm/resetcooldown']
160172
}.freeze
161173
end # Bridge
162174
end # Remote
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
module Selenium
2+
module WebDriver
3+
module FedCM
4+
# Represents an account displayed in a FedCm account list.
5+
# See: https://fedidcg.github.io/FedCM/#dictdef-identityprovideraccount
6+
# https://fedidcg.github.io/FedCM/#webdriver-accountlist
7+
class Account
8+
@account_id: String
9+
10+
@email: String
11+
12+
@name: String
13+
14+
@given_name: String
15+
16+
@picture_url: String
17+
18+
@idp_config_url: String
19+
20+
@login_state: String
21+
22+
@terms_of_service_url: String
23+
24+
@privacy_policy_url: String
25+
26+
LOGIN_STATE_SIGNIN: String
27+
28+
LOGIN_STATE_SIGNUP: String
29+
30+
attr_reader account_id: String
31+
32+
attr_reader email: String
33+
34+
attr_reader name: String
35+
36+
attr_reader given_name: String
37+
38+
attr_reader picture_url: String
39+
40+
attr_reader idp_config_url: String
41+
42+
attr_reader login_state: String
43+
44+
attr_reader terms_of_service_url: String
45+
46+
attr_reader privacy_policy_url: String
47+
48+
def initialize: (**untyped args) -> void
49+
end
50+
end
51+
end
52+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module Selenium
2+
module WebDriver
3+
module FedCM
4+
class Dialog
5+
DIALOG_TYPE_ACCOUNT_LIST: String
6+
DIALOG_TYPE_AUTO_REAUTH: String
7+
8+
@bridge: Remote::Bridge
9+
10+
def accounts: -> Array[Account]
11+
12+
def cancel: -> Remote::Response?
13+
14+
def click: -> untyped
15+
16+
def select_account: (Integer index) -> Remote::Response?
17+
18+
def subtitle: -> (String | Remote::Response)?
19+
20+
def title: -> (String | Remote::Response)
21+
22+
def type: -> (String | Remote::Response)
23+
end
24+
end
25+
end
26+
end

0 commit comments

Comments
 (0)