Commit 7370efa1 authored by Jialiu Lin's avatar Jialiu Lin Committed by Commit Bot

Retrieve advanced protection bit from ID token (Non-CrOS desktop chrome)

Advanced Protection (Titanium) service flag is included in client ID
token if user is in this program. This CL parses client ID token to
determine if Advanced Protection serice flag is available. Very similar
to how we get the child account bit for unicorn.

Service side of work is tracked in b/109872750.

Change-Id: Iaf9490fb2bd16b61480bbf1c875ba451d25f853d
Bug: 866620
Reviewed-on: https://chromium-review.googlesource.com/1155813Reviewed-by: default avatarMihai Sardarescu <msarda@chromium.org>
Commit-Queue: Jialiu Lin <jialiul@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580935}
parent 84ecb1d1
......@@ -283,7 +283,8 @@ TEST_F(DiceResponseHandlerTest, Signin) {
// Simulate GaiaAuthFetcher success.
signin_client_.consumer_->OnClientOAuthSuccess(
GaiaAuthConsumer::ClientOAuthResult("refresh_token", "access_token", 10,
false /* is_child_account */));
false /* is_child_account */,
false /* is_advanced_protection*/));
// Check that the token has been inserted in the token service.
EXPECT_TRUE(token_service_.RefreshTokenIsAvailable(account_id));
EXPECT_TRUE(auth_error_email_.empty());
......@@ -342,7 +343,8 @@ TEST_F(DiceResponseHandlerTest, SigninRepeatedWithSameAccount) {
ASSERT_THAT(signin_client_.consumer_, testing::IsNull());
// Simulate GaiaAuthFetcher success for the first request.
consumer->OnClientOAuthSuccess(GaiaAuthConsumer::ClientOAuthResult(
"refresh_token", "access_token", 10, false /* is_child_account */));
"refresh_token", "access_token", 10, false /* is_child_account */,
false /* is_advanced_protection*/));
// Check that the token has been inserted in the token service.
EXPECT_TRUE(token_service_.RefreshTokenIsAvailable(account_id));
}
......@@ -378,12 +380,14 @@ TEST_F(DiceResponseHandlerTest, SigninWithTwoAccounts) {
ASSERT_THAT(consumer_2, testing::NotNull());
// Simulate GaiaAuthFetcher success for the first request.
consumer_1->OnClientOAuthSuccess(GaiaAuthConsumer::ClientOAuthResult(
"refresh_token", "access_token", 10, false /* is_child_account */));
"refresh_token", "access_token", 10, false /* is_child_account */,
false /* is_advanced_protection*/));
// Check that the token has been inserted in the token service.
EXPECT_TRUE(token_service_.RefreshTokenIsAvailable(account_id_1));
// Simulate GaiaAuthFetcher success for the second request.
consumer_2->OnClientOAuthSuccess(GaiaAuthConsumer::ClientOAuthResult(
"refresh_token", "access_token", 10, false /* is_child_account */));
"refresh_token", "access_token", 10, false /* is_child_account */,
false /* is_advanced_protection*/));
// Check that the token has been inserted in the token service.
EXPECT_TRUE(token_service_.RefreshTokenIsAvailable(account_id_2));
// Check that the reconcilor was blocked and unblocked exactly once.
......@@ -407,7 +411,8 @@ TEST_F(DiceResponseHandlerTest, SigninEnableSyncAfterRefreshTokenFetched) {
// Simulate GaiaAuthFetcher success.
signin_client_.consumer_->OnClientOAuthSuccess(
GaiaAuthConsumer::ClientOAuthResult("refresh_token", "access_token", 10,
false /* is_child_account */));
false /* is_child_account */,
false /* is_advanced_protection*/));
// Check that the token has been inserted in the token service.
EXPECT_TRUE(token_service_.RefreshTokenIsAvailable(account_id));
// Check that delegate was not called to enable sync.
......@@ -446,7 +451,8 @@ TEST_F(DiceResponseHandlerTest, SigninEnableSyncBeforeRefreshTokenFetched) {
// Simulate GaiaAuthFetcher success.
signin_client_.consumer_->OnClientOAuthSuccess(
GaiaAuthConsumer::ClientOAuthResult("refresh_token", "access_token", 10,
false /* is_child_account */));
false /* is_child_account */,
false /* is_advanced_protection*/));
// Check that the token has been inserted in the token service.
EXPECT_TRUE(token_service_.RefreshTokenIsAvailable(account_id));
// Check that delegate was called to enable sync.
......@@ -669,7 +675,8 @@ TEST_F(DiceResponseHandlerTest, SigninSignoutDifferentAccount) {
// Allow the remaining fetcher to complete.
signin_client_.consumer_->OnClientOAuthSuccess(
GaiaAuthConsumer::ClientOAuthResult("refresh_token", "access_token", 10,
false /* is_child_account */));
false /* is_child_account */,
false /* is_advanced_protection*/));
EXPECT_EQ(
0u, dice_response_handler_->GetPendingDiceTokenFetchersCountForTesting());
// Check that the right token is available.
......@@ -717,7 +724,8 @@ TEST_F(DiceResponseHandlerTest, FixAuthErrorSignOutDuringRequest) {
// Simulate GaiaAuthFetcher success.
signin_client_.consumer_->OnClientOAuthSuccess(
GaiaAuthConsumer::ClientOAuthResult("refresh_token", "access_token", 10,
false /* is_child_account */));
false /* is_child_account */,
false /* is_advanced_protection*/));
// Check that the token has not been inserted in the token service.
EXPECT_FALSE(token_service_.RefreshTokenIsAvailable(account_id));
}
......@@ -750,7 +758,8 @@ TEST_F(DiceResponseHandlerTest, FixAuthError) {
// Simulate GaiaAuthFetcher success.
signin_client_.consumer_->OnClientOAuthSuccess(
GaiaAuthConsumer::ClientOAuthResult("refresh_token", "access_token", 10,
false /* is_child_account */));
false /* is_child_account */,
false /* is_advanced_protection*/));
// Check that the token has not been inserted in the token service.
EXPECT_TRUE(token_service_.RefreshTokenIsAvailable(account_id));
EXPECT_TRUE(token_service_observer->token_received());
......
......@@ -40,11 +40,13 @@ GaiaAuthConsumer::ClientOAuthResult::ClientOAuthResult(
const std::string& new_refresh_token,
const std::string& new_access_token,
int new_expires_in_secs,
bool new_is_child_account)
bool new_is_child_account,
bool new_is_under_advanced_protection)
: refresh_token(new_refresh_token),
access_token(new_access_token),
expires_in_secs(new_expires_in_secs),
is_child_account(new_is_child_account) {}
is_child_account(new_is_child_account),
is_under_advanced_protection(new_is_under_advanced_protection) {}
GaiaAuthConsumer::ClientOAuthResult::ClientOAuthResult(
const ClientOAuthResult& other) = default;
......
......@@ -45,7 +45,8 @@ class GaiaAuthConsumer {
ClientOAuthResult(const std::string& new_refresh_token,
const std::string& new_access_token,
int new_expires_in_secs,
bool is_child_account);
bool is_child_account,
bool is_under_advanced_protection);
ClientOAuthResult(const ClientOAuthResult& other);
~ClientOAuthResult();
......@@ -63,6 +64,9 @@ class GaiaAuthConsumer {
// Whether the authenticated user is a child account.
bool is_child_account;
// Whether the authenticated user is in advanced protection program.
bool is_under_advanced_protection;
};
// Possible server responses to a token revocation request.
......
......@@ -75,10 +75,12 @@ ExtractOAuth2TokenPairResponse(const std::string& data) {
if (!dict->GetStringWithoutPathExpansion("id_token", &id_token)) {
LOG(ERROR) << "Missing ID token on refresh token fetch response.";
}
bool is_child_account = gaia::IsChildAccountFromIdToken(id_token);
gaia::TokenServiceFlags service_flags = gaia::ParseServiceFlags(id_token);
return std::make_unique<const GaiaAuthConsumer::ClientOAuthResult>(
refresh_token, access_token, expires_in_secs, is_child_account);
refresh_token, access_token, expires_in_secs,
service_flags.is_child_account,
service_flags.is_under_advanced_protection);
}
void GetCookiesFromResponse(
......
......@@ -331,7 +331,8 @@ TEST_F(GaiaAuthFetcherTest, OAuthLoginTokenSuccess) {
EXPECT_CALL(consumer, OnClientOAuthCode("test-code")).Times(0);
EXPECT_CALL(consumer,
OnClientOAuthSuccess(GaiaAuthConsumer::ClientOAuthResult(
"rt1", "at1", 3600, false /* is_child_account */)))
"rt1", "at1", 3600, false /* is_child_account */,
false /* is_advanced_protection */)))
.Times(1);
TestGaiaAuthFetcher auth(&consumer, std::string(), GetURLLoaderFactory());
......@@ -357,7 +358,8 @@ TEST_F(GaiaAuthFetcherTest, OAuthLoginTokenSuccessNoTokenFetch) {
EXPECT_CALL(consumer, OnClientOAuthCode("test-code")).Times(1);
EXPECT_CALL(consumer,
OnClientOAuthSuccess(GaiaAuthConsumer::ClientOAuthResult(
"", "", 0, false /* is_child_account */)))
"", "", 0, false /* is_child_account */,
false /* is_advanced_protection */)))
.Times(0);
net::TestURLFetcherFactory factory;
......
......@@ -15,6 +15,10 @@ namespace {
// The name of the service flag that defines the account is Unicorn.
const char kChildAccountServiceFlag[] = "uca";
// The name of the service flag that defines the account is in advanced
// protection program.
const char kAdvancedProtectionAccountServiceFlag[] = "tia";
// The key indexing service flags in the ID token JSON.
const char kServicesKey[] = "services";
......@@ -79,15 +83,24 @@ bool GetServiceFlags(const std::string id_token,
namespace gaia {
bool IsChildAccountFromIdToken(const std::string& id_token) {
TokenServiceFlags ParseServiceFlags(const std::string& id_token) {
TokenServiceFlags token_service_flags;
std::vector<std::string> service_flags;
if (!GetServiceFlags(id_token, &service_flags)) {
// If service flags can’t be obtained, then assume it’s not a child account.
VLOG(1) << "Assuming non-child account due to decoding failure";
return false;
// If service flags can’t be obtained, then assume these service flags
// are not set.
VLOG(1) << "Assuming the account doesn't have any service flag set "
<< "due to decoding failure";
return token_service_flags;
}
return std::find(service_flags.begin(), service_flags.end(),
kChildAccountServiceFlag) != service_flags.end();
token_service_flags.is_child_account =
std::find(service_flags.begin(), service_flags.end(),
kChildAccountServiceFlag) != service_flags.end();
token_service_flags.is_under_advanced_protection =
std::find(service_flags.begin(), service_flags.end(),
kAdvancedProtectionAccountServiceFlag) != service_flags.end();
return token_service_flags;
}
} // namespace gaia
......@@ -10,13 +10,18 @@
// This file holds methods decodes the id token received for OAuth2 token
// endpoint, and derive useful information from it, such as whether the account
// is a child account.
// is a child account, and whether this account is under advanced protection.
namespace gaia {
// Detects if the signed-in account is a child account from service flags
// retrieved in the ID token.
bool IsChildAccountFromIdToken(const std::string& id_token);
// Service flags extracted from ID token.
struct TokenServiceFlags {
bool is_child_account = false;
bool is_under_advanced_protection = false;
};
// Parses service flag from ID token.
TokenServiceFlags ParseServiceFlags(const std::string& id_token);
} // namespace gaia
......
......@@ -39,41 +39,59 @@ const char kIdTokenChildAccount[] =
"dummy-header."
"eyAic2VydmljZXMiOiBbInVjYSJdIH0=" // payload: { "services": ["uca"] }
".dummy-signature";
const char kIdTokenAdvancedProtectionAccount[] =
"dummy-header."
"eyAic2VydmljZXMiOiBbInRpYSJdIH0=" // payload: { "services": ["tia"] }
".dummy-signature";
const char kIdTokenChildAndAdvancedProtectionAccount[] =
"dummy-header."
"eyAic2VydmljZXMiOiBbInRpYSIsICJ1Y2EiXSB9"
".dummy-signature"; // payload: { "services": ["tia", "uca"] }
class OAuth2IdTokenDecoderTest : public testing::Test {};
TEST_F(OAuth2IdTokenDecoderTest, Invalid) {
std::string id_token_ = kIdTokenInvalidJwt;
bool is_child_account = gaia::IsChildAccountFromIdToken(id_token_);
EXPECT_EQ(is_child_account, false);
EXPECT_FALSE(gaia::ParseServiceFlags(kIdTokenInvalidJwt).is_child_account);
id_token_ = kIdTokenInvalidJson;
is_child_account = gaia::IsChildAccountFromIdToken(id_token_);
EXPECT_EQ(is_child_account, false);
EXPECT_FALSE(gaia::ParseServiceFlags(kIdTokenInvalidJson).is_child_account);
id_token_ = kIdTokenMissingServices;
is_child_account = gaia::IsChildAccountFromIdToken(id_token_);
EXPECT_EQ(is_child_account, false);
EXPECT_FALSE(
gaia::ParseServiceFlags(kIdTokenMissingServices).is_child_account);
}
TEST_F(OAuth2IdTokenDecoderTest, NotChild) {
std::string id_token_ = kIdTokenEmptyServices;
bool is_child_account = gaia::IsChildAccountFromIdToken(id_token_);
EXPECT_EQ(is_child_account, false);
EXPECT_FALSE(gaia::ParseServiceFlags(kIdTokenEmptyServices).is_child_account);
id_token_ = kIdTokenEmptyServicesHeaderSignature;
is_child_account = gaia::IsChildAccountFromIdToken(id_token_);
EXPECT_EQ(is_child_account, false);
EXPECT_FALSE(gaia::ParseServiceFlags(kIdTokenEmptyServicesHeaderSignature)
.is_child_account);
id_token_ = kIdTokenNotChildAccount;
is_child_account = gaia::IsChildAccountFromIdToken(id_token_);
EXPECT_EQ(is_child_account, false);
EXPECT_FALSE(
gaia::ParseServiceFlags(kIdTokenNotChildAccount).is_child_account);
}
TEST_F(OAuth2IdTokenDecoderTest, Child) {
std::string id_token_ = kIdTokenChildAccount;
bool is_child_account = gaia::IsChildAccountFromIdToken(id_token_);
EXPECT_EQ(is_child_account, true);
EXPECT_TRUE(gaia::ParseServiceFlags(kIdTokenChildAccount).is_child_account);
}
TEST_F(OAuth2IdTokenDecoderTest, NotAdvancedProtection) {
EXPECT_FALSE(gaia::ParseServiceFlags(kIdTokenEmptyServices)
.is_under_advanced_protection);
EXPECT_FALSE(gaia::ParseServiceFlags(kIdTokenEmptyServicesHeaderSignature)
.is_under_advanced_protection);
EXPECT_FALSE(gaia::ParseServiceFlags(kIdTokenChildAccount)
.is_under_advanced_protection);
}
TEST_F(OAuth2IdTokenDecoderTest, AdvancedProtection) {
EXPECT_TRUE(gaia::ParseServiceFlags(kIdTokenAdvancedProtectionAccount)
.is_under_advanced_protection);
gaia::TokenServiceFlags service_flags =
gaia::ParseServiceFlags(kIdTokenChildAndAdvancedProtectionAccount);
EXPECT_TRUE(service_flags.is_child_account);
EXPECT_TRUE(service_flags.is_under_advanced_protection);
}
} // namespace
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