Commit 96c94f34 authored by Nohemi Fernandez's avatar Nohemi Fernandez Committed by Commit Bot

[iOS] Add authentication operations needed for sign-in migration.

This patch adds the authentication flow required to sign-in or sign-out
a user account following a click on one of the "Turn on Sync?" buttons.

This CL is part of a series of refactors to move the existing sign-in
architecture to the Coordinator-Mediator design paradigm.

Fore more information, see go/chrome-ios-signin-migration.

Bug: 971989
Change-Id: Id9e38721b4343929d23f7861b5b5199a5dfb6a41
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2059414
Commit-Queue: Nohemi Fernandez <fernandex@chromium.org>
Reviewed-by: default avatarJérôme Lebel <jlebel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#745077}
parent 6234106d
......@@ -15,15 +15,23 @@ source_set("user_signin") {
"user_signin_view_controller.mm",
]
deps = [
"//components/consent_auditor",
"//components/sync/base",
"//components/unified_consent",
"//ios/chrome/app/strings",
"//ios/chrome/browser/main:public",
"//ios/chrome/browser/signin",
"//ios/chrome/browser/sync",
"//ios/chrome/browser/ui/authentication",
"//ios/chrome/browser/ui/authentication/signin:signin_protected",
"//ios/chrome/browser/ui/authentication/unified_consent",
"//ios/chrome/browser/ui/collection_view/cells",
"//ios/chrome/browser/ui/commands",
"//ios/chrome/browser/ui/util",
"//ios/chrome/browser/unified_consent",
"//ios/chrome/common/ui/colors",
"//ios/chrome/common/ui/util",
"//ios/public/provider/chrome/browser/signin",
"//ui/base",
]
public_deps =
......
......@@ -6,12 +6,19 @@
#import "ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.h"
#import "base/metrics/user_metrics.h"
#import "ios/chrome/browser/main/browser.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/signin/identity_manager_factory.h"
#import "ios/chrome/browser/sync/consent_auditor_factory.h"
#import "ios/chrome/browser/sync/sync_setup_service_factory.h"
#import "ios/chrome/browser/ui/authentication/authentication_flow.h"
#import "ios/chrome/browser/ui/authentication/signin/signin_coordinator+protected.h"
#import "ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_mediator.h"
#import "ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.h"
#import "ios/chrome/browser/ui/authentication/unified_consent/unified_consent_coordinator.h"
#import "ios/chrome/browser/ui/commands/browsing_data_commands.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/chrome/browser/unified_consent/unified_consent_service_factory.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
......@@ -69,7 +76,15 @@ using signin_metrics::PromoAction;
self.mediator = [[UserSigninMediator alloc]
initWithAuthenticationService:AuthenticationServiceFactory::
GetForBrowserState(self.browserState)];
GetForBrowserState(self.browserState)
identityManager:IdentityManagerFactory::GetForBrowserState(
self.browserState)
consentAuditor:ConsentAuditorFactory::GetForBrowserState(
self.browserState)
unifiedConsentService:UnifiedConsentServiceFactory::
GetForBrowserState(self.browserState)
syncSetupService:SyncSetupServiceFactory::GetForBrowserState(
self.browserState)];
self.mediator.delegate = self;
self.unifiedConsentCoordinator = [[UnifiedConsentCoordinator alloc]
......@@ -92,9 +107,35 @@ using signin_metrics::PromoAction;
completion:nil];
}
- (void)stop {
[super stop];
self.unifiedConsentCoordinator = nil;
- (void)interruptWithAction:(SigninCoordinatorInterruptAction)action
completion:(ProceduralBlock)completion {
[self.mediator cancelAndDismissAuthenticationFlow];
ProceduralBlock runCompletionCallback = ^{
[self
runCompletionCallbackWithSigninResult:SigninCoordinatorResultInterrupted
identity:self.unifiedConsentCoordinator
.selectedIdentity];
if (completion) {
completion();
}
};
switch (action) {
case SigninCoordinatorInterruptActionNoDismiss: {
runCompletionCallback();
break;
}
case SigninCoordinatorInterruptActionDismissWithAnimation: {
[self.viewController dismissViewControllerAnimated:YES
completion:runCompletionCallback];
break;
}
case SigninCoordinatorInterruptActionDismissWithoutAnimation: {
[self.viewController dismissViewControllerAnimated:NO
completion:runCompletionCallback];
break;
}
}
}
#pragma mark - UnifiedConsentCoordinatorDelegate
......@@ -154,8 +195,49 @@ using signin_metrics::PromoAction;
[self.mediator cancelSignin];
}
- (void)userSigninViewControllerDidTapOnSignin {
DCHECK(self.unifiedConsentCoordinator.selectedIdentity);
// TODO(crbug.com/971989): Pass ShouldClearDataOption from main controller.
AuthenticationFlow* authenticationFlow = [[AuthenticationFlow alloc]
initWithBrowser:self.browser
identity:self.unifiedConsentCoordinator.selectedIdentity
shouldClearData:SHOULD_CLEAR_DATA_USER_CHOICE
postSignInAction:POST_SIGNIN_ACTION_NONE
presentingViewController:self.viewController];
authenticationFlow.dispatcher = HandlerForProtocol(
self.browser->GetCommandDispatcher(), BrowsingDataCommands);
[self.mediator
authenticateWithIdentity:self.unifiedConsentCoordinator.selectedIdentity
authenticationFlow:authenticationFlow];
}
#pragma mark - UserSigninMediatorDelegate
- (void)userSigninMediatorDidTapResetSettingLink {
[self.unifiedConsentCoordinator resetSettingLinkTapped];
}
- (BOOL)userSigninMediatorGetSettingsLinkWasTapped {
return self.unifiedConsentCoordinator.settingsLinkWasTapped;
}
- (int)userSigninMediatorGetConsentConfirmationId {
if (self.userSigninMediatorGetSettingsLinkWasTapped) {
base::RecordAction(
base::UserMetricsAction("Signin_Signin_WithAdvancedSyncSettings"));
return self.unifiedConsentCoordinator.openSettingsStringId;
} else {
base::RecordAction(
base::UserMetricsAction("Signin_Signin_WithDefaultSyncSettings"));
return self.viewController.acceptSigninButtonStringId;
}
}
- (const std::vector<int>&)userSigninMediatorGetConsentStringIds {
return self.unifiedConsentCoordinator.consentStringIds;
}
- (void)userSigninMediatorSigninFinishedWithResult:
(SigninCoordinatorResult)signinResult {
[self recordSigninMetricsWithResult:signinResult];
......@@ -171,6 +253,7 @@ using signin_metrics::PromoAction;
[self.viewController dismissViewControllerAnimated:YES completion:completion];
self.unifiedConsentCoordinator.delegate = nil;
[self.unifiedConsentCoordinator stop];
self.unifiedConsentCoordinator = nil;
}
......@@ -178,6 +261,8 @@ using signin_metrics::PromoAction;
[self.viewController updatePrimaryButtonStyle];
}
#pragma mark - Private
- (void)recordSigninMetricsWithResult:(SigninCoordinatorResult)signinResult {
switch (signinResult) {
case SigninCoordinatorResultSuccess: {
......
......@@ -11,10 +11,37 @@
@class AuthenticationFlow;
class AuthenticationService;
@class ChromeIdentity;
class SyncSetupService;
// Delegate that handles interactions with unified consent coordinator.
namespace consent_auditor {
class ConsentAuditor;
}
namespace signin {
class IdentityManager;
}
namespace unified_consent {
class UnifiedConsentService;
}
// Delegate that handles interactions with unified consent screen.
@protocol UserSigninMediatorDelegate
// Sends a signal that the |settingsLinkWasTapped| parameter needs to be reset.
- (void)userSigninMediatorDidTapResetSettingLink;
// Returns the state of the |settingsLinkWasTapped| parameter in
// UnifiedConsentCoordinator.
- (BOOL)userSigninMediatorGetSettingsLinkWasTapped;
// Gets the consent confirmation ID from UnifiedConsentCoordinator.
- (int)userSigninMediatorGetConsentConfirmationId;
// Get the consent string IDs from UnifiedConsentCoordinator.
- (const std::vector<int>&)userSigninMediatorGetConsentStringIds;
// Updates sign-in state for the UserSigninCoordinator following sign-in
// finishing its workflow.
- (void)userSigninMediatorSigninFinishedWithResult:
......@@ -29,18 +56,31 @@ class AuthenticationService;
@interface UserSigninMediator : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithAuthenticationService:
(AuthenticationService*)authenticationService NS_DESIGNATED_INITIALIZER;
- (instancetype)
initWithAuthenticationService:(AuthenticationService*)authenticationService
identityManager:(signin::IdentityManager*)identityManager
consentAuditor:
(consent_auditor::ConsentAuditor*)consentAuditor
unifiedConsentService:
(unified_consent::UnifiedConsentService*)unifiedConsentService
syncSetupService:(SyncSetupService*)syncSetupService
NS_DESIGNATED_INITIALIZER;
// The delegate.
@property(nonatomic, weak) id<UserSigninMediatorDelegate> delegate;
// Property denoting whether the authentication operation is complete.
@property(nonatomic, assign) BOOL isAuthenticationCompleted;
// Enters the authentication state following identity selection. If there is an
// error transitions to the identity selection state, otherwise enters the final
// authentication completed state.
- (void)authenticateWithIdentity:(ChromeIdentity*)identity
authenticationFlow:(AuthenticationFlow*)authenticationFlow;
// Reverts the sign-in operation.
- (void)cancelSignin;
// Cancels and dismisses the authentication flow if sign-in is in progress.
- (void)cancelAndDismissAuthenticationFlow;
@end
#endif // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_USER_SIGNIN_USER_SIGNIN_MEDIATOR_H_
......@@ -8,8 +8,14 @@
#include "base/logging.h"
#import "base/metrics/user_metrics.h"
#import "base/strings/sys_string_conversions.h"
#import "components/consent_auditor/consent_auditor.h"
#import "components/unified_consent/unified_consent_service.h"
#import "ios/chrome/browser/signin/authentication_service.h"
#import "ios/chrome/browser/signin/identity_manager_factory.h"
#import "ios/chrome/browser/sync/sync_setup_service.h"
#import "ios/chrome/browser/ui/authentication/authentication_flow.h"
#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
......@@ -19,8 +25,17 @@
// Manager for the authentication flow.
@property(nonatomic, strong) AuthenticationFlow* authenticationFlow;
// Manager for user's Google identities.
@property(nonatomic, assign) signin::IdentityManager* identityManager;
// Auditor for user consent.
@property(nonatomic, assign) consent_auditor::ConsentAuditor* consentAuditor;
// Chrome interface to the iOS shared authentication library.
@property(nonatomic, assign) AuthenticationService* authenticationService;
// Manager for user consent.
@property(nonatomic, assign)
unified_consent::UnifiedConsentService* unifiedConsentService;
// Service that allows for configuring sync.
@property(nonatomic, assign) SyncSetupService* syncSetupService;
@end
......@@ -28,37 +43,115 @@
#pragma mark - Public
- (instancetype)initWithAuthenticationService:
(AuthenticationService*)authenticationService {
- (instancetype)
initWithAuthenticationService:(AuthenticationService*)authenticationService
identityManager:(signin::IdentityManager*)identityManager
consentAuditor:
(consent_auditor::ConsentAuditor*)consentAuditor
unifiedConsentService:
(unified_consent::UnifiedConsentService*)unifiedConsentService
syncSetupService:(SyncSetupService*)syncSetupService {
self = [super init];
if (self) {
_identityManager = identityManager;
_consentAuditor = consentAuditor;
_authenticationService = authenticationService;
_unifiedConsentService = unifiedConsentService;
_syncSetupService = syncSetupService;
}
return self;
}
- (void)authenticateWithIdentity:(ChromeIdentity*)identity
authenticationFlow:(AuthenticationFlow*)authenticationFlow {
DCHECK(!self.authenticationFlow);
self.authenticationFlow = authenticationFlow;
__weak UserSigninMediator* weakSelf = self;
[self.authenticationFlow startSignInWithCompletion:^(BOOL success) {
[weakSelf onAccountSigninCompletion:success identity:identity];
}];
}
- (void)cancelSignin {
if (!self.isAuthenticationCompleted) {
if (self.isAuthenticationInProgress) {
[self cancelAndDismissAuthenticationFlow];
[self.delegate userSigninMediatorNeedPrimaryButtonUpdate];
} else {
[self.delegate userSigninMediatorSigninFinishedWithResult:
SigninCoordinatorResultCanceledByUser];
self.isAuthenticationCompleted = YES;
} else if (self.isAuthenticationInProgress) {
// TODO(crbug.com/971989): Do not remove until the migration has been
// completed to ensure that the metrics calculated remain the same
// throughout the code changes.
base::RecordAction(base::UserMetricsAction("Signin_Undo_Signin"));
[self.authenticationFlow cancelAndDismiss];
self.authenticationService->SignOut(signin_metrics::ABORT_SIGNIN,
/*force_clear_browsing_data=*/false,
nil);
[self.delegate userSigninMediatorNeedPrimaryButtonUpdate];
}
}
#pragma mark - State
- (void)cancelAndDismissAuthenticationFlow {
if (!self.isAuthenticationInProgress) {
return;
}
// TODO(crbug.com/971989): Remove this metric following the architecture
// migration in the case that the flow has been dismissed by the user and
// rename in the case sign-in has been interrupted.
base::RecordAction(base::UserMetricsAction("Signin_Undo_Signin"));
// TODO(crbug.com/1056634): Support cancelAndDismiss with animation parameter.
[self.authenticationFlow cancelAndDismiss];
self.authenticationService->SignOut(signin_metrics::ABORT_SIGNIN,
/*force_clear_browsing_data=*/false, nil);
}
#pragma mark - Private
- (BOOL)isAuthenticationInProgress {
return self.authenticationFlow != nil;
}
- (void)onAccountSigninCompletion:(BOOL)success
identity:(ChromeIdentity*)identity {
self.authenticationFlow = nil;
if (success) {
[self signinCompletedWithIdentity:identity];
} else {
[self.delegate userSigninMediatorNeedPrimaryButtonUpdate];
[self.delegate userSigninMediatorDidTapResetSettingLink];
}
}
// Starts the sync engine only if the user tapped on "YES, I'm in", and closes
// the sign-in view.
- (void)signinCompletedWithIdentity:(ChromeIdentity*)identity {
// The consent has to be given as soon as the user is signed in. Even when
// they open the settings through the link.
self.unifiedConsentService->SetUrlKeyedAnonymizedDataCollectionEnabled(true);
BOOL settingsLinkWasTapped =
[self.delegate userSigninMediatorGetSettingsLinkWasTapped];
if (!settingsLinkWasTapped) {
// FirstSetupComplete flag should be only turned on when the user agrees
// to start Sync.
self.syncSetupService->SetFirstSetupComplete(
syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
self.syncSetupService->CommitSyncChanges();
}
sync_pb::UserConsentTypes::SyncConsent syncConsent;
syncConsent.set_status(sync_pb::UserConsentTypes::ConsentStatus::
UserConsentTypes_ConsentStatus_GIVEN);
int consentConfirmationId =
[self.delegate userSigninMediatorGetConsentConfirmationId];
syncConsent.set_confirmation_grd_id(consentConfirmationId);
std::vector<int> consentTextIds =
[self.delegate userSigninMediatorGetConsentStringIds];
for (int id : consentTextIds) {
syncConsent.add_description_grd_ids(id);
}
CoreAccountId coreAccountId = self.identityManager->PickAccountIdForAccount(
base::SysNSStringToUTF8([identity gaiaID]),
base::SysNSStringToUTF8([identity userEmail]));
self.consentAuditor->RecordSyncConsent(coreAccountId, syncConsent);
[self.delegate userSigninMediatorSigninFinishedWithResult:
SigninCoordinatorResultSuccess];
}
@end
......@@ -23,6 +23,9 @@
// Performs operations to skip sign-in or undo existing sign-in.
- (void)userSigninViewControllerDidTapOnSkipSignin;
// Performs operations to skip sign-in or undo existing sign-in.
- (void)userSigninViewControllerDidTapOnSignin;
@end
// View controller used to show sign-in UI.
......@@ -34,6 +37,8 @@
// View controller that handles the user consent before the user signs in.
@property(nonatomic, weak) UIViewController* unifiedConsentViewController;
@property(nonatomic, assign, readonly) int acceptSigninButtonStringId;
// Informs the view controller that the unified consent has reached the bottom
// of the screen.
- (void)markUnifiedConsentScreenReachedBottom;
......
......@@ -161,6 +161,10 @@ enum AuthenticationButtonType {
IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_SCROLL_BUTTON);
}
- (int)acceptSigninButtonStringId {
return IDS_IOS_ACCOUNT_UNIFIED_CONSENT_OK_BUTTON;
}
- (const AuthenticationViewConstants&)authenticationViewConstants {
BOOL isRegularSizeClass = IsRegularXRegularSizeClass(self.traitCollection);
return isRegularSizeClass ? kRegularConstants : kCompactConstants;
......@@ -272,8 +276,7 @@ enum AuthenticationButtonType {
break;
}
case AuthenticationButtonTypeConfirmation: {
// TODO(crbug.com/971989): Populate action.
NOTIMPLEMENTED();
[self.delegate userSigninViewControllerDidTapOnSignin];
break;
}
}
......
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