Skip to content

Commit 61e2236

Browse files
authored
Merge pull request forcedotcom#3950 from wmathurin/login_dev_menu
Add Login Options to Dev Menu
2 parents b812b48 + dfda66a commit 61e2236

32 files changed

+2517
-877
lines changed

SalesforceSDKCore.podspec

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

libs/MobileSync/Configuration/MobileSync-Common.xcconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ GCC_PREFIX_HEADER = $(PROJECT_NAME)/$(PROJECT_NAME)-Prefix.pch
1313

1414
PRODUCT_NAME = MobileSync
1515

16-
LIBRARY_SEARCH_PATHS = $(SRCROOT)/../../external/ThirdPartyDependencies/sqlcipher $(inherited)
16+
LIBRARY_SEARCH_PATHS = $(inherited)
1717

1818
// Make sure to use only API that are safe for application extensions
1919
APPLICATION_EXTENSION_API_ONLY = YES

libs/MobileSync/MobileSync.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,6 +1610,7 @@
16101610
"@executable_path/Frameworks",
16111611
"@loader_path/Frameworks",
16121612
);
1613+
LIBRARY_SEARCH_PATHS = "$(inherited)";
16131614
OTHER_LDFLAGS = (
16141615
"$(inherited)",
16151616
"-framework",
@@ -1640,6 +1641,7 @@
16401641
"@executable_path/Frameworks",
16411642
"@loader_path/Frameworks",
16421643
);
1644+
LIBRARY_SEARCH_PATHS = "$(inherited)";
16431645
OTHER_LDFLAGS = (
16441646
"$(inherited)",
16451647
"-framework",

libs/SalesforceSDKCore/SalesforceSDKCore.xcodeproj/project.pbxproj

Lines changed: 155 additions & 131 deletions
Large diffs are not rendered by default.

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager+Internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@ API_UNAVAILABLE(visionos)
3434
- (BOOL)isSnapshotPresented:(nonnull UIScene *)scene API_UNAVAILABLE(visionos);
3535
- (void)dismissSnapshot:(nonnull UIScene *)scene completion:(void (^ __nullable)(void))completion API_UNAVAILABLE(visionos);
3636

37+
- (nonnull NSArray<SFSDKDevAction *> *)getDevActions:(nonnull UIViewController *)presentedViewController;
38+
3739
@end

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager.m

Lines changed: 122 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
#import "SFManagedPreferences.h"
3030
#import "SFApplicationHelper.h"
3131
#import "SFSDKAppFeatureMarkers.h"
32-
#import "SFSDKDevInfoViewController.h"
3332
#import "SFDefaultUserManagementViewController.h"
33+
#import "SFSDKAuthRootController.h"
3434
#import <SalesforceSDKCommon/SFSwiftDetectUtil.h>
3535
#import "SFSDKEncryptedURLCache.h"
3636
#import "SFSDKNullURLCache.h"
@@ -471,25 +471,66 @@ - (NSString *)devInfoTitleString
471471

472472
- (NSArray<SFSDKDevAction *>*) getDevActions:(UIViewController *)presentedViewController
473473
{
474-
return @[
475-
[[SFSDKDevAction alloc]initWith:@"Show dev info" handler:^{
476-
SFSDKDevInfoViewController *devInfo = [[SFSDKDevInfoViewController alloc] init];
477-
[presentedViewController presentViewController:devInfo animated:NO completion:nil];
478-
}],
479-
[[SFSDKDevAction alloc]initWith:@"Logout" handler:^{
480-
[[SFUserAccountManager sharedInstance] logout:SFLogoutReasonUserInitiated];
481-
}],
482-
[[SFSDKDevAction alloc]initWith:@"Switch user" handler:^{
483-
SFDefaultUserManagementViewController *umvc = [[SFDefaultUserManagementViewController alloc] initWithCompletionBlock:^(SFUserManagementAction action) {
484-
[presentedViewController dismissViewControllerAnimated:YES completion:nil];
485-
}];
486-
[presentedViewController presentViewController:umvc animated:YES completion:nil];
487-
}],
488-
[[SFSDKDevAction alloc]initWith:@"Inspect Key-Value Store" handler:^{
489-
UIViewController *keyValueStoreInspector = [[SFSDKKeyValueEncryptedFileStoreViewController new] createUI];
490-
[presentedViewController presentViewController:keyValueStoreInspector animated:YES completion:nil];
491-
}]
492-
];
474+
NSMutableArray<SFSDKDevAction *> *actions = [NSMutableArray array];
475+
SFUserAccountManager *userAccountManager = [SFUserAccountManager sharedInstance];
476+
SFUserAccount *currentUser = userAccountManager.currentUser;
477+
478+
// Check if we're showing the login screen
479+
BOOL isShowingLogin = [presentedViewController isKindOfClass:[SFLoginViewController class]];
480+
// TODO uncomment to support advanced auth case (once we add code to restart auth in that case below)
481+
// || [presentedViewController.presentingViewController isKindOfClass:[SFSDKAuthRootController class]];
482+
483+
// Show dev info - always available
484+
[actions addObject:[[SFSDKDevAction alloc]initWith:@"Show dev info" handler:^{
485+
UIViewController *devInfo = [SFSDKDevInfoViewController makeViewController];
486+
[presentedViewController presentViewController:devInfo animated:YES completion:nil];
487+
}]];
488+
489+
// Login Options - only show on login screen
490+
if (isShowingLogin) {
491+
[actions addObject:[[SFSDKDevAction alloc]initWith:@"Login Options" handler:^{
492+
UIViewController *configPicker = [BootConfigPickerViewController makeViewControllerOnConfigurationCompleted:^{
493+
[presentedViewController dismissViewControllerAnimated:YES completion:^{
494+
// Restart authentication with the updated configuration
495+
if ([presentedViewController isKindOfClass:[SFLoginViewController class]]) {
496+
[[SFUserAccountManager sharedInstance] restartAuthenticationForViewController:(SFLoginViewController *)presentedViewController];
497+
}
498+
// TODO support advanced auth case
499+
}];
500+
}];
501+
[presentedViewController presentViewController:configPicker animated:YES completion:nil];
502+
}]];
503+
}
504+
505+
// Logout - only show if there's a current user and not on login screen
506+
if (currentUser && !isShowingLogin) {
507+
[actions addObject:[[SFSDKDevAction alloc]initWith:@"Logout" handler:^{
508+
[[SFUserAccountManager sharedInstance] logout:SFLogoutReasonUserInitiated];
509+
}]];
510+
}
511+
512+
// Switch user - only show if there's a current user and not on login screen
513+
if (currentUser && !isShowingLogin) {
514+
[actions addObject:[[SFSDKDevAction alloc]initWith:@"Switch user" handler:^{
515+
SFDefaultUserManagementViewController *umvc = [[SFDefaultUserManagementViewController alloc] initWithCompletionBlock:^(SFUserManagementAction action) {
516+
[presentedViewController dismissViewControllerAnimated:YES completion:nil];
517+
}];
518+
[presentedViewController presentViewController:umvc animated:YES completion:nil];
519+
}]];
520+
}
521+
522+
// Inspect Key-Value Store - only show if there are stores
523+
BOOL hasGlobalStores = [SFSDKKeyValueEncryptedFileStore allGlobalStoreNames].count > 0;
524+
BOOL hasUserStores = currentUser && [SFSDKKeyValueEncryptedFileStore allStoreNames].count > 0;
525+
526+
if (hasGlobalStores || hasUserStores) {
527+
[actions addObject:[[SFSDKDevAction alloc]initWith:@"Inspect Key-Value Store" handler:^{
528+
UIViewController *keyValueStoreInspector = [[SFSDKKeyValueEncryptedFileStoreViewController new] createUI];
529+
[presentedViewController presentViewController:keyValueStoreInspector animated:YES completion:nil];
530+
}]];
531+
}
532+
533+
return [actions copy];
493534
}
494535

495536
- (NSArray<NSString *>*) getDevSupportInfos
@@ -498,25 +539,62 @@ - (NSString *)devInfoTitleString
498539
NSMutableArray * devInfos = [NSMutableArray arrayWithArray:@[
499540
@"SDK Version", SALESFORCE_SDK_VERSION,
500541
@"App Type", [self getAppTypeAsString],
501-
@"User Agent", self.userAgentString(@""),
542+
@"User Agent", self.userAgentString(@"")
543+
]];
544+
NSArray* allUsers = userAccountManager.allUserAccounts;
545+
if ([allUsers count] > 0) {
546+
[devInfos addObjectsFromArray:@[
547+
@"Authenticated Users", [self usersToString:allUsers]
548+
]];
549+
}
550+
551+
// Auth configuration
552+
[devInfos addObject:@"section:Auth Config"];
553+
[devInfos addObjectsFromArray:@[
502554
@"Use Web Server Authentication", [self useWebServerAuthentication] ? @"YES" : @"NO",
555+
@"Use Hybrid Authentication", [self useHybridAuthentication] ? @"YES" : @"NO",
556+
@"Supports Welcome Discovery", [self supportsWelcomeDiscovery] ? @"YES" : @"NO",
503557
@"Browser Login Enabled", [SFUserAccountManager sharedInstance].useBrowserAuth? @"YES" : @"NO",
504558
@"IDP Enabled", [self idpEnabled] ? @"YES" : @"NO",
505-
@"Identity Provider", [self isIdentityProvider] ? @"YES" : @"NO",
506-
@"Current User", [self userToString:userAccountManager.currentUser],
507-
@"Scopes", [userAccountManager.currentUser.credentials.scopes componentsJoinedByString:@" "],
508-
@"Access Token Expiration", [self accessTokenExpiration],
509-
@"Authenticated Users", [self usersToString:userAccountManager.allUserAccounts],
510-
@"User Key-Value Stores", [self safeJoin:[SFSDKKeyValueEncryptedFileStore allStoreNames] separator:@", "],
511-
@"Global Key-Value Stores", [self safeJoin:[SFSDKKeyValueEncryptedFileStore allGlobalStoreNames] separator:@", "]
559+
@"Identity Provider", [self isIdentityProvider] ? @"YES" : @"NO"
512560
]];
513561

514-
[devInfos addObjectsFromArray:[self dictToDevInfos:self.appConfig.configDict keyPrefix:@"BootConfig"]];
562+
// Static bootconfig
563+
[devInfos addObject:@"section:Bootconfig"];
564+
[devInfos addObjectsFromArray:[self dictToDevInfos:self.appConfig.configDict]];
565+
566+
// Current user info
567+
SFUserAccount* currentUser = userAccountManager.currentUser;
568+
if (currentUser) {
569+
SFOAuthCredentials* creds = currentUser.credentials;
570+
[devInfos addObject:@"section:Current User"];
571+
[devInfos addObjectsFromArray:@[
572+
@"Username", [self userToString:currentUser],
573+
@"Consumer Key", creds.clientId ?: @"(nil)",
574+
@"Redirect URI", creds.redirectUri ?: @"(nil)",
575+
@"Scopes", [self scopesToString:currentUser],
576+
@"Instance URL", [creds.instanceUrl absoluteString] ?: @"(nil)",
577+
@"Token format", [creds.tokenFormat isEqualToString:@"jwt"] ? @"jwt" : @"opaque",
578+
@"Access Token Expiration", [self accessTokenExpiration],
579+
@"Beacon Child Consumer Key", creds.beaconChildConsumerKey ?: @"(empty)"
580+
]];
581+
}
515582

583+
// Key Value Stores
584+
NSArray<NSString*>* globalKeyValueStores = [SFSDKKeyValueEncryptedFileStore allGlobalStoreNames];
585+
NSArray<NSString*>* userKeyValueStores = [SFSDKKeyValueEncryptedFileStore allStoreNames];
586+
if ([userKeyValueStores count] > 0 || [globalKeyValueStores count] > 0) {
587+
[devInfos addObject:@"section:Key Value Stores"];
588+
[devInfos addObjectsFromArray:@[@"Global stores", [self safeJoin:globalKeyValueStores separator:@", "]]];
589+
[devInfos addObjectsFromArray:@[@"User stores", [self safeJoin:globalKeyValueStores separator:@", "]]];
590+
}
591+
592+
// Managed prefs
516593
SFManagedPreferences *managedPreferences = [SFManagedPreferences sharedPreferences];
517-
[devInfos addObjectsFromArray:@[@"Managed", [managedPreferences hasManagedPreferences] ? @"YES" : @"NO"]];
518594
if ([managedPreferences hasManagedPreferences]) {
519-
[devInfos addObjectsFromArray:[self dictToDevInfos:managedPreferences.rawPreferences keyPrefix:@"Managed Pref"]];
595+
[devInfos addObject:@"section:Managed Pref"];
596+
[devInfos addObjectsFromArray:@[@"Managed", [managedPreferences hasManagedPreferences] ? @"YES" : @"NO"]];
597+
[devInfos addObjectsFromArray:[self dictToDevInfos:managedPreferences.rawPreferences]];
520598
}
521599

522600
return devInfos;
@@ -542,21 +620,31 @@ - (NSString *) accessTokenExpiration {
542620
}
543621

544622
- (NSString*) userToString:(SFUserAccount*)user {
545-
return user ? user.idData.username : @"";
623+
return user.idData.username != nil ? user.idData.username : @"";
624+
}
625+
626+
- (NSString*) scopesToString:(SFUserAccount*)user {
627+
return user.credentials.scopes != nil ? [user.credentials.scopes componentsJoinedByString:@" "] : @"";
546628
}
547629

548630
- (NSString*) usersToString:(NSArray<SFUserAccount*>*)userAccounts {
631+
if (userAccounts == nil) {
632+
return @"";
633+
}
549634
NSMutableArray* usernames = [NSMutableArray new];
550635
for (SFUserAccount *userAccount in userAccounts) {
551636
[usernames addObject:[self userToString:userAccount]];
552637
}
553638
return [usernames componentsJoinedByString:@", "];
554639
}
555640

556-
- (NSArray*) dictToDevInfos:(NSDictionary*)dict keyPrefix:(NSString*)keyPrefix {
641+
- (NSArray*) dictToDevInfos:(NSDictionary*)dict {
642+
if (dict == nil) {
643+
return @[];
644+
}
557645
NSMutableArray * devInfos = [NSMutableArray new];
558646
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
559-
[devInfos addObject:[NSString stringWithFormat:@"%@ - %@", keyPrefix, key]];
647+
[devInfos addObject:key];
560648
[devInfos addObject:[[NSString stringWithFormat:@"%@", obj] stringByReplacingOccurrencesOfString:@"\n" withString:@""]];
561649
}];
562650
return devInfos;

native/SampleApps/AuthFlowTester/AuthFlowTester/Views/FlowTypesView.swift renamed to libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/AuthFlowTypesView.swift

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
2-
FlowTypesView.swift
3-
AuthFlowTester
2+
AuthFlowTypesView.swift
3+
SalesforceSDKCore
44

55
Copyright (c) 2025-present, salesforce.com, inc. All rights reserved.
66

@@ -26,18 +26,19 @@
2626
*/
2727

2828
import SwiftUI
29-
import SalesforceSDKCore
3029

31-
struct FlowTypesView: View {
30+
public struct AuthFlowTypesView: View {
3231
@State private var useWebServerFlow: Bool
3332
@State private var useHybridFlow: Bool
33+
@State private var supportsWelcomeDiscovery: Bool
3434

35-
init() {
35+
public init() {
3636
_useWebServerFlow = State(initialValue: SalesforceManager.shared.useWebServerAuthentication)
3737
_useHybridFlow = State(initialValue: SalesforceManager.shared.useHybridAuthentication)
38+
_supportsWelcomeDiscovery = State(initialValue: SalesforceManager.shared.supportsWelcomeDiscovery)
3839
}
3940

40-
var body: some View {
41+
public var body: some View {
4142
VStack(alignment: .leading, spacing: 12) {
4243
Text("Authentication Flow Types")
4344
.font(.headline)
@@ -48,7 +49,7 @@ struct FlowTypesView: View {
4849
Text("Use Web Server Flow")
4950
.font(.body)
5051
}
51-
.onChange(of: useWebServerFlow) { newValue in
52+
.onChange(of: useWebServerFlow) { _, newValue in
5253
SalesforceManager.shared.useWebServerAuthentication = newValue
5354
}
5455
.padding(.horizontal)
@@ -57,10 +58,19 @@ struct FlowTypesView: View {
5758
Text("Use Hybrid Flow")
5859
.font(.body)
5960
}
60-
.onChange(of: useHybridFlow) { newValue in
61+
.onChange(of: useHybridFlow) { _, newValue in
6162
SalesforceManager.shared.useHybridAuthentication = newValue
6263
}
6364
.padding(.horizontal)
65+
66+
Toggle(isOn: $supportsWelcomeDiscovery) {
67+
Text("Support Welcome Discovery")
68+
.font(.body)
69+
}
70+
.onChange(of: supportsWelcomeDiscovery) { _, newValue in
71+
SalesforceManager.shared.supportsWelcomeDiscovery = newValue
72+
}
73+
.padding(.horizontal)
6474
}
6575
}
6676
.padding(.vertical)

native/SampleApps/AuthFlowTester/AuthFlowTester/Views/BootConfigEditor.swift renamed to libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/BootConfigEditor.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
BootConfigEditor.swift
3-
AuthFlowTester
3+
SalesforceSDKCore
44

55
Copyright (c) 2025-present, salesforce.com, inc. All rights reserved.
66

@@ -26,9 +26,8 @@
2626
*/
2727

2828
import SwiftUI
29-
import SalesforceSDKCore
3029

31-
struct BootConfigEditor: View {
30+
public struct BootConfigEditor: View {
3231
let title: String
3332
let buttonLabel: String
3433
let buttonColor: Color
@@ -40,7 +39,29 @@ struct BootConfigEditor: View {
4039
let initiallyExpanded: Bool
4140
@State private var isExpanded: Bool = false
4241

43-
var body: some View {
42+
public init(
43+
title: String,
44+
buttonLabel: String,
45+
buttonColor: Color,
46+
consumerKey: Binding<String>,
47+
callbackUrl: Binding<String>,
48+
scopes: Binding<String>,
49+
isLoading: Bool,
50+
onUseConfig: @escaping () -> Void,
51+
initiallyExpanded: Bool
52+
) {
53+
self.title = title
54+
self.buttonLabel = buttonLabel
55+
self.buttonColor = buttonColor
56+
self._consumerKey = consumerKey
57+
self._callbackUrl = callbackUrl
58+
self._scopes = scopes
59+
self.isLoading = isLoading
60+
self.onUseConfig = onUseConfig
61+
self.initiallyExpanded = initiallyExpanded
62+
}
63+
64+
public var body: some View {
4465
VStack(alignment: .leading, spacing: 12) {
4566
Button(action: {
4667
withAnimation {

0 commit comments

Comments
 (0)