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
20 changes: 20 additions & 0 deletions FirebaseAuth/Sources/Auth/FIRAuth.m
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,26 @@ - (void)useAppLanguage {
});
}

- (void)useEmulatorWithHost:(NSString *)host port:(NSInteger)port {
NSAssert(host.length > 0, @"Cannot connect to nil or empty host");

NSString *formattedHost;
if ([host containsString:@":"]) {
// Host is an IPv6 address and should be formatted with surrounding brackets.
formattedHost = [NSString stringWithFormat:@"[%@]", host];
} else {
formattedHost = host;
}

dispatch_sync(FIRAuthGlobalWorkQueue(), ^{
self->_requestConfiguration.emulatorHostAndPort =
[NSString stringWithFormat:@"%@:%ld", formattedHost, (long)port];
#if TARGET_OS_IOS
self->_settings.appVerificationDisabledForTesting = YES;
#endif
});
}

- (nullable NSString *)languageCode {
return _requestConfiguration.languageCode;
}
Expand Down
5 changes: 5 additions & 0 deletions FirebaseAuth/Sources/Auth/FIRAuth_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ NS_ASSUME_NONNULL_BEGIN
- (FIRAuthDataResultCallback)signInFlowAuthDataResultCallbackByDecoratingCallback:
(nullable FIRAuthDataResultCallback)callback;

/** @fn useEmulatorWithHost:port
@brief Configures Firebase Auth to connect to an emulated host instead of the remote backend.
*/
- (void)useEmulatorWithHost:(NSString *)host port:(NSInteger)port;

@end

NS_ASSUME_NONNULL_END
19 changes: 16 additions & 3 deletions FirebaseAuth/Sources/AuthProvider/OAuth/FIROAuthProvider.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ typedef void (^FIRHeadfulLiteURLCallBack)(NSURL *_Nullable headfulLiteURL,
*/
NSString *const kHeadfulLiteURLStringFormat = @"https://%@/__/auth/handler?%@";

/** @var kHeadfulLiteEmulatorURLStringFormat
@brief The format of the URL used to open the emulated headful lite page during sign-in.
*/
NSString *const kHeadfulLiteEmulatorURLStringFormat = @"http://%@/emulator/auth/handler?%@";

/** @var kauthTypeSignInWithRedirect
@brief The auth type to be specified in the sign-in request with redirect request and response.
*/
Expand Down Expand Up @@ -323,9 +328,17 @@ - (void)getHeadFulLiteURLWithEventID:(NSString *)eventID
}
NSString *argumentsString = [strongSelf
httpArgumentsStringForArgsDictionary:urlArguments];
NSString *URLString =
[NSString stringWithFormat:kHeadfulLiteURLStringFormat,
authDomain, argumentsString];
NSString *URLString;
if (strongSelf->_auth.requestConfiguration
.emulatorHostAndPort) {
URLString = [NSString
stringWithFormat:kHeadfulLiteEmulatorURLStringFormat,
authDomain, argumentsString];
} else {
URLString =
[NSString stringWithFormat:kHeadfulLiteURLStringFormat,
authDomain, argumentsString];
}
if (completion) {
NSCharacterSet *set =
[NSCharacterSet URLFragmentAllowedCharacterSet];
Expand Down
4 changes: 4 additions & 0 deletions FirebaseAuth/Sources/Backend/FIRAuthBackend.m
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,10 @@ - (void)asyncPostToURLWithRequestConfiguration:(FIRAuthRequestConfiguration *)re
[request setValue:languageCode forHTTPHeaderField:kFirebaseLocalHeader];
}
GTMSessionFetcher *fetcher = [_fetcherService fetcherWithRequest:request];
NSString *emulatorHostAndPort = requestConfiguration.emulatorHostAndPort;
if (emulatorHostAndPort) {
fetcher.allowLocalhostRequest = YES;
}
fetcher.bodyData = body;
[fetcher beginFetchWithCompletionHandler:handler];
}
Expand Down
5 changes: 5 additions & 0 deletions FirebaseAuth/Sources/Backend/FIRAuthRequestConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property(nonatomic, copy, nullable) NSString *additionalFrameworkMarker;

/** @property emulatorHostAndPort
@brief If set, the local emulator host and port to point to instead of the remote backend.
*/
@property(nonatomic, copy, nullable) NSString *emulatorHostAndPort;

- (instancetype)init NS_UNAVAILABLE;

/** @fn initWithRequestClass:APIKey:authLanguage:
Expand Down
40 changes: 30 additions & 10 deletions FirebaseAuth/Sources/Backend/FIRIdentityToolkitRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@

NS_ASSUME_NONNULL_BEGIN

static NSString *const kHttpsProtocol = @"https:";
static NSString *const kHttpProtocol = @"http:";

static NSString *const kFirebaseAuthAPIURLFormat =
@"https://%@/identitytoolkit/v3/relyingparty/%@?key=%@";
static NSString *const kIdentityPlatformAPIURLFormat = @"https://%@/v2/%@?key=%@";
@"%@//%@/identitytoolkit/v3/relyingparty/%@?key=%@";
static NSString *const kIdentityPlatformAPIURLFormat = @"%@//%@/v2/%@?key=%@";
static NSString *const kEmulatorHostAndPrefixFormat = @"%@/%@";

static NSString *gAPIHost = @"www.googleapis.com";

Expand Down Expand Up @@ -80,23 +84,39 @@ - (BOOL)containsPostBody {

- (NSURL *)requestURL {
NSString *apiURLFormat;
NSString *apiHost;
NSString *apiProtocol;
NSString *apiHostAndPathPrefix;

NSString *emulatorHostAndPort = _requestConfiguration.emulatorHostAndPort;

if (_useIdentityPlatform) {
apiURLFormat = kIdentityPlatformAPIURLFormat;
if (_useStaging) {
apiHost = kIdentityPlatformStagingAPIHost;
apiProtocol = kHttpsProtocol;
if (emulatorHostAndPort) {
apiProtocol = kHttpProtocol;
apiHostAndPathPrefix =
[NSString stringWithFormat:kEmulatorHostAndPrefixFormat, emulatorHostAndPort,
kIdentityPlatformAPIHost];
} else if (_useStaging) {
apiHostAndPathPrefix = kIdentityPlatformStagingAPIHost;
} else {
apiHost = kIdentityPlatformAPIHost;
apiHostAndPathPrefix = kIdentityPlatformAPIHost;
}
} else {
apiURLFormat = kFirebaseAuthAPIURLFormat;
if (_useStaging) {
apiHost = kFirebaseAuthStagingAPIHost;
apiProtocol = kHttpsProtocol;
if (emulatorHostAndPort) {
apiProtocol = kHttpProtocol;
apiHostAndPathPrefix = [NSString
stringWithFormat:kEmulatorHostAndPrefixFormat, emulatorHostAndPort, kFirebaseAuthAPIHost];
} else if (_useStaging) {
apiHostAndPathPrefix = kFirebaseAuthStagingAPIHost;
} else {
apiHost = kFirebaseAuthAPIHost;
apiHostAndPathPrefix = kFirebaseAuthAPIHost;
}
}
NSString *URLString = [NSString stringWithFormat:apiURLFormat, apiHost, _endpoint, _APIKey];
NSString *URLString = [NSString
stringWithFormat:apiURLFormat, apiProtocol, apiHostAndPathPrefix, _endpoint, _APIKey];
NSURL *URL = [NSURL URLWithString:URLString];
return URL;
}
Expand Down
18 changes: 16 additions & 2 deletions FirebaseAuth/Sources/Backend/RPC/FIRSecureTokenRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
*/
static NSString *const kFIRSecureTokenServiceGetTokenURLFormat = @"https://%@/v1/token?key=%@";

/** @var kFIREmulatorURLFormat
@brief The format of the emulated secure token service URLs. Requires string format substitution
with the emulator host, the gAPIHost, and the client's API Key.
*/
static NSString *const kFIREmulatorURLFormat = @"http://%@/%@/v1/token?key=%@";

/** @var kFIRSecureTokenServiceGrantTypeRefreshToken
@brief The string value of the @c FIRSecureTokenRequestGrantTypeRefreshToken request type.
*/
Expand Down Expand Up @@ -123,8 +129,16 @@ - (FIRAuthRequestConfiguration *)requestConfiguration {
}

- (NSURL *)requestURL {
NSString *URLString =
[NSString stringWithFormat:kFIRSecureTokenServiceGetTokenURLFormat, gAPIHost, _APIKey];
NSString *URLString;

NSString *emulatorHostAndPort = _requestConfiguration.emulatorHostAndPort;
if (emulatorHostAndPort) {
URLString =
[NSString stringWithFormat:kFIREmulatorURLFormat, emulatorHostAndPort, gAPIHost, _APIKey];
} else {
URLString =
[NSString stringWithFormat:kFIRSecureTokenServiceGetTokenURLFormat, gAPIHost, _APIKey];
}
NSURL *URL = [NSURL URLWithString:URLString];
return URL;
}
Expand Down
6 changes: 6 additions & 0 deletions FirebaseAuth/Sources/Utilities/FIRAuthWebUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ + (BOOL)isExpectedCallbackURL:(nullable NSURL *)URL

+ (void)fetchAuthDomainWithRequestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration
completion:(FIRFetchAuthDomainCallback)completion {
if (requestConfiguration.emulatorHostAndPort) {
// If we are using the auth emulator, we do not want to call the GetProjectConfig endpoint. The
// widget is hosted on the emulator host and port, so we can return that directly.
completion(requestConfiguration.emulatorHostAndPort, nil);
return;
}
FIRGetProjectConfigRequest *request =
[[FIRGetProjectConfigRequest alloc] initWithRequestConfiguration:requestConfiguration];

Expand Down
36 changes: 36 additions & 0 deletions FirebaseAuth/Tests/Unit/FIRAuthTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRActionCodeSettings.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRAdditionalUserInfo.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuthSettings.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIREmailAuthProvider.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRFacebookAuthProvider.h"
#import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRGoogleAuthProvider.h"
Expand Down Expand Up @@ -2191,6 +2192,41 @@ - (void)testIDTokenChanges {
[self waitForTimeIntervel:kWaitInterval]; // make sure listener is no longer called
}

/** @fn testUseEmulator
@brief Tests the @c useEmulatorWithHost:port: method.
*/
- (void)testUseEmulator {
[[FIRAuth auth] useEmulatorWithHost:@"host" port:12345];

XCTAssertEqualObjects(@"host:12345", [FIRAuth auth].requestConfiguration.emulatorHostAndPort);
#if TARGET_OS_IOS
XCTAssertTrue([FIRAuth auth].settings.isAppVerificationDisabledForTesting);
#endif
}

/** @fn testUseEmulatorNeverCalled
@brief Tests that the emulatorHostAndPort stored in @c FIRAuthRequestConfiguration is nil if the
@c useEmulatorWithHost:port: is not called.
*/
- (void)testUseEmulatorNeverCalled {
XCTAssertEqualObjects(nil, [FIRAuth auth].requestConfiguration.emulatorHostAndPort);
#if TARGET_OS_IOS
XCTAssertFalse([FIRAuth auth].settings.isAppVerificationDisabledForTesting);
#endif
}

/** @fn testUseEmulatorIPv6Address
@brief Tests the @c useEmulatorWithHost:port: method with an IPv6 host address.
*/
- (void)testUseEmulatorIPv6Address {
[[FIRAuth auth] useEmulatorWithHost:@"::1" port:12345];

XCTAssertEqualObjects(@"[::1]:12345", [FIRAuth auth].requestConfiguration.emulatorHostAndPort);
#if TARGET_OS_IOS
XCTAssertTrue([FIRAuth auth].settings.isAppVerificationDisabledForTesting);
#endif
}

#pragma mark - Automatic Token Refresh Tests.

/** @fn testAutomaticTokenRefresh
Expand Down
Loading