Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
4F3139682331C5C7007B3705 /* SFSDKAuthRootController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F3139672331C5B9007B3705 /* SFSDKAuthRootController.h */; };
4F3ECD8A2EBBD150005020A6 /* SFOAuthCoordinatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F3ECD892EBBD150005020A6 /* SFOAuthCoordinatorTests.m */; };
4F3ECD8C2EBBD182005020A6 /* SFOAuthInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F3ECD8B2EBBD182005020A6 /* SFOAuthInfoTests.m */; };
4FA1B2C32F0E000000000001 /* LoginForAdminTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA1B2C32F0E000000000002 /* LoginForAdminTests.m */; };
4F5727E327F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F5727DC27F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.h */; settings = {ATTRIBUTES = (Public, ); }; };
4F5727E427F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F5727E227F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.m */; };
4F5A49502E98711600C89DDD /* ScopeParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5A494F2E98711600C89DDD /* ScopeParser.swift */; };
Expand Down Expand Up @@ -595,6 +596,7 @@
4F3139672331C5B9007B3705 /* SFSDKAuthRootController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKAuthRootController.h; sourceTree = "<group>"; };
4F3ECD892EBBD150005020A6 /* SFOAuthCoordinatorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFOAuthCoordinatorTests.m; sourceTree = "<group>"; };
4F3ECD8B2EBBD182005020A6 /* SFOAuthInfoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFOAuthInfoTests.m; sourceTree = "<group>"; };
4FA1B2C32F0E000000000002 /* LoginForAdminTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginForAdminTests.swift; sourceTree = "<group>"; };
4F5727DC27F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFSDKPrimingRecordsResponse.h; sourceTree = "<group>"; };
4F5727E227F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFSDKPrimingRecordsResponse.m; sourceTree = "<group>"; };
4F5A494F2E98711600C89DDD /* ScopeParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScopeParser.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1080,6 +1082,7 @@
4F7EB3F81BFFC87600768720 /* SFEncryptionKeyTests.m */,
B7352CA422761D8400DA2CFF /* SFManagedPreferencesTest.m */,
69CEBC7D22F368CF00F16218 /* SFNetworkTests.m */,
4FA1B2C32F0E000000000002 /* LoginForAdminTests.swift */,
4F3ECD892EBBD150005020A6 /* SFOAuthCoordinatorTests.m */,
23EED8892E2ACD3300646B10 /* SFOAuthCoordinatorTests.swift */,
4F9E05332DD7BE0A00548985 /* SFOAuthCredentialsTests.m */,
Expand Down Expand Up @@ -2252,6 +2255,7 @@
4F7EB41B1BFFC8D700768720 /* SFSDKCryptoUtilsTests.m in Sources */,
69848CB82364035300893E57 /* SFSDKEncryptedPushNotificationTests.m in Sources */,
4F3ECD8A2EBBD150005020A6 /* SFOAuthCoordinatorTests.m in Sources */,
4FA1B2C32F0E000000000001 /* LoginForAdminTests.swift in Sources */,
4F9E05322DD6A08000548985 /* SFSDKOAuthTokenEndpointResponseTests.m in Sources */,
4F06AF8D1C49A18E00F70798 /* SalesforceSDKManagerTests.m in Sources */,
237C186C2E44FCAE0008015C /* EncryptStreamTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ NS_SWIFT_NAME(SalesforceLoginViewControllerDelegate)
- (void)loginViewControllerDidChangeLoginOptions:(nonnull SFLoginViewController *)loginViewController;


/**
* Notifies the delegate that the user selected "Login for Admin" from the settings menu.
* This forces browser-based (advanced) authentication via ASWebAuthenticationSession,
* regardless of org configuration, to support phishing-resistant MFA.
* @param loginViewController The instance sending this message.
*/
- (void)loginViewControllerDidSelectLoginForAdmin:(nonnull SFLoginViewController *)loginViewController;

@end

/** The Salesforce login screen view.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,17 @@ - (UIBarButtonItem *)createSettingsButton {
[self presentViewController:configPicker animated:YES completion:nil];
}]];
}


// Login for Admin - forces browser-based (advanced) authentication to support phishing-resistant MFA.
[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_FOR_ADMIN"]
image:nil
identifier:nil
handler:^(__kindof UIAction* _Nonnull action) {
if ([self.delegate respondsToSelector:@selector(loginViewControllerDidSelectLoginForAdmin:)]) {
[self.delegate loginViewControllerDidSelectLoginForAdmin:self];
}
}]];

UIMenu *menu = [UIMenu menuWithTitle:@"" // No title
children:menuActions];
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithImage:image menu:menu];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ - (void)authenticate {
self.authenticating = YES;
if (self.credentials.refreshToken) {
self.authInfo = [[SFOAuthInfo alloc] initWithAuthType:SFOAuthTypeRefresh];
} else if (self.useBrowserAuth) {
self.authInfo = [[SFOAuthInfo alloc] initWithAuthType:SFOAuthTypeAdvancedBrowser];
} else if ([[SalesforceSDKManager sharedManager] useWebServerAuthentication]) {
self.authInfo = [[SFOAuthInfo alloc] initWithAuthType:SFOAuthTypeWebServer];
} else {
Expand Down Expand Up @@ -777,7 +779,7 @@ - (void)handleUserAgentResponse:(NSURL *)requestUrl {
- (NSString *)generateApprovalUrlString {
return [self approvalURLForEndpoint:[self brandedAuthorizeURL]
credentials:self.credentials
webServerFlow:[[SalesforceSDKManager sharedManager] useWebServerAuthentication]
webServerFlow:(self.useBrowserAuth || [[SalesforceSDKManager sharedManager] useWebServerAuthentication])
protocol:nil
domain:nil
codeChallenge:nil];
Expand Down Expand Up @@ -893,7 +895,7 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigati
// If a front door bridge URL override is present, use its code verifier to choose between user agent or web server authentication.
if (self.frontdoorBridgeLoginOverride.frontdoorBridgeUrl // Check if an override is provided
? self.frontdoorBridgeLoginOverride.codeVerifier != nil // If yes, only proceed if it's a web server flow as indicated by a code verifier.
: [[SalesforceSDKManager sharedManager] useWebServerAuthentication] // If there's no override use the default SDK setting.
: (self.useBrowserAuth || [[SalesforceSDKManager sharedManager] useWebServerAuthentication]) // If there's no override use browser auth or the default SDK setting.
)
{
[self handleWebServerResponse:url]; // Web server flow/URLs with query string parameters.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ NS_ASSUME_NONNULL_BEGIN
@interface SFSDKAuthRequest : NSObject

@property (nonatomic, assign) BOOL useBrowserAuth;

/// Indicates that browser auth was initiated by the "Login for Admin" action.
/// When YES, cancelling the browser session returns to the WebView login instead of showing the server picker.
@property (nonatomic, assign) BOOL loginAsAdmin;

@property (nonatomic, strong) NSArray<NSString *> *additionalOAuthParameterKeys;
@property (nonatomic, strong) NSDictionary<NSString *,id> * additionalTokenRefreshParams;
@property (nonatomic, copy) NSString *loginHost;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ -(void)initCoordinator {
self.oauthCoordinator.additionalTokenRefreshParams = self.oauthRequest.additionalTokenRefreshParams;
self.oauthCoordinator.scopes = self.oauthRequest.scopes;
self.oauthCoordinator.brandLoginPath = self.oauthRequest.brandLoginPath;
self.oauthCoordinator.useBrowserAuth = self.oauthRequest.useBrowserAuth;
self.oauthCoordinator.useBrowserAuth = self.oauthRequest.useBrowserAuth || self.oauthRequest.loginAsAdmin;

// TODO: Remove in Mobile SDK 14.0
#pragma clang diagnostic push
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1030,14 +1030,22 @@ - (void)oauthCoordinatorDidCancelBrowserAuthentication:(SFOAuthCoordinator *)coo
});
return;
}


// When "Login for Admin" initiated the browser auth, clear the flag and
// restart the WebView login flow instead of showing the server picker.
if (coordinator.authSession.oauthRequest.loginAsAdmin) {
coordinator.authSession.oauthRequest.loginAsAdmin = NO;
[self restartAuthentication:coordinator.authSession];
return;
}

if (self.nativeLoginEnabled && self.shouldFallbackToWebAuthentication) {
self.shouldFallbackToWebAuthentication = NO;
[self stopCurrentAuthentication:nil];
[self loginWithCompletion:^(SFOAuthInfo* authInfo, SFUserAccount* user) { } failure:^(SFOAuthInfo* authInfo, NSError* error) { }];
return;
}

SFOAuthInfo *authInfo = [[SFOAuthInfo alloc] initWithAuthType:SFOAuthTypeAdvancedBrowser];
NSDictionary *userInfo = @{ kSFNotificationUserInfoCredentialsKey: coordinator.credentials,
kSFNotificationUserInfoAuthTypeKey: authInfo };
Expand Down Expand Up @@ -1114,6 +1122,13 @@ - (void)loginViewControllerDidReload:(SFLoginViewController *)loginViewControlle
[self restartAuthenticationForViewController:loginViewController];
}

- (void)loginViewControllerDidSelectLoginForAdmin:(SFLoginViewController *)loginViewController {
NSString *sceneId = loginViewController.view.window.windowScene.session.persistentIdentifier;
SFSDKAuthSession *session = self.authSessions[sceneId];
session.oauthRequest.loginAsAdmin = YES;
[self restartAuthenticationForViewController:loginViewController];
}

- (void)loginViewControllerDidChangeLoginOptions:(SFLoginViewController *)loginViewController {
[self restartAuthenticationForViewController:loginViewController recreateAuthRequest:YES];
}
Expand Down
Loading
Loading