Commit 31219d16 authored by William Lin's avatar William Lin Committed by Commit Bot

Allow GetAuthToken to skip the account chooser

Unbundled consent will allow GetAuthToken to make API calls to the
server to automatically select an account to request permissions from.
This account can be one specified by the extension or one that
previously granted permissions to the extension. This can be useful for
users so they don't need to choose or remember the "correct" account in
the account chooser. This CL adds this feature of skipping the account
chooser and puts it behind a flag to enable it.

Bug: 1100535
Change-Id: I859784b5d3a533144c48802845b3317ce0e01935
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2363879
Commit-Queue: William Lin <williamlin@google.com>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarAlex Ilin <alexilin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800794}
parent b8b0a643
......@@ -96,6 +96,11 @@ bool IsReturnScopesInGetAuthTokenEnabled() {
extensions_features::kReturnScopesInGetAuthToken);
}
bool IsSelectedUserIdInGetAuthTokenEnabled() {
return base::FeatureList::IsEnabled(
extensions_features::kSelectedUserIdInGetAuthToken);
}
} // namespace
IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction()
......@@ -179,6 +184,7 @@ ExtensionFunction::ResponseAction IdentityGetAuthTokenFunction::Run() {
.value_or("");
}
selected_gaia_id_ = gaia_id;
// From here on out, results must be returned asynchronously.
StartAsyncRun();
......@@ -1037,13 +1043,14 @@ IdentityGetAuthTokenFunction::CreateMintTokenFlow() {
std::string signin_scoped_device_id =
GetSigninScopedDeviceIdForProfile(GetProfile());
auto mint_token_flow = std::make_unique<OAuth2MintTokenFlow>(
this, OAuth2MintTokenFlow::Parameters(
extension()->id(), oauth2_client_id_,
std::vector<std::string>(token_key_.scopes.begin(),
token_key_.scopes.end()),
enable_granular_permissions_, signin_scoped_device_id,
consent_result_, GetOAuth2MintTokenFlowVersion(),
GetOAuth2MintTokenFlowChannel(), gaia_mint_token_mode_));
this,
OAuth2MintTokenFlow::Parameters(
extension()->id(), oauth2_client_id_,
std::vector<std::string>(token_key_.scopes.begin(),
token_key_.scopes.end()),
enable_granular_permissions_, signin_scoped_device_id,
GetSelectedUserId(), consent_result_, GetOAuth2MintTokenFlowVersion(),
GetOAuth2MintTokenFlowChannel(), gaia_mint_token_mode_));
return mint_token_flow;
}
......@@ -1080,4 +1087,12 @@ bool IdentityGetAuthTokenFunction::enable_granular_permissions() const {
return enable_granular_permissions_;
}
std::string IdentityGetAuthTokenFunction::GetSelectedUserId() const {
if (IsSelectedUserIdInGetAuthTokenEnabled() &&
selected_gaia_id_ == token_key_.account_info.gaia)
return selected_gaia_id_;
return "";
}
} // namespace extensions
......@@ -118,6 +118,12 @@ class IdentityGetAuthTokenFunction : public ExtensionFunction,
Profile* GetProfile() const;
// Returns the gaia id of the account requested by or previously selected for
// this extension if the account is available on the device. Otherwise,
// returns an empty string.
// Exposed for testing.
std::string GetSelectedUserId() const;
// Pending request for an access token from the device account (via
// DeviceOAuth2TokenService).
std::unique_ptr<OAuth2AccessTokenManager::Request>
......@@ -228,6 +234,10 @@ class IdentityGetAuthTokenFunction : public ExtensionFunction,
bool should_prompt_for_signin_ = false;
bool enable_granular_permissions_ = false;
// The gaia id of the account requested by or previously selected for this
// extension.
std::string selected_gaia_id_;
// Shown in the extension login prompt.
std::string email_for_default_web_account_;
......
......@@ -57,4 +57,10 @@ const base::Feature kReportKeepaliveUkm{"ReportKeepaliveUkm",
const base::Feature kReturnScopesInGetAuthToken{
"ReturnScopesInGetAuthToken", base::FEATURE_DISABLED_BY_DEFAULT};
// If enabled, allows the GetAuthToken API to provide the "selected_user_id"
// parameter to the server, indicating which account to request permissions
// from.
const base::Feature kSelectedUserIdInGetAuthToken{
"SelectedUserIdInGetAuthToken", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace extensions_features
......@@ -31,6 +31,8 @@ extern const base::Feature kReportKeepaliveUkm;
extern const base::Feature kReturnScopesInGetAuthToken;
extern const base::Feature kSelectedUserIdInGetAuthToken;
} // namespace extensions_features
#endif // EXTENSIONS_COMMON_EXTENSION_FEATURES_H_
......@@ -45,6 +45,8 @@ const char kOAuth2IssueTokenBodyFormat[] =
"&origin=%s"
"&lib_ver=%s"
"&release_channel=%s";
const char kOAuth2IssueTokenBodyFormatSelectedUserIdAddendum[] =
"&selected_user_id=%s";
const char kOAuth2IssueTokenBodyFormatDeviceIdAddendum[] =
"&device_id=%s&device_type=chrome";
const char kOAuth2IssueTokenBodyFormatConsentResultAddendum[] =
......@@ -150,6 +152,7 @@ OAuth2MintTokenFlow::Parameters::Parameters(
const std::vector<std::string>& scopes_arg,
bool enable_granular_permissions,
const std::string& device_id,
const std::string& selected_user_id,
const std::string& consent_result,
const std::string& version,
const std::string& channel,
......@@ -159,6 +162,7 @@ OAuth2MintTokenFlow::Parameters::Parameters(
scopes(scopes_arg),
enable_granular_permissions(enable_granular_permissions),
device_id(device_id),
selected_user_id(selected_user_id),
consent_result(consent_result),
version(version),
channel(channel),
......@@ -240,6 +244,11 @@ std::string OAuth2MintTokenFlow::CreateApiCallBody() {
kOAuth2IssueTokenBodyFormatDeviceIdAddendum,
net::EscapeUrlEncodedData(parameters_.device_id, true).c_str()));
}
if (!parameters_.selected_user_id.empty()) {
body.append(base::StringPrintf(
kOAuth2IssueTokenBodyFormatSelectedUserIdAddendum,
net::EscapeUrlEncodedData(parameters_.selected_user_id, true).c_str()));
}
if (!parameters_.consent_result.empty()) {
body.append(base::StringPrintf(
kOAuth2IssueTokenBodyFormatConsentResultAddendum,
......
......@@ -117,6 +117,7 @@ class OAuth2MintTokenFlow : public OAuth2ApiCallFlow {
const std::vector<std::string>& scopes_arg,
bool enable_granular_permissions,
const std::string& device_id,
const std::string& selected_user_id,
const std::string& consent_result,
const std::string& version,
const std::string& channel,
......@@ -129,6 +130,7 @@ class OAuth2MintTokenFlow : public OAuth2ApiCallFlow {
std::vector<std::string> scopes;
bool enable_granular_permissions;
std::string device_id;
std::string selected_user_id;
std::string consent_result;
std::string version;
std::string channel;
......
......@@ -235,29 +235,35 @@ class OAuth2MintTokenFlowTest : public testing::Test {
const network::mojom::URLResponseHeadPtr head_200_;
void CreateFlow(OAuth2MintTokenFlow::Mode mode) {
return CreateFlow(&delegate_, mode, false, "", "");
return CreateFlow(&delegate_, mode, false, "", "", "");
}
void CreateFlowWithEnableGranularPermissions(
const bool enable_granular_permissions) {
return CreateFlow(&delegate_, OAuth2MintTokenFlow::MODE_ISSUE_ADVICE,
enable_granular_permissions, "", "");
enable_granular_permissions, "", "", "");
}
void CreateFlowWithDeviceId(const std::string& device_id) {
return CreateFlow(&delegate_, OAuth2MintTokenFlow::MODE_ISSUE_ADVICE, false,
device_id, "");
device_id, "", "");
}
void CreateFlowWithSelectedUserId(const std::string& selected_user_id) {
return CreateFlow(&delegate_, OAuth2MintTokenFlow::MODE_ISSUE_ADVICE, false,
"", selected_user_id, "");
}
void CreateFlowWithConsentResult(const std::string& consent_result) {
return CreateFlow(&delegate_, OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE,
false, "", consent_result);
false, "", "", consent_result);
}
void CreateFlow(MockDelegate* delegate,
OAuth2MintTokenFlow::Mode mode,
const bool enable_granular_permissions,
const std::string& device_id,
const std::string& selected_user_id,
const std::string& consent_result) {
std::string ext_id = "ext1";
std::string client_id = "client1";
......@@ -265,9 +271,10 @@ class OAuth2MintTokenFlowTest : public testing::Test {
std::string channel = "test_channel";
std::vector<std::string> scopes(CreateTestScopes());
flow_ = std::make_unique<MockMintTokenFlow>(
delegate, OAuth2MintTokenFlow::Parameters(
ext_id, client_id, scopes, enable_granular_permissions,
device_id, consent_result, version, channel, mode));
delegate,
OAuth2MintTokenFlow::Parameters(
ext_id, client_id, scopes, enable_granular_permissions, device_id,
selected_user_id, consent_result, version, channel, mode));
}
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
......@@ -381,6 +388,21 @@ TEST_F(OAuth2MintTokenFlowTest, CreateApiCallBody) {
"&device_type=chrome");
EXPECT_EQ(expected_body, body);
}
{
CreateFlowWithSelectedUserId("user_id1");
std::string body = flow_->CreateApiCallBody();
std::string expected_body(
"force=false"
"&response_type=none"
"&scope=http://scope1+http://scope2"
"&enable_granular_permissions=false"
"&client_id=client1"
"&origin=ext1"
"&lib_ver=test_version"
"&release_channel=test_channel"
"&selected_user_id=user_id1");
EXPECT_EQ(expected_body, body);
}
{
CreateFlowWithConsentResult("consent1");
std::string body = flow_->CreateApiCallBody();
......@@ -707,7 +729,7 @@ TEST_F(OAuth2MintTokenFlowTest, ProcessApiCallSuccess_RemoteConsentFailure) {
TEST_F(OAuth2MintTokenFlowTest, ProcessApiCallFailure_NullDelegate) {
network::mojom::URLResponseHead head;
CreateFlow(nullptr, OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE, false, "",
"");
"", "");
ProcessApiCallFailure(net::ERR_FAILED, &head, nullptr);
histogram_tester_.ExpectUniqueSample(
kOAuth2MintTokenApiCallResultHistogram,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment