Skip to content

Commit 5e33153

Browse files
committed
Allow for custom domains in the FDL iOS SDK
Allow for custo domains to be whitelisted by the info.plist file.
1 parent 55acc03 commit 5e33153

File tree

8 files changed

+163
-16
lines changed

8 files changed

+163
-16
lines changed

Example/DynamicLinks/App/iOS/DL-Info.plist

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,13 @@
5050
<string>UIInterfaceOrientationLandscapeLeft</string>
5151
<string>UIInterfaceOrientationLandscapeRight</string>
5252
</array>
53+
<key>FirebaseDynamicLinksCustomDomains</key>
54+
<array>
55+
<string>https://google.com</string>
56+
<string>g.co</string>
57+
<string>https://g.co</string>
58+
<string>https://google.com/one</string>
59+
<string>https://a.firebase.com/mypath</string>
60+
</array>
5361
</dict>
5462
</plist>

Example/DynamicLinks/FDLBuilderTestAppObjC/Info.plist

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>en</string>
7+
<key>CFBundleExecutable</key>
8+
<string>$(EXECUTABLE_NAME)</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+
<key>CFBundleInfoDictionaryVersion</key>
12+
<string>6.0</string>
13+
<key>CFBundleName</key>
14+
<string>$(PRODUCT_NAME)</string>
15+
<key>CFBundlePackageType</key>
16+
<string>APPL</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>1.0</string>
519
<key>CFBundleURLTypes</key>
620
<array>
721
<dict>
@@ -55,20 +69,6 @@
5569
</array>
5670
</dict>
5771
</array>
58-
<key>CFBundleDevelopmentRegion</key>
59-
<string>en</string>
60-
<key>CFBundleExecutable</key>
61-
<string>$(EXECUTABLE_NAME)</string>
62-
<key>CFBundleIdentifier</key>
63-
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
64-
<key>CFBundleInfoDictionaryVersion</key>
65-
<string>6.0</string>
66-
<key>CFBundleName</key>
67-
<string>$(PRODUCT_NAME)</string>
68-
<key>CFBundlePackageType</key>
69-
<string>APPL</string>
70-
<key>CFBundleShortVersionString</key>
71-
<string>1.0</string>
7272
<key>CFBundleVersion</key>
7373
<string>1</string>
7474
<key>LSRequiresIPhoneOS</key>
@@ -85,5 +85,19 @@
8585
<string>UIInterfaceOrientationLandscapeLeft</string>
8686
<string>UIInterfaceOrientationLandscapeRight</string>
8787
</array>
88+
<key>FirebaseDynamicLinksCustomDomains</key>
89+
<array>
90+
<string>https://mydomain.com</string>
91+
<string>https://mydomain2.com</string>
92+
<string>https://google.com</string>
93+
<string>https://google.com</string>
94+
<string>go</string>
95+
<string>g.co</string>
96+
<string>https://go</string>
97+
<string>https://g.co</string>
98+
<string>https://google.com/one</string>
99+
<string>https://custom.com/one/two</string>
100+
<string>https://custom1.com/one/</string>
101+
</array>
88102
</dict>
89103
</plist>

Example/DynamicLinks/FDLBuilderTestAppObjCTests/Info.plist

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,24 @@
1818
<string>1.0</string>
1919
<key>CFBundleVersion</key>
2020
<string>1</string>
21+
<key>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
22+
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
23+
&lt;plist version=&quot;1.0&quot;&gt;
24+
&lt;array&gt;
25+
&lt;string&gt;https://mydomain.com&lt;/string&gt;
26+
&lt;string&gt;https://mydomain2.com&lt;/string&gt;
27+
&lt;string&gt;https://google.com&lt;/string&gt;
28+
&lt;string&gt;https://google.com&lt;/string&gt;
29+
&lt;string&gt;go&lt;/string&gt;
30+
&lt;string&gt;g.co&lt;/string&gt;
31+
&lt;string&gt;https://go&lt;/string&gt;
32+
&lt;string&gt;https://g.co&lt;/string&gt;
33+
&lt;string&gt;https://google.com/one&lt;/string&gt;
34+
&lt;string&gt;https://custom.com/one/two&lt;/string&gt;
35+
&lt;string&gt;https://custom1.com/one/&lt;/string&gt;
36+
&lt;/array&gt;
37+
&lt;/plist&gt;
38+
</key>
39+
<string></string>
2140
</dict>
2241
</plist>

Example/DynamicLinks/Tests/FIRDynamicLinksTest.m

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,63 @@ - (void)testSelfDiagnoseCompletionCalled {
10151015
[self waitForExpectationsWithTimeout:2.0 handler:nil];
10161016
}
10171017

1018+
#pragma mark - Custom domain tests
1019+
- (void)testValidCustomDomainNames {
1020+
// Entries in plist file:
1021+
// https://google.com
1022+
// g.co
1023+
// https://g.co
1024+
// https://google.com/one
1025+
// https://a.firebase.com/mypath
1026+
1027+
[FIRApp configure];
1028+
NSArray<NSString *> *urlStrings = @[
1029+
@"https://google.com/1", // Valid domain. Any path.
1030+
@"https://google.com/2", // Valid domain. Any path.
1031+
@"https://google.com/one", // Valid domain. Specified path.
1032+
@"https://g.co/1", // Valid domain. Any path.
1033+
@"https://a.firebase.com/mypath/",// Valid subdomain.
1034+
@"https://a.firebase.com/mypath/abcd/efgh",// Long path.
1035+
];
1036+
1037+
for (NSString *urlString in urlStrings) {
1038+
NSURL *url = [NSURL URLWithString:urlString];
1039+
BOOL matchesShortLinkFormat = [self.service matchesShortLinkFormat:url];
1040+
1041+
XCTAssertTrue(matchesShortLinkFormat,
1042+
@"Non-DDL domain URL matched short link format with URL: %@", url);
1043+
}
1044+
}
1045+
1046+
- (void)testInvalidCustomDomainNames {
1047+
// Entries in plist file:
1048+
// https://google.com
1049+
// g.co
1050+
// https://g.co
1051+
// https://google.com/one
1052+
// https://a.firebase.com/mypath
1053+
1054+
[FIRApp configure];
1055+
NSArray<NSString *> *urlStrings = @[
1056+
@"mydomain.com", // Domain not in plist. Also, no scheme.
1057+
@"http://mydomain", // Domain not in plist. No path.
1058+
@"google.com", // Valid domain. No scheme.
1059+
@"https://google.com", // Valid domain. No path.
1060+
@"http://google.com", // Valid domain. Invalid scheme.
1061+
@"https://g.co.com/abc", //Invalid domain starts with valid domain.
1062+
@"https://firebase.com/mypath", // Invalid (sub)domain.
1063+
@"https://b.firebase.com/mypath" // Invalid subdomain.
1064+
];
1065+
1066+
for (NSString *urlString in urlStrings) {
1067+
NSURL *url = [NSURL URLWithString:urlString];
1068+
BOOL matchesShortLinkFormat = [self.service matchesShortLinkFormat:url];
1069+
1070+
XCTAssertFalse(matchesShortLinkFormat,
1071+
@"Non-DDL domain URL matched short link format with URL: %@", url);
1072+
}
1073+
}
1074+
10181075
#pragma mark - Private Helpers
10191076

10201077
- (void)removeAllFIRApps {

Example/DynamicLinks/Tests/GINDurableDeepLinkServiceReceivingTests.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#import <XCTest/XCTest.h>
1919

2020
#import "DynamicLinks/GINDurableDeepLinkServiceReceiving+Private.h"
21-
#import "OCMock.h"
21+
#import <OCMock/OCMock.h>
2222

2323
@interface GINDurableDeepLinkServiceReceivingTests : XCTestCase
2424
@end

Firebase/DynamicLinks/FIRDynamicLinks.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
// We should only open url once. We use the following key to store the state in the user defaults.
6161
static NSString *const kFIRDLOpenURLKey = @"com.google.appinvite.openURL";
6262

63+
// Custom domains to be whitelisted are optionally added as an array to the info.plist.
64+
static NSString *const kInfoPlistCustomDomainsKey = @"FirebaseDynamicLinksCustomDomains";
65+
6366
NS_ASSUME_NONNULL_BEGIN
6467

6568
@interface FIRDynamicLinks () <FIRDLRetrievalProcessDelegate>
@@ -216,6 +219,12 @@ - (void)configureDynamicLinks:(FIRApp *)app {
216219
}
217220
[NSException raise:kFirebaseDurableDeepLinkErrorDomain format:@"%@", message];
218221
}
222+
// Check to see if FirebaseDynamicLinksCustomDomains array is present.
223+
NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary;
224+
NSArray *customDomains = infoDictionary[kInfoPlistCustomDomainsKey];
225+
if (customDomains && customDomains.count > 0) {
226+
FIRDLAddToWhiteListForCustomDomainsArray(customDomains);
227+
}
219228
}
220229

221230
- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics {

Firebase/DynamicLinks/Utilities/FDLUtilities.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,9 @@ BOOL FIRDLMatchesShortLinkFormat(NSURL *URL);
136136
*/
137137
NSString *FIRDLMatchTypeStringFromServerString(NSString *_Nullable serverMatchTypeString);
138138

139+
/**
140+
Add custom domains from the info.plist to the internal whitelist.
141+
*/
142+
void FIRDLAddToWhiteListForCustomDomainsArray(NSArray* _Nonnull customDomains);
143+
139144
NS_ASSUME_NONNULL_END

Firebase/DynamicLinks/Utilities/FDLUtilities.m

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
NSString *const kFIRDLParameterWeakMatchEndpoint = @"invitation_weakMatchEndpoint";
3333
NSString *const kFIRDLParameterMatchMessage = @"match_message";
3434
NSString *const kFIRDLParameterRequestIPVersion = @"request_ip_version";
35+
static NSSet *FIRDLCustomDomains = nil;
3536

3637
NSURL *FIRDLCookieRetrievalURL(NSString *urlScheme, NSString *bundleID) {
3738
static NSString *const kFDLBundleIDQueryParameterName = @"fdl_ios_bundle_id";
@@ -192,6 +193,22 @@ BOOL FIRDLOSVersionSupported(NSString *_Nullable systemVersion, NSString *minSup
192193
return timeZoneName;
193194
}
194195

196+
BOOL FIRDLIsURLForWhiteListedCustomDomain(NSURL *_Nullable URL) {
197+
BOOL customDomainMatchFound = false;
198+
for(NSURL* allowedCustomDomain in FIRDLCustomDomains) {
199+
// All custom domain host names should match at a minimum.
200+
if ([allowedCustomDomain.host isEqualToString:URL.host]) {
201+
// Next, do a string compare to check if the full path matches as well.
202+
if (([URL.absoluteString rangeOfString:allowedCustomDomain.absoluteString options:NSCaseInsensitiveSearch | NSAnchoredSearch].location) == 0)
203+
{
204+
customDomainMatchFound = true;
205+
break;
206+
}
207+
}
208+
}
209+
return customDomainMatchFound;
210+
}
211+
195212
BOOL FIRDLCanParseUniversalLinkURL(NSURL *_Nullable URL) {
196213
// Handle universal links with format |https://goo.gl/app/<appcode>?<parameters>|.
197214
// Also support page.link format.
@@ -200,7 +217,11 @@ BOOL FIRDLCanParseUniversalLinkURL(NSURL *_Nullable URL) {
200217
// Handle universal links with format |https://<appcode>.app.goo.gl?<parameters>| and page.link.
201218
BOOL isDDLWithSubdomain =
202219
[URL.host hasSuffix:@".app.goo.gl"] || [URL.host hasSuffix:@".page.link"];
203-
return isDDLWithAppcodeInPath || isDDLWithSubdomain;
220+
221+
// Handle universal links for custom domains.
222+
BOOL isDDLWithCustomDomain = FIRDLIsURLForWhiteListedCustomDomain(URL);
223+
224+
return isDDLWithAppcodeInPath || isDDLWithSubdomain || isDDLWithCustomDomain;
204225
}
205226

206227
BOOL FIRDLMatchesShortLinkFormat(NSURL *URL) {
@@ -227,4 +248,18 @@ BOOL FIRDLMatchesShortLinkFormat(NSURL *URL) {
227248
return matchMap[serverMatchTypeString] ?: @"none";
228249
}
229250

251+
void FIRDLAddToWhiteListForCustomDomainsArray(NSArray* _Nonnull customDomains) {
252+
// Duplicates will be weeded out when converting to a set.
253+
NSMutableArray *validCustomDomains = [[NSMutableArray alloc] initWithCapacity:customDomains.count];
254+
for(NSString* customDomainEntry in customDomains) {
255+
NSURL* customDomainURL = [NSURL URLWithString:customDomainEntry];
256+
// We require a valid scheme for each custom domain enumerated in the info.plist file.
257+
if (customDomainURL && customDomainURL.scheme) {
258+
[validCustomDomains addObject:customDomainURL];
259+
}
260+
}
261+
// Duplicates will be weeded out when converting to a set.
262+
FIRDLCustomDomains = [NSSet setWithArray:validCustomDomains];
263+
}
264+
230265
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)