Skip to content

Commit

Permalink
custom auth domain changes (#11619)
Browse files Browse the repository at this point in the history
Co-authored-by: renkelvin <[email protected]>
  • Loading branch information
pragatimodi and renkelvin authored Sep 27, 2023
1 parent 681e740 commit 91a6974
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 15 deletions.
3 changes: 3 additions & 0 deletions FirebaseAuth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 10.16.0
- [added] Added custom auth domain support in recaptcha v2 authentication flows. (#7553)

# 10.14.0
- [added] Added reCAPTCHA verification support in email authentication flows. (#11231)

Expand Down
7 changes: 7 additions & 0 deletions FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuth.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,13 @@ NS_SWIFT_NAME(Auth)
*/
@property(nonatomic, strong, nullable) NSData *APNSToken API_UNAVAILABLE(macos, tvos, watchos);

/**
* @property customAuthDomain
* @brief The custom authentication domain used to handle all sign-in redirects. End-users will see
* this domain when signing in. This domain must be allowlisted in the Firebase Console.
*/
@property(nonatomic, copy, nullable) NSString *customAuthDomain;

/** @fn init
@brief Please access auth instances using `Auth.auth()` and `Auth.auth(app:)`.
*/
Expand Down
6 changes: 6 additions & 0 deletions FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ typedef void (^FIRFetchAuthDomainCallback)(NSString *_Nullable authDomain,
authType:(NSString *)authType
callbackScheme:(NSString *)callbackScheme;

/** @fn extractDomain:urlString
@brief Strips url of scheme and path string to extract domain name
@param urlString URL string for domain
*/
+ (NSString *)extractDomain:(NSString *)urlString;

/** @fn fetchAuthDomainWithCompletion:completion:
@brief Fetches the auth domain associated with the Firebase Project.
@param completion The callback invoked after the auth domain has been constructed or an error
Expand Down
71 changes: 58 additions & 13 deletions FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ + (BOOL)isExpectedCallbackURL:(nullable NSURL *)URL
return NO;
}

+ (NSString *)extractDomain:(NSString *)urlString {
// Remove trailing slashes
urlString = [urlString
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"/"]];

// Check for the presence of a scheme (e.g., http:// or https://)
NSRange range = [urlString rangeOfString:@"http://" options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound) {
urlString = [urlString stringByReplacingCharactersInRange:range withString:@""];
} else {
range = [urlString rangeOfString:@"https://" options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound) {
urlString = [urlString stringByReplacingCharactersInRange:range withString:@""];
}
}

// Split the URL by "/"
NSArray *urlComponents = [urlString componentsSeparatedByString:@"/"];

// The domain is the first component after removing the scheme
NSString *domain = urlComponents[0];

return domain;
}

+ (void)fetchAuthDomainWithRequestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration
completion:(FIRFetchAuthDomainCallback)completion {
if (requestConfiguration.emulatorHostAndPort) {
Expand All @@ -104,22 +129,42 @@ + (void)fetchAuthDomainWithRequestConfiguration:(FIRAuthRequestConfiguration *)r
return;
}
// Look up an authorized domain ends with one of the supportedAuthDomains.
// The sequence of supportedAuthDomains matters. ("firebaseapp.com", "web.app")
// The searching ends once the first valid suportedAuthDomain is found.
// The searching ends once the first valid supportedAuthDomain is found.
NSString *authDomain;
for (NSString *domain in response.authorizedDomains) {
for (NSString *suportedAuthDomain in [self supportedAuthDomains]) {
NSInteger index = domain.length - suportedAuthDomain.length;
if (index >= 2) {
if ([domain hasSuffix:suportedAuthDomain] &&
domain.length >= suportedAuthDomain.length + 2) {
authDomain = domain;
break;
}
NSString *customAuthDomain = requestConfiguration.auth.customAuthDomain;
if (customAuthDomain) {
customAuthDomain = [FIRAuthWebUtils extractDomain:customAuthDomain];
BOOL isCustomAuthDomainAuthorized = NO;
for (NSString *domain in response.authorizedDomains) {
if ([customAuthDomain isEqualToString:domain]) {
authDomain = customAuthDomain;
isCustomAuthDomainAuthorized = YES;
break;
}
}
if (authDomain != nil) {
break;
if (!isCustomAuthDomainAuthorized) {
NSError *customDomainError =
[FIRAuthErrorUtils unauthorizedDomainErrorWithMessage:
@"Error while validating application identity: The "
@"configured custom domain is not allowlisted."];
completion(nil, customDomainError);
return;
}
} else {
for (NSString *domain in response.authorizedDomains) {
for (NSString *supportedAuthDomain in [self supportedAuthDomains]) {
NSInteger index = domain.length - supportedAuthDomain.length;
if (index >= 2) {
if ([domain hasSuffix:supportedAuthDomain] &&
domain.length >= supportedAuthDomain.length + 2) {
authDomain = domain;
break;
}
}
}
if (authDomain != nil) {
break;
}
}
}
if (!authDomain.length) {
Expand Down
2 changes: 2 additions & 0 deletions FirebaseAuth/Tests/Sample/Sample/MainViewController+Auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ NS_ASSUME_NONNULL_BEGIN

- (void)signInAnonymouslyWithCallback:(nullable FIRAuthDataResultCallback)callback;

- (void)setAuthDomain;

@end

NS_ASSUME_NONNULL_END
23 changes: 21 additions & 2 deletions FirebaseAuth/Tests/Sample/Sample/MainViewController+Auth.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ - (StaticContentTableViewSection *)authSection {
[StaticContentTableViewCell cellWithTitle:@"Sign out"
action:^{ [weakSelf signOut]; }],
[StaticContentTableViewCell cellWithTitle:@"Initialize Recaptcha Config"
action:^{ [weakSelf initializeRecaptchaConfig]; }]
]];
action:^{ [weakSelf initializeRecaptchaConfig]; }],
[StaticContentTableViewCell cellWithTitle:@"Set Auth Domain"
action:^{ [weakSelf setAuthDomain]; }]
]];
}

- (void)signInAnonymously {
Expand Down Expand Up @@ -84,6 +86,23 @@ - (void)initializeRecaptchaConfig {
}];
}

- (void)setAuthDomain {
[self showTextInputPromptWithMessage:@"Auth Domain:"
completionBlock:^(BOOL userPressedOK, NSString *_Nullable customAuthDomain) {
if (userPressedOK && customAuthDomain.length) {
FIRAuth *auth = [AppManager auth];
if(!auth) {
[self logFailedTest:@"Could not obtain auth object."];
return;
}
auth.customAuthDomain = customAuthDomain;
[self logSuccess:[NSString stringWithFormat:@"Successfully set auth domain to: %@", customAuthDomain]];
}
return;
}];
}


@end

NS_ASSUME_NONNULL_END
73 changes: 73 additions & 0 deletions FirebaseAuth/Tests/Unit/FIRAuthWebUtilsTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#import <XCTest/XCTest.h>
#import "FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.h"

/** @class FIRAuthWebUtilsTests
@brief Tests for the FIRAuthWebUtils class.
*/
@interface FIRAuthWebUtilsTests : XCTestCase
@end

@implementation FIRAuthWebUtilsTests

/** @fn testExtractDomainWithHTTP
@brief Test case for extracting the domain from a URL with "http://" scheme.
*/
- (void)testExtractDomainWithHTTP {
NSString *urlString = @"http://www.example.com/path/to/resource";
NSString *domain = [FIRAuthWebUtils extractDomain:urlString];
XCTAssertEqualObjects(domain, @"www.example.com");
}

/** @fn testExtractDomainWithHTTPS
@brief Test case for extracting the domain from a URL with "https://" scheme.
*/
- (void)testExtractDomainWithHTTPS {
NSString *urlString = @"https://www.example.com/path/to/resource";
NSString *domain = [FIRAuthWebUtils extractDomain:urlString];
XCTAssertEqualObjects(domain, @"www.example.com");
}

/** @fn testExtractDomainWithoutScheme
@brief Test case for extracting the domain from a URL without a scheme (assumes HTTP by default).
*/
- (void)testExtractDomainWithoutScheme {
NSString *urlString = @"www.example.com/path/to/resource";
NSString *domain = [FIRAuthWebUtils extractDomain:urlString];
XCTAssertEqualObjects(domain, @"www.example.com");
}

/** @fn testExtractDomainWithTrailingSlashes
@brief Test case for extracting the domain from a URL with trailing slashes.
*/
- (void)testExtractDomainWithTrailingSlashes {
NSString *urlString = @"http://www.example.com/////";
NSString *domain = [FIRAuthWebUtils extractDomain:urlString];
XCTAssertEqualObjects(domain, @"www.example.com");
}

/** @fn testExtractDomainWithStringDomain
@brief Test case for extracting the domain from a string that represents just the domain itself.
*/
- (void)testExtractDomainWithStringDomain {
NSString *urlString = @"example.com";
NSString *domain = [FIRAuthWebUtils extractDomain:urlString];
XCTAssertEqualObjects(domain, @"example.com");
}

@end

0 comments on commit 91a6974

Please sign in to comment.