Skip to content

Commit

Permalink
feat(auth, authDomain): implement FirebaseOptions.authDomain on Auth
Browse files Browse the repository at this point in the history
JS SDK has FirebaseOptions.authDomain, and while the native SDKs do
not directly have this FirebaseOptions key, they do have
Auth.customAuthDomain - so take the key if it is set during initializeApp,
store it, then as Auth instances are initialized, set it in if it exists
  • Loading branch information
mikehardy committed Jan 8, 2024
1 parent d7589ea commit a1f4710
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 1 deletion.
1 change: 1 addition & 0 deletions jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jest.doMock('react-native', () => {
addIdTokenListener: jest.fn(),
setTenantId: jest.fn(),
useEmulator: jest.fn(),
configureAuthDomain: jest.fn(),
},
RNFBCrashlyticsModule: {},
RNFBDatabaseModule: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*
*/

import android.util.Log;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
Expand All @@ -38,6 +39,8 @@
public class ReactNativeFirebaseAppModule extends ReactNativeFirebaseModule {
private static final String TAG = "App";

public static Map<String, String> authDomains = new HashMap<>();

ReactNativeFirebaseAppModule(ReactApplicationContext reactContext) {
super(reactContext, TAG);
}
Expand All @@ -52,11 +55,22 @@ public void initialize() {
public void initializeApp(ReadableMap options, ReadableMap appConfig, Promise promise) {
FirebaseApp firebaseApp =
RCTConvertFirebase.readableMapToFirebaseApp(options, appConfig, getContext());
ReactNativeFirebaseAppModule.configureAuthDomain(
appConfig.getString("name"), options.getString("authDomain"));

WritableMap firebaseAppMap = RCTConvertFirebase.firebaseAppToWritableMap(firebaseApp);
promise.resolve(firebaseAppMap);
}

public static void configureAuthDomain(String name, String authDomain) {
if (authDomain != null) {
Log.d(TAG, name + " custom authDomain " + authDomain);
authDomains.put(name, authDomain);
} else {
authDomains.remove(name);
}
}

@ReactMethod
public void setAutomaticDataCollectionEnabled(String appName, Boolean enabled) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import io.invertase.firebase.app.ReactNativeFirebaseAppModule;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -56,6 +57,10 @@ public static Map<String, Object> firebaseAppToMap(FirebaseApp firebaseApp) {
options.put("messagingSenderId", appOptions.getGcmSenderId());
options.put("storageBucket", appOptions.getStorageBucket());

if (ReactNativeFirebaseAppModule.authDomains.get(name) != null) {
options.put("authDomain", ReactNativeFirebaseAppModule.authDomains.get(name));
}

root.put("options", options);
root.put("appConfig", appConfig);

Expand Down
2 changes: 2 additions & 0 deletions packages/app/ios/RNFBApp/RNFBAppModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

@interface RNFBAppModule : NSObject <RCTBridgeModule>

+ (NSString *)getCustomDomain:(NSString *)appName;

- (void)setLogLevel:(NSString *)logLevel;

@end
47 changes: 46 additions & 1 deletion packages/app/ios/RNFBApp/RNFBAppModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,52 @@ - (void)invalidate {
#pragma mark Firebase App Methods

RCT_EXPORT_METHOD(initializeApp
: (FIROptions *)firOptions appConfig
: (NSDictionary *)options appConfig
: (NSDictionary *)appConfig resolver
: (RCTPromiseResolveBlock)resolve rejecter
: (RCTPromiseRejectBlock)reject) {
RCTUnsafeExecuteOnMainQueueSync(^{
FIRApp *firApp;
NSString *appName = [appConfig valueForKey:@"name"];

NSString *appId = [options valueForKey:@"appId"];
NSString *messagingSenderId = [options valueForKey:@"messagingSenderId"];
FIROptions *firOptions = [[FIROptions alloc] initWithGoogleAppID:appId
GCMSenderID:messagingSenderId];
firOptions.APIKey = [options valueForKey:@"apiKey"];
firOptions.projectID = [options valueForKey:@"projectId"];
// kFirebaseOptionsDatabaseUrl
if (![[options valueForKey:@"databaseURL"] isEqual:[NSNull null]]) {
firOptions.databaseURL = [options valueForKey:@"databaseURL"];
}
// kFirebaseOptionsStorageBucket
if (![[options valueForKey:@"storageBucket"] isEqual:[NSNull null]]) {
firOptions.storageBucket = [options valueForKey:@"storageBucket"];
}
// kFirebaseOptionsDeepLinkURLScheme
if (![[options valueForKey:@"deepLinkURLScheme"] isEqual:[NSNull null]]) {
firOptions.deepLinkURLScheme = [options valueForKey:@"deepLinkURLScheme"];
}
// kFirebaseOptionsIosBundleId
if (![[options valueForKey:@"iosBundleId"] isEqual:[NSNull null]]) {
firOptions.bundleID = [options valueForKey:@"iosBundleId"];
}
// kFirebaseOptionsIosClientId
if (![[options valueForKey:@"iosClientId"] isEqual:[NSNull null]]) {
firOptions.clientID = [options valueForKey:@"iosClientId"];
}
// kFirebaseOptionsAppGroupId
if (![[options valueForKey:@"appGroupId"] isEqual:[NSNull null]]) {
firOptions.appGroupID = [options valueForKey:@"appGroupId"];
}

if ([options valueForKey:@"authDomain"] != nil) {
DLog(@"RNFBAuth app: %@ customAuthDomain: %@", appName, [options valueForKey:@"authDomain"]);
if (customAuthDomains == nil) {
customAuthDomains = [[NSMutableDictionary alloc] init];
}
customAuthDomains[appName] = [options valueForKey:@"authDomain"];
}
@try {
if (!appName || [appName isEqualToString:DEFAULT_APP_DISPLAY_NAME]) {
[FIRApp configureWithOptions:firOptions];
Expand All @@ -195,6 +233,13 @@ - (void)invalidate {
});
}

static NSMutableDictionary<NSString *, NSString *> *customAuthDomains;

+ (NSString *)getCustomDomain:(NSString *)appName {
DLog(@"authDomains: %@", customAuthDomains);
return customAuthDomains[appName];
}

RCT_EXPORT_METHOD(setLogLevel : (NSString *)logLevel) {
int level = FIRLoggerLevelError;
if ([logLevel isEqualToString:@"verbose"]) {
Expand Down
5 changes: 5 additions & 0 deletions packages/app/ios/RNFBApp/RNFBSharedUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#import "RNFBSharedUtils.h"
#import "RNFBAppModule.h"
#import "RNFBJSON.h"
#import "RNFBMeta.h"
#import "RNFBPreferences.h"
Expand Down Expand Up @@ -65,6 +66,10 @@ + (NSDictionary *)firAppToDictionary:(FIRApp *)firApp {
firAppOptions[@"clientId"] = firOptions.clientID;
firAppOptions[@"androidClientID"] = firOptions.androidClientID;
firAppOptions[@"deepLinkUrlScheme"] = firOptions.deepLinkURLScheme;
// not in FIROptions API but in JS SDK and project config JSON
if ([RNFBAppModule getCustomDomain:name] != nil) {
firAppOptions[@"authDomain"] = [RNFBAppModule getCustomDomain:name];
}

firAppDictionary[@"options"] = firAppOptions;
firAppDictionary[@"appConfig"] = firAppConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import com.google.firebase.auth.TwitterAuthProvider;
import com.google.firebase.auth.UserInfo;
import com.google.firebase.auth.UserProfileChangeRequest;
import io.invertase.firebase.app.ReactNativeFirebaseAppModule;
import io.invertase.firebase.common.ReactNativeFirebaseEvent;
import io.invertase.firebase.common.ReactNativeFirebaseEventEmitter;
import io.invertase.firebase.common.ReactNativeFirebaseModule;
Expand Down Expand Up @@ -150,6 +151,26 @@ public void onCatalystInstanceDestroy() {
mMultiFactorSessions.clear();
}

@ReactMethod
public void configureAuthDomain(final String appName) {
Log.d(TAG, "configureAuthDomain");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
String authDomain = ReactNativeFirebaseAppModule.authDomains.get(appName);
Log.d(TAG, "configureAuthDomain - app " + appName + " domain? " + authDomain);
if (authDomain != null) {
firebaseAuth.setCustomAuthDomain(authDomain);
}
}

@ReactMethod
public void getCustomAuthDomain(final String appName, final Promise promise) {
Log.d(TAG, "configureAuthDomain");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
promise.resolve(firebaseAuth.getCustomAuthDomain());
}

/** Add a new auth state listener - if one doesn't exist already */
@ReactMethod
public void addAuthStateListener(final String appName) {
Expand Down
17 changes: 17 additions & 0 deletions packages/auth/e2e/auth.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,23 @@ describe('auth() modular', function () {

secondaryApp.auth().app.name.should.equal('secondaryFromNative');
});

it('supports an app initialized with custom authDomain', async function () {
const { getAuth, getCustomAuthDomain } = authModular;
const { initializeApp } = modular;

const name = `testscoreapp${FirebaseHelpers.id}`;
const platformAppConfig = FirebaseHelpers.app.config();
platformAppConfig.authDomain = 'example.com';
const newApp = await initializeApp(platformAppConfig, name);
const secondaryApp = firebase.app(name);
const secondaryAuth = getAuth(secondaryApp);
secondaryAuth.app.name.should.equal(name);
secondaryApp.auth().app.name.should.equal(name);
const customAuthDomain = await getCustomAuthDomain(secondaryAuth);
customAuthDomain.should.equal(platformAppConfig.authDomain);
return newApp.delete();
});
});
describe('applyActionCode()', function () {
// Needs a different setup to work against the auth emulator
Expand Down
16 changes: 16 additions & 0 deletions packages/auth/ios/RNFBAuth/RNFBAuthModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import <React/RCTUtils.h>

#import "RNFBApp/RCTConvert+FIRApp.h"
#import "RNFBApp/RNFBAppModule.h"
#import "RNFBApp/RNFBSharedUtils.h"
#import "RNFBAuthModule.h"

Expand Down Expand Up @@ -159,6 +160,21 @@ - (void)invalidate {
}
}

RCT_EXPORT_METHOD(configureAuthDomain : (FIRApp *)firebaseApp) {
NSString *authDomain = [RNFBAppModule getCustomDomain:firebaseApp.name];
DLog(@"RNFBAuth app: %@ customAuthDomain: %@", firebaseApp.name, authDomain);
if (authDomain != nil) {
[FIRAuth authWithApp:firebaseApp].customAuthDomain = authDomain;
}
}

RCT_EXPORT_METHOD(getCustomAuthDomain
: (FIRApp *)firebaseApp
: (RCTPromiseResolveBlock)resolve
: (RCTPromiseRejectBlock)reject) {
resolve([FIRAuth authWithApp:firebaseApp].customAuthDomain);
}

RCT_EXPORT_METHOD(setAppVerificationDisabledForTesting : (FIRApp *)firebaseApp : (BOOL)disabled) {
[FIRAuth authWithApp:firebaseApp].settings.appVerificationDisabledForTesting = disabled;
}
Expand Down
11 changes: 11 additions & 0 deletions packages/auth/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export {
fetchSignInMethodsForEmail,
getAdditionalUserInfo,
getAuth,
getCustomAuthDomain,
getIdToken,
getIdTokenResult,
getMultiFactorResolver,
Expand Down Expand Up @@ -173,6 +174,12 @@ class FirebaseAuthModule extends FirebaseModule {

this.native.addAuthStateListener();
this.native.addIdTokenListener();

// custom authDomain in only available from App's FirebaseOptions,
// but we need it in Auth if it exists. During app configuration we store
// mappings from app name to authDomain, this auth constructor
// is a reasonable time to use the mapping and set it into auth natively
this.native.configureAuthDomain();
}

get languageCode() {
Expand Down Expand Up @@ -500,6 +507,10 @@ class FirebaseAuthModule extends FirebaseModule {
}
return new MultiFactorUser(this, user);
}

getCustomAuthDomain() {
return this.native.getCustomAuthDomain();
}
}

// import { SDK_VERSION } from '@react-native-firebase/auth';
Expand Down
5 changes: 5 additions & 0 deletions packages/auth/lib/modular/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,3 +467,8 @@ export async function verifyBeforeUpdateEmail(user, newEmail, actionCodeSettings
export function getAdditionalUserInfo(userCredential) {
return userCredential.additionalUserInfo;
}

export function getCustomAuthDomain(auth) {
const _auth = _getUnderlyingAuth(auth);
return _auth.getCustomAuthDomain();
}

0 comments on commit a1f4710

Please sign in to comment.