Skip to content

Commit 57f835c

Browse files
authored
Merge pull request #284 from wmathurin/zero_scopes
Hybrid app inspect scopes in user credentials and warn if important ones are missing
2 parents 7f85cb3 + d88e75b commit 57f835c

File tree

6 files changed

+519
-20
lines changed

6 files changed

+519
-20
lines changed

external/SalesforceMobileSDK-iOS

libs/SalesforceHybridSDK/SalesforceHybridSDK.xcodeproj/project.pbxproj

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
4F06AFC61C49A89900F70798 /* SalesforceHybridSDKTests-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AFC51C49A89900F70798 /* SalesforceHybridSDKTests-Prefix.pch */; };
3232
4F06AFEB1C49D35B00F70798 /* test_credentials.json in Resources */ = {isa = PBXBuildFile; fileRef = 82EFF33D16E8063700A63CDF /* test_credentials.json */; };
3333
4F0C192F18AAFE54000FD4E9 /* cordova.js in Copy files to www/ */ = {isa = PBXBuildFile; fileRef = 4F0C192D18AAFE41000FD4E9 /* cordova.js */; };
34+
4F2462E82E9EF36600373302 /* SalesforceWebViewCookieManagerTestSuite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F2462E72E9EF36600373302 /* SalesforceWebViewCookieManagerTestSuite.swift */; };
3435
4F37EB0F1DA324130049992D /* force.js in Copy sfdc libs */ = {isa = PBXBuildFile; fileRef = 4F37EB0A1DA323AF0049992D /* force.js */; };
3536
4F37EB121DA3247F0049992D /* SFForceJSTestSuite.js in Copy test lib */ = {isa = PBXBuildFile; fileRef = 4F37EB101DA3246B0049992D /* SFForceJSTestSuite.js */; };
3637
4F458AEE1911CF4C00545F86 /* mobilesync.js in Copy sfdc libs */ = {isa = PBXBuildFile; fileRef = 4FBFFAF1177A715300D1E36E /* mobilesync.js */; };
@@ -133,7 +134,6 @@
133134
CE4CE4651C0E5B2A009F6029 /* SFHybridViewConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = CEF4FFAF170CC748001AA255 /* SFHybridViewConfig.m */; };
134135
CE4CE4A71C0E60A4009F6029 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82EFF2D316E7C57100A63CDF /* Foundation.framework */; };
135136
CE65C23A1C0FD52F00319E68 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CE65C2391C0FD52F00319E68 /* libz.tbd */; };
136-
CE6839431FFEE5220011DA0A /* Cordova.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEEF966C1EB7CBF50059CA79 /* Cordova.framework */; };
137137
CE7BA1571D89F9EE000B91D7 /* SFNetworkPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7BA1551D89F9EE000B91D7 /* SFNetworkPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; };
138138
CE7BA1581D89F9EE000B91D7 /* SFNetworkPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = CE7BA1561D89F9EE000B91D7 /* SFNetworkPlugin.m */; };
139139
CE7BA1EF1D8CB388000B91D7 /* com.salesforce.plugin.network.js in Copy Salesforce plugin */ = {isa = PBXBuildFile; fileRef = CE7BA1E31D8CA3E7000B91D7 /* com.salesforce.plugin.network.js */; };
@@ -143,7 +143,6 @@
143143
CEA2A8922214C114009D923B /* SmartStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEA2A8572214C0CB009D923B /* SmartStore.framework */; };
144144
CEA2A8932214C114009D923B /* MobileSync.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEA2A8372214C0A6009D923B /* MobileSync.framework */; };
145145
CEEF96721EB7CC190059CA79 /* Cordova.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEEF966C1EB7CBF50059CA79 /* Cordova.framework */; };
146-
CEEF96731EB7CD050059CA79 /* Cordova.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEEF966C1EB7CBF50059CA79 /* Cordova.framework */; };
147146
FDCEC28E410DD8D98D826A33 /* SalesforceHybridSDKManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FDCEC47AE59418FFDB4C61C2 /* SalesforceHybridSDKManager.m */; };
148147
FDCECEB8C12C6043E1BD488B /* SalesforceHybridSDKManager.h in Headers */ = {isa = PBXBuildFile; fileRef = FDCEC934C681C14971B0C5E3 /* SalesforceHybridSDKManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
149148
/* End PBXBuildFile section */
@@ -459,6 +458,8 @@
459458
4F22155619DF480100FF2D26 /* SFForcePlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFForcePlugin.h; path = SFForcePlugin/SFForcePlugin.h; sourceTree = "<group>"; };
460459
4F22155719DF480100FF2D26 /* SFForcePlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFForcePlugin.m; path = SFForcePlugin/SFForcePlugin.m; sourceTree = "<group>"; };
461460
4F22159619DF60A200FF2D26 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; };
461+
4F2462E72E9EF36600373302 /* SalesforceWebViewCookieManagerTestSuite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SalesforceWebViewCookieManagerTestSuite.swift; sourceTree = "<group>"; };
462+
4F2462E92E9EF36A00373302 /* SalesforceHybridSDKTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SalesforceHybridSDKTests-Bridging-Header.h"; sourceTree = "<group>"; };
462463
4F37EB0A1DA323AF0049992D /* force.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = force.js; sourceTree = "<group>"; };
463464
4F37EB101DA3246B0049992D /* SFForceJSTestSuite.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = SFForceJSTestSuite.js; sourceTree = "<group>"; };
464465
4F5B96661DAD9D09001627F2 /* com.salesforce.plugin.smartstore.client.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = com.salesforce.plugin.smartstore.client.js; sourceTree = "<group>"; };
@@ -583,7 +584,6 @@
583584
isa = PBXFrameworksBuildPhase;
584585
buildActionMask = 2147483647;
585586
files = (
586-
CEEF96731EB7CD050059CA79 /* Cordova.framework in Frameworks */,
587587
CE0AB0A51C10D60400BFAEED /* SalesforceHybridSDK.framework in Frameworks */,
588588
CE0AB09C1C10D5E400BFAEED /* libxml2.tbd in Frameworks */,
589589
CE0AB09F1C10D5E400BFAEED /* libz.tbd in Frameworks */,
@@ -614,7 +614,6 @@
614614
isa = PBXFrameworksBuildPhase;
615615
buildActionMask = 2147483647;
616616
files = (
617-
CE6839431FFEE5220011DA0A /* Cordova.framework in Frameworks */,
618617
CE4CE2E81C0E465B009F6029 /* SalesforceHybridSDK.framework in Frameworks */,
619618
);
620619
runOnlyForDeploymentPostprocessing = 0;
@@ -744,6 +743,7 @@
744743
822272A219C39F4500FC537E /* SalesforceHybridSDKTests */ = {
745744
isa = PBXGroup;
746745
children = (
746+
4F2462E72E9EF36600373302 /* SalesforceWebViewCookieManagerTestSuite.swift */,
747747
4F06AFA61C49A77200F70798 /* ForceJSTestSuite.m */,
748748
4F06AFA81C49A77200F70798 /* SDKInfoTestSuite.m */,
749749
82AB22641F9EB9C100A754C8 /* SFHybridViewConfigTestSuite.m */,
@@ -754,6 +754,7 @@
754754
4F06AFAE1C49A77200F70798 /* SmartStoreTestSuite.m */,
755755
4F06AFB01C49A77200F70798 /* MobileSyncTestSuite.m */,
756756
4F06AFC31C49A87F00F70798 /* Supporting Files */,
757+
4F2462E92E9EF36A00373302 /* SalesforceHybridSDKTests-Bridging-Header.h */,
757758
);
758759
path = SalesforceHybridSDKTests;
759760
sourceTree = "<group>";
@@ -1242,6 +1243,7 @@
12421243
ORGANIZATIONNAME = salesforce.com;
12431244
TargetAttributes = {
12441245
8222729C19C39F4500FC537E = {
1246+
LastSwiftMigration = 1640;
12451247
TestTargetID = 82EFF2FE16E8049500A63CDF;
12461248
};
12471249
82EFF2FE16E8049500A63CDF = {
@@ -1511,6 +1513,7 @@
15111513
isa = PBXSourcesBuildPhase;
15121514
buildActionMask = 2147483647;
15131515
files = (
1516+
4F2462E82E9EF36600373302 /* SalesforceWebViewCookieManagerTestSuite.swift in Sources */,
15141517
4F06AFC11C49A7BF00F70798 /* SmartStoreTestSuite.m in Sources */,
15151518
4F924C5D24BD4BA700D2F6DC /* SFHybridViewControllerTestSuite.m in Sources */,
15161519
4F06AFBD1C49A7BF00F70798 /* ForceJSTestSuite.m in Sources */,
@@ -1635,6 +1638,9 @@
16351638
"@loader_path/Frameworks",
16361639
);
16371640
PRODUCT_NAME = "$(TARGET_NAME)";
1641+
SWIFT_OBJC_BRIDGING_HEADER = "SalesforceHybridSDKTests/SalesforceHybridSDKTests-Bridging-Header.h";
1642+
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1643+
SWIFT_VERSION = 6.0;
16381644
TEST_HOST = "$(BUNDLE_LOADER)";
16391645
WRAPPER_EXTENSION = xctest;
16401646
};
@@ -1666,6 +1672,9 @@
16661672
"@loader_path/Frameworks",
16671673
);
16681674
PRODUCT_NAME = "$(TARGET_NAME)";
1675+
SWIFT_OBJC_BRIDGING_HEADER = "SalesforceHybridSDKTests/SalesforceHybridSDKTests-Bridging-Header.h";
1676+
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1677+
SWIFT_VERSION = 6.0;
16691678
TEST_HOST = "$(BUNDLE_LOADER)";
16701679
WRAPPER_EXTENSION = xctest;
16711680
};

libs/SalesforceHybridSDK/SalesforceHybridSDK/Classes/SalesforceWebViewCookieManager.swift

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,52 @@
2929

3030
import Foundation
3131
import WebKit
32+
import SalesforceSDKCore
3233

3334
@objc(SFSDKSalesforceWebViewCookieManager)
3435
public class SalesforceWebViewCookieManager: NSObject {
36+
37+
// MARK: - Public Methods
38+
39+
/**
40+
* Sets cookies for the provided user account (Objective-C compatible version).
41+
* This method delegates to the Swift-only version with default implementations.
42+
*/
3543
@MainActor @objc public func setCookies(userAccount: UserAccount, completion: @escaping () -> Void) {
36-
let creds = userAccount.credentials
44+
setCookies(
45+
userAccount: userAccount,
46+
setCookieValue: { [weak self] cookieType, domain, setDomain, name, value in
47+
guard let self = self else { return }
48+
self.setCookieValue(cookieStore: WKWebsiteDataStore.default().httpCookieStore,
49+
cookieType: cookieType,
50+
domain: domain,
51+
name: name,
52+
value: value)
53+
},
54+
completion: completion
55+
)
56+
}
57+
58+
/**
59+
* Sets cookies for the provided user account (Swift version with testable parameters).
60+
* Internal access for testing purposes only.
61+
*
62+
* - Parameters:
63+
* - userAccount: The user account for which to set cookies
64+
* - setCookieValue: Optional lambda to set cookie values (for testing)
65+
* - completion: Completion block called when cookie setting is finished
66+
*/
67+
@MainActor internal func setCookies(
68+
userAccount: UserAccount,
69+
setCookieValue: @escaping (String, String?, Bool, String?, String?) -> Void = { _, _, _, _, _ in },
70+
completion: @escaping () -> Void
71+
) {
72+
let creds = userAccount.credentials
3773
SFSDKHybridLogger.i(Self.self, message: "[\(Self.self) \(#function)]: setting cookies for \(String(describing: creds.userId)).")
38-
let cookieStore = WKWebsiteDataStore.default().httpCookieStore
74+
75+
// Warn if expected scopes are missing
76+
inspectScopes(creds.scopes)
77+
3978
let instanceUrl = creds.instanceUrl
4079
let lightningDomain = creds.lightningDomain
4180
let lightningSid = creds.lightningSid
@@ -51,25 +90,28 @@ public class SalesforceWebViewCookieManager: NSObject {
5190
let orgId = userAccount.accountIdentity.orgId
5291
let mainDomain = getDomainFromUrl(instanceUrl)
5392

93+
// Determine setDomain flag based on community URL presence
94+
let hasCommunityUrl = creds.communityUrl != nil
95+
5496
// Main domain cookies
55-
setCookieValue(cookieStore: cookieStore, cookieType: "sid for main", domain: mainDomain, name: sidCookieName, value: mainSid)
56-
setCookieValue(cookieStore: cookieStore, cookieType: Self.CLIENT_SRC, domain: mainDomain, name: Self.CLIENT_SRC, value: clientSrc)
57-
setCookieValue(cookieStore: cookieStore, cookieType: Self.SID_CLIENT, domain: mainDomain, name: Self.SID_CLIENT, value: sidClient)
58-
setCookieValue(cookieStore: cookieStore, cookieType: Self.ORG_ID, domain: mainDomain, name: Self.ORG_ID, value: orgId)
59-
setCookieValue(cookieStore: cookieStore, cookieType: Self.csrfTokenCookieName, domain: mainDomain, name: Self.csrfTokenCookieName, value: csrfToken)
97+
setCookieValue("sid for main", mainDomain, hasCommunityUrl, sidCookieName, mainSid)
98+
setCookieValue(Self.CLIENT_SRC, mainDomain, hasCommunityUrl, Self.CLIENT_SRC, clientSrc)
99+
setCookieValue(Self.SID_CLIENT, mainDomain, hasCommunityUrl, Self.SID_CLIENT, sidClient)
100+
setCookieValue(Self.ORG_ID, mainDomain, hasCommunityUrl, Self.ORG_ID, orgId)
101+
setCookieValue(Self.csrfTokenCookieName, mainDomain, hasCommunityUrl, Self.csrfTokenCookieName, csrfToken)
60102

61103
// Lightning domain cookies
62-
setCookieValue(cookieStore: cookieStore, cookieType: "sid for lightning", domain: lightningDomain, name: sidCookieName, value: lightningSid)
63-
setCookieValue(cookieStore: cookieStore, cookieType: Self.csrfTokenCookieName, domain: lightningDomain, name: Self.csrfTokenCookieName, value: csrfToken)
104+
setCookieValue("sid for lightning", lightningDomain, hasCommunityUrl, sidCookieName, lightningSid)
105+
setCookieValue(Self.csrfTokenCookieName, lightningDomain, hasCommunityUrl, Self.csrfTokenCookieName, csrfToken)
64106

65107
// Content domain cookies
66-
setCookieValue(cookieStore: cookieStore, cookieType: "sid for content", domain: contentDomain, name: sidCookieName, value: contentSid)
108+
setCookieValue("sid for content", contentDomain, hasCommunityUrl, sidCookieName, contentSid)
67109

68110
// Vf domain cookies
69-
setCookieValue(cookieStore: cookieStore, cookieType: "sid for vf", domain: vfDomain, name: sidCookieName, value: vfSid)
70-
setCookieValue(cookieStore: cookieStore, cookieType: Self.CLIENT_SRC, domain: vfDomain, name: Self.CLIENT_SRC, value: clientSrc)
71-
setCookieValue(cookieStore: cookieStore, cookieType: Self.SID_CLIENT, domain: vfDomain, name: Self.SID_CLIENT, value: sidClient)
72-
setCookieValue(cookieStore: cookieStore, cookieType: Self.ORG_ID, domain: vfDomain, name: Self.ORG_ID, value: orgId)
111+
setCookieValue("sid for vf", vfDomain, hasCommunityUrl, sidCookieName, vfSid)
112+
setCookieValue(Self.CLIENT_SRC, vfDomain, hasCommunityUrl, Self.CLIENT_SRC, clientSrc)
113+
setCookieValue(Self.SID_CLIENT, vfDomain, hasCommunityUrl, Self.SID_CLIENT, sidClient)
114+
setCookieValue(Self.ORG_ID, vfDomain, hasCommunityUrl, Self.ORG_ID, orgId)
73115

74116
SFSDKHybridLogger.i(Self.self, message: "[\(Self.self) \(#function)]: done setting cookies for \(String(describing: creds.userId)).")
75117
completion()
@@ -99,6 +141,34 @@ public class SalesforceWebViewCookieManager: NSObject {
99141
private func getDomainFromUrl(_ url: URL?) -> String {
100142
return url?.host ?? ""
101143
}
144+
145+
internal func inspectScopes(_ scopes: [String]?, warn: ((String) -> Void)? = nil) {
146+
let warnFunction = warn ?? { message in
147+
SFSDKHybridLogger.w(Self.self, message: message)
148+
}
149+
150+
let scopeParser = ScopeParser(scopes: scopes)
151+
152+
// full encompasses all other scopes except for refresh
153+
if !scopeParser.hasScope("full") {
154+
if !scopeParser.hasScope("web") {
155+
warnFunction("Missing web scope: will not be able to access web content.")
156+
157+
// web encompasses visualforce scope
158+
if !scopeParser.hasScope("visualforce") {
159+
warnFunction("Missing visualforce scope: will not be able to access Visualforce pages.")
160+
}
161+
}
162+
163+
if !scopeParser.hasScope("lightning") {
164+
warnFunction("Missing lightning scope: will not be able to access Lightning applications.")
165+
}
166+
167+
if !scopeParser.hasScope("content") {
168+
warnFunction("Missing content scope: will not be able to access Content resources.")
169+
}
170+
}
171+
}
102172

103173
static let CLIENT_SRC = "clientSrc"
104174
static let SID_CLIENT = "sid_Client"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//
2+
// Use this file to import your target's public headers that you would like to expose to Swift.
3+
//
4+

0 commit comments

Comments
 (0)