@react-native-firebase/auth
Version:
React Native Firebase - The authentication module provides an easy-to-use API to integrate an authentication workflow into new and existing applications. React Native Firebase provides access to all Firebase authentication methods and identity providers.
510 lines (435 loc) • 15.7 kB
JavaScript
/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library 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 {
createDeprecationProxy,
isAndroid,
isBoolean,
isNull,
isOther,
isString,
isValidUrl,
parseListenerOrObserver,
} from '@react-native-firebase/app/lib/common';
import { setReactNativeModule } from '@react-native-firebase/app/lib/internal/nativeModule';
import {
FirebaseModule,
createModuleNamespace,
getFirebaseRoot,
} from '@react-native-firebase/app/lib/internal';
import ConfirmationResult from './ConfirmationResult';
import PhoneAuthListener from './PhoneAuthListener';
import PhoneMultiFactorGenerator from './PhoneMultiFactorGenerator';
import Settings from './Settings';
import User from './User';
import { getMultiFactorResolver } from './getMultiFactorResolver';
import { MultiFactorUser, multiFactor } from './multiFactor';
import AppleAuthProvider from './providers/AppleAuthProvider';
import EmailAuthProvider from './providers/EmailAuthProvider';
import FacebookAuthProvider from './providers/FacebookAuthProvider';
import GithubAuthProvider from './providers/GithubAuthProvider';
import GoogleAuthProvider from './providers/GoogleAuthProvider';
import OAuthProvider from './providers/OAuthProvider';
import OIDCAuthProvider from './providers/OIDCAuthProvider';
import PhoneAuthProvider from './providers/PhoneAuthProvider';
import TwitterAuthProvider from './providers/TwitterAuthProvider';
import version from './version';
import fallBackModule from './web/RNFBAuthModule';
const PhoneAuthState = {
CODE_SENT: 'sent',
AUTO_VERIFY_TIMEOUT: 'timeout',
AUTO_VERIFIED: 'verified',
ERROR: 'error',
};
export {
AppleAuthProvider,
EmailAuthProvider,
PhoneAuthProvider,
GoogleAuthProvider,
GithubAuthProvider,
TwitterAuthProvider,
FacebookAuthProvider,
PhoneMultiFactorGenerator,
OAuthProvider,
OIDCAuthProvider,
PhoneAuthState,
};
const statics = {
AppleAuthProvider,
EmailAuthProvider,
PhoneAuthProvider,
GoogleAuthProvider,
GithubAuthProvider,
TwitterAuthProvider,
FacebookAuthProvider,
PhoneMultiFactorGenerator,
OAuthProvider,
OIDCAuthProvider,
PhoneAuthState,
getMultiFactorResolver,
multiFactor,
};
const namespace = 'auth';
const nativeModuleName = 'RNFBAuthModule';
class FirebaseAuthModule extends FirebaseModule {
constructor(...args) {
super(...args);
this._user = null;
this._settings = null;
this._authResult = false;
this._languageCode = this.native.APP_LANGUAGE[this.app._name];
this._tenantId = null;
if (!this.languageCode) {
this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]'];
}
if (this.native.APP_USER[this.app._name]) {
this._setUser(this.native.APP_USER[this.app._name]);
}
this.emitter.addListener(this.eventNameForApp('auth_state_changed'), event => {
this._setUser(event.user);
this.emitter.emit(this.eventNameForApp('onAuthStateChanged'), this._user);
});
this.emitter.addListener(this.eventNameForApp('phone_auth_state_changed'), event => {
const eventKey = `phone:auth:${event.requestKey}:${event.type}`;
this.emitter.emit(eventKey, event.state);
});
this.emitter.addListener(this.eventNameForApp('auth_id_token_changed'), auth => {
this._setUser(auth.user);
this.emitter.emit(this.eventNameForApp('onIdTokenChanged'), this._user);
});
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
if (!isOther) {
// Only supported on native platforms
this.native.configureAuthDomain();
}
}
get languageCode() {
return this._languageCode;
}
set languageCode(code) {
// For modular API, not recommended to set languageCode directly as it should be set in the native SDKs first
if (!isString(code) && !isNull(code)) {
throw new Error(
"firebase.auth().languageCode = (*) expected 'languageCode' to be a string or null value",
);
}
// as this is a setter, we can't use async/await. So we set it first so it is available immediately
if (code === null) {
this._languageCode = this.native.APP_LANGUAGE[this.app._name];
if (!this.languageCode) {
this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]'];
}
} else {
this._languageCode = code;
}
// This sets it natively
this.setLanguageCode(code);
}
get config() {
// for modular API, firebase JS SDK has a config object which is not available in native SDKs
return {};
}
get tenantId() {
return this._tenantId;
}
get settings() {
if (!this._settings) {
this._settings = new Settings(this);
}
return this._settings;
}
get currentUser() {
return this._user;
}
_setUser(user) {
this._user = user ? createDeprecationProxy(new User(this, user)) : null;
this._authResult = true;
this.emitter.emit(this.eventNameForApp('onUserChanged'), this._user);
return this._user;
}
_setUserCredential(userCredential) {
const user = createDeprecationProxy(new User(this, userCredential.user));
this._user = user;
this._authResult = true;
this.emitter.emit(this.eventNameForApp('onUserChanged'), this._user);
return {
additionalUserInfo: userCredential.additionalUserInfo,
user,
};
}
async setLanguageCode(code) {
if (!isString(code) && !isNull(code)) {
throw new Error(
"firebase.auth().setLanguageCode(*) expected 'languageCode' to be a string or null value",
);
}
await this.native.setLanguageCode(code);
if (code === null) {
this._languageCode = this.native.APP_LANGUAGE[this.app._name];
if (!this.languageCode) {
this._languageCode = this.native.APP_LANGUAGE['[DEFAULT]'];
}
} else {
this._languageCode = code;
}
}
async setTenantId(tenantId) {
if (!isString(tenantId)) {
throw new Error("firebase.auth().setTenantId(*) expected 'tenantId' to be a string");
}
this._tenantId = tenantId;
await this.native.setTenantId(tenantId);
}
onAuthStateChanged(listenerOrObserver) {
const listener = parseListenerOrObserver(listenerOrObserver);
const subscription = this.emitter.addListener(
this.eventNameForApp('onAuthStateChanged'),
listener,
);
if (this._authResult) {
Promise.resolve().then(() => {
listener(this._user || null);
});
}
return () => subscription.remove();
}
onIdTokenChanged(listenerOrObserver) {
const listener = parseListenerOrObserver(listenerOrObserver);
const subscription = this.emitter.addListener(
this.eventNameForApp('onIdTokenChanged'),
listener,
);
if (this._authResult) {
Promise.resolve().then(() => {
listener(this._user || null);
});
}
return () => subscription.remove();
}
onUserChanged(listenerOrObserver) {
const listener = parseListenerOrObserver(listenerOrObserver);
const subscription = this.emitter.addListener(this.eventNameForApp('onUserChanged'), listener);
if (this._authResult) {
Promise.resolve().then(() => {
listener(this._user || null);
});
}
return () => {
subscription.remove();
};
}
signOut() {
return this.native.signOut().then(() => {
this._setUser();
});
}
signInAnonymously() {
return this.native
.signInAnonymously()
.then(userCredential => this._setUserCredential(userCredential));
}
signInWithPhoneNumber(phoneNumber, forceResend) {
if (isAndroid) {
return this.native
.signInWithPhoneNumber(phoneNumber, forceResend || false)
.then(result => new ConfirmationResult(this, result.verificationId));
}
return this.native
.signInWithPhoneNumber(phoneNumber)
.then(result => new ConfirmationResult(this, result.verificationId));
}
verifyPhoneNumber(phoneNumber, autoVerifyTimeoutOrForceResend, forceResend) {
let _forceResend = forceResend;
let _autoVerifyTimeout = 60;
if (isBoolean(autoVerifyTimeoutOrForceResend)) {
_forceResend = autoVerifyTimeoutOrForceResend;
} else {
_autoVerifyTimeout = autoVerifyTimeoutOrForceResend;
}
return new PhoneAuthListener(this, phoneNumber, _autoVerifyTimeout, _forceResend);
}
verifyPhoneNumberWithMultiFactorInfo(multiFactorHint, session) {
return this.native.verifyPhoneNumberWithMultiFactorInfo(multiFactorHint.uid, session);
}
verifyPhoneNumberForMultiFactor(phoneInfoOptions) {
const { phoneNumber, session } = phoneInfoOptions;
return this.native.verifyPhoneNumberForMultiFactor(phoneNumber, session);
}
resolveMultiFactorSignIn(session, verificationId, verificationCode) {
return this.native
.resolveMultiFactorSignIn(session, verificationId, verificationCode)
.then(userCredential => {
return this._setUserCredential(userCredential);
});
}
createUserWithEmailAndPassword(email, password) {
return this.native
.createUserWithEmailAndPassword(email, password)
.then(userCredential => this._setUserCredential(userCredential));
}
signInWithEmailAndPassword(email, password) {
return this.native
.signInWithEmailAndPassword(email, password)
.then(userCredential => this._setUserCredential(userCredential));
}
signInWithCustomToken(customToken) {
return this.native
.signInWithCustomToken(customToken)
.then(userCredential => this._setUserCredential(userCredential));
}
signInWithCredential(credential) {
return this.native
.signInWithCredential(credential.providerId, credential.token, credential.secret)
.then(userCredential => this._setUserCredential(userCredential));
}
revokeToken(authorizationCode) {
return this.native.revokeToken(authorizationCode);
}
sendPasswordResetEmail(email, actionCodeSettings = null) {
return this.native.sendPasswordResetEmail(email, actionCodeSettings);
}
sendSignInLinkToEmail(email, actionCodeSettings = {}) {
return this.native.sendSignInLinkToEmail(email, actionCodeSettings);
}
isSignInWithEmailLink(emailLink) {
return this.native.isSignInWithEmailLink(emailLink);
}
signInWithEmailLink(email, emailLink) {
return this.native
.signInWithEmailLink(email, emailLink)
.then(userCredential => this._setUserCredential(userCredential));
}
confirmPasswordReset(code, newPassword) {
return this.native.confirmPasswordReset(code, newPassword);
}
applyActionCode(code) {
return this.native.applyActionCode(code).then(user => {
this._setUser(user);
});
}
checkActionCode(code) {
return this.native.checkActionCode(code);
}
fetchSignInMethodsForEmail(email) {
return this.native.fetchSignInMethodsForEmail(email);
}
verifyPasswordResetCode(code) {
return this.native.verifyPasswordResetCode(code);
}
useUserAccessGroup(userAccessGroup) {
if (isAndroid) {
return Promise.resolve();
}
return this.native.useUserAccessGroup(userAccessGroup);
}
getRedirectResult() {
throw new Error(
'firebase.auth().getRedirectResult() is unsupported by the native Firebase SDKs.',
);
}
setPersistence() {
throw new Error('firebase.auth().setPersistence() is unsupported by the native Firebase SDKs.');
}
signInWithPopup(provider) {
return this.native
.signInWithProvider(provider.toObject())
.then(userCredential => this._setUserCredential(userCredential));
}
signInWithRedirect(provider) {
return this.native
.signInWithProvider(provider.toObject())
.then(userCredential => this._setUserCredential(userCredential));
}
// firebase issue - https://github.com/invertase/react-native-firebase/pull/655#issuecomment-349904680
useDeviceLanguage() {
throw new Error(
'firebase.auth().useDeviceLanguage() is unsupported by the native Firebase SDKs.',
);
}
useEmulator(url) {
if (!url || !isString(url) || !isValidUrl(url)) {
throw new Error('firebase.auth().useEmulator() takes a non-empty string URL');
}
let _url = url;
const androidBypassEmulatorUrlRemap =
typeof this.firebaseJson.android_bypass_emulator_url_remap === 'boolean' &&
this.firebaseJson.android_bypass_emulator_url_remap;
if (!androidBypassEmulatorUrlRemap && isAndroid && _url) {
if (_url.startsWith('http://localhost')) {
_url = _url.replace('http://localhost', 'http://10.0.2.2');
// eslint-disable-next-line no-console
console.log(
'Mapping auth host "localhost" to "10.0.2.2" for android emulators. Use real IP on real devices. You can bypass this behaviour with "android_bypass_emulator_url_remap" flag.',
);
}
if (_url.startsWith('http://127.0.0.1')) {
_url = _url.replace('http://127.0.0.1', 'http://10.0.2.2');
// eslint-disable-next-line no-console
console.log(
'Mapping auth host "127.0.0.1" to "10.0.2.2" for android emulators. Use real IP on real devices. You can bypass this behaviour with "android_bypass_emulator_url_remap" flag.',
);
}
}
// Native calls take the host and port split out
const hostPortRegex = /^http:\/\/([\w\d-.]+):(\d+)$/;
const urlMatches = _url.match(hostPortRegex);
if (!urlMatches) {
throw new Error('firebase.auth().useEmulator() unable to parse host and port from URL');
}
const host = urlMatches[1];
const port = parseInt(urlMatches[2], 10);
this.native.useEmulator(host, port);
return [host, port]; // undocumented return, useful for unit testing
}
getMultiFactorResolver(error) {
return getMultiFactorResolver(this, error);
}
multiFactor(user) {
if (user.userId !== this.currentUser.userId) {
throw new Error('firebase.auth().multiFactor() only operates on currentUser');
}
return new MultiFactorUser(this, user);
}
getCustomAuthDomain() {
return this.native.getCustomAuthDomain();
}
}
// import { SDK_VERSION } from '@react-native-firebase/auth';
export const SDK_VERSION = version;
// import auth from '@react-native-firebase/auth';
// auth().X(...);
export default createModuleNamespace({
statics,
version,
namespace,
nativeModuleName,
nativeEvents: ['auth_state_changed', 'auth_id_token_changed', 'phone_auth_state_changed'],
hasMultiAppSupport: true,
hasCustomUrlOrRegionSupport: false,
ModuleClass: FirebaseAuthModule,
});
export * from './modular/index';
// import auth, { firebase } from '@react-native-firebase/auth';
// auth().X(...);
// firebase.auth().X(...);
export const firebase = getFirebaseRoot();
// Register the interop module for non-native platforms.
setReactNativeModule(nativeModuleName, fallBackModule);