@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.
1,205 lines (1,076 loc) • 37.6 kB
JavaScript
import {
getApp,
initializeAuth,
getReactNativePersistence,
onAuthStateChanged,
onIdTokenChanged,
signInAnonymously,
sendSignInLinkToEmail,
getAdditionalUserInfo,
multiFactor,
getMultiFactorResolver,
TotpMultiFactorGenerator,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
isSignInWithEmailLink,
signInWithEmailLink,
signInWithCustomToken,
sendPasswordResetEmail,
useDeviceLanguage,
verifyPasswordResetCode,
connectAuthEmulator,
fetchSignInMethodsForEmail,
sendEmailVerification,
verifyBeforeUpdateEmail,
confirmPasswordReset,
updateEmail,
updatePassword,
updateProfile,
updatePhoneNumber,
signInWithCredential,
unlink,
linkWithCredential,
reauthenticateWithCredential,
getIdToken,
getIdTokenResult,
applyActionCode,
checkActionCode,
EmailAuthProvider,
FacebookAuthProvider,
GoogleAuthProvider,
TwitterAuthProvider,
GithubAuthProvider,
PhoneAuthProvider,
OAuthProvider,
} from '@react-native-firebase/app/lib/internal/web/firebaseAuth';
import { guard, getWebError, emitEvent } from '@react-native-firebase/app/lib/internal/web/utils';
import {
getReactNativeAsyncStorageInternal,
isMemoryStorage,
} from '@react-native-firebase/app/lib/internal/asyncStorage';
/**
* Resolves or rejects an auth method promise without a user (user was missing).
* @param {boolean} isError whether to reject the promise.
* @returns {Promise<void>} - Void promise.
*/
function promiseNoUser(isError = false) {
if (isError) {
return rejectPromiseWithCodeAndMessage('no-current-user', 'No user currently signed in.');
}
// TODO(ehesp): Should this be null, or undefined?
return Promise.resolve(null);
}
/**
* Returns a structured error object.
* @param {string} code - The error code.
* @param {string} message - The error message.
*/
function rejectPromiseWithCodeAndMessage(code, message) {
return rejectPromise(getWebError({ code: `auth/${code}`, message }));
}
function rejectWithCodeAndMessage(code, message) {
return Promise.reject(
getWebError({
code,
message,
}),
);
}
/**
* Returns a structured error object.
* @param {error} error The error object.
* @returns {never}
*/
function rejectPromise(error) {
const { code, message, details } = error;
const nativeError = {
code,
message,
userInfo: {
code: code ? code.replace('auth/', '') : 'unknown',
message,
details,
},
};
return Promise.reject(nativeError);
}
/**
* Converts a user object to a plain object.
* @param {User} user - The User object to convert.
* @returns {object}
*/
function userToObject(user) {
return {
...userInfoToObject(user),
emailVerified: user.emailVerified,
isAnonymous: user.isAnonymous,
tenantId: user.tenantId !== null && user.tenantId !== '' ? user.tenantId : null,
providerData: user.providerData.map(userInfoToObject),
metadata: userMetadataToObject(user.metadata),
multiFactor: {
enrolledFactors: multiFactor(user).enrolledFactors.map(multiFactorInfoToObject),
},
};
}
/**
* Returns an AuthCredential object for the given provider.
* @param {Auth} auth - The Auth instance to use.
* @param {string} provider - The provider to get the credential for.
* @param {string} token - The token to use for the credential.
* @param {string|null} secret - The secret to use for the credential.
* @returns {AuthCredential|null} - The AuthCredential object.
*/
function getAuthCredential(_auth, provider, token, secret) {
if (provider.startsWith('oidc.')) {
return new OAuthProvider(provider).credential({
idToken: token,
});
}
switch (provider) {
case 'facebook.com':
return FacebookAuthProvider().credential(token);
case 'google.com':
return GoogleAuthProvider().credential(token, secret);
case 'twitter.com':
return TwitterAuthProvider().credential(token, secret);
case 'github.com':
return GithubAuthProvider().credential(token);
case 'apple.com':
return new OAuthProvider(provider).credential({
idToken: token,
rawNonce: secret,
});
case 'oauth':
return OAuthProvider(provider).credential({
idToken: token,
accessToken: secret,
});
case 'phone':
return PhoneAuthProvider.credential(token, secret);
case 'password':
return EmailAuthProvider.credential(token, secret);
case 'emailLink':
return EmailAuthProvider.credentialWithLink(token, secret);
default:
return null;
}
}
/**
* Converts a user info object to a plain object.
* @param {UserInfo} userInfo - The UserInfo object to convert.
*/
function userInfoToObject(userInfo) {
return {
providerId: userInfo.providerId,
uid: userInfo.uid,
displayName:
userInfo.displayName !== null && userInfo.displayName !== '' ? userInfo.displayName : null,
email: userInfo.email !== null && userInfo.email !== '' ? userInfo.email : null,
photoURL: userInfo.photoURL !== null && userInfo.photoURL !== '' ? userInfo.photoURL : null,
phoneNumber:
userInfo.phoneNumber !== null && userInfo.phoneNumber !== '' ? userInfo.phoneNumber : null,
};
}
/**
* Converts a user metadata object to a plain object.
* @param {UserMetadata} metadata - The UserMetadata object to convert.
*/
function userMetadataToObject(metadata) {
return {
creationTime: metadata.creationTime ? new Date(metadata.creationTime).toISOString() : null,
lastSignInTime: metadata.lastSignInTime
? new Date(metadata.lastSignInTime).toISOString()
: null,
};
}
/**
* Converts a MultiFactorInfo object to a plain object.
* @param {MultiFactorInfo} multiFactorInfo - The MultiFactorInfo object to convert.
*/
function multiFactorInfoToObject(multiFactorInfo) {
const obj = {
displayName: multiFactorInfo.displayName,
enrollmentTime: multiFactorInfo.enrollmentTime,
factorId: multiFactorInfo.factorId,
uid: multiFactorInfo.uid,
};
// If https://firebase.google.com/docs/reference/js/auth.phonemultifactorinfo
if ('phoneNumber' in multiFactorInfo) {
obj.phoneNumber = multiFactorInfo.phoneNumber;
}
return obj;
}
/**
* Converts a user credential object to a plain object.
* @param {UserCredential} userCredential - The user credential object to convert.
*/
function authResultToObject(userCredential) {
const additional = getAdditionalUserInfo(userCredential);
return {
user: userToObject(userCredential.user),
additionalUserInfo: {
isNewUser: additional.isNewUser,
profile: additional.profile,
providerId: additional.providerId,
username: additional.username,
},
};
}
const instances = {};
const authStateListeners = {};
const idTokenListeners = {};
const sessionMap = new Map();
const totpSecretMap = new Map();
let sessionId = 0;
// Returns a cached Firestore instance.
function getCachedAuthInstance(appName) {
if (!instances[appName]) {
if (isMemoryStorage()) {
// Warn auth persistence is is disabled unless Async Storage implementation is provided.
// eslint-disable-next-line no-console
console.warn(
'Firebase Auth persistence is disabled. To enable persistence, provide an Async Storage implementation.\n' +
'\n' +
'For example, to use React Native Async Storage:\n' +
'\n' +
" import AsyncStorage from '@react-native-async-storage/async-storage';\n" +
'\n' +
' // Before initializing Firebase set the Async Storage implementation\n' +
' // that will be used to persist user sessions.\n' +
' firebase.setReactNativeAsyncStorage(AsyncStorage);\n' +
'\n' +
' // Then initialize Firebase as normal.\n' +
' await firebase.initializeApp({ ... });\n',
);
}
instances[appName] = initializeAuth(getApp(appName), {
persistence: getReactNativePersistence(getReactNativeAsyncStorageInternal()),
});
}
return instances[appName];
}
// getConstants
const CONSTANTS = {
APP_LANGUAGE: {},
APP_USER: {},
};
// Not required for web, since it's dynamic initialization
// and we are not making instances of auth based on apps that already exist
// since there are none that exist before we initialize them in our code below.
// for (const appName of getApps()) {
// const instance = getAuth(getApp(appName));
// CONSTANTS.APP_LANGUAGE[appName] = instance.languageCode;
// if (instance.currentUser) {
// CONSTANTS.APP_USER[appName] = userToObject(instance.currentUser);
// }
// }
/**
* This is a 'NativeModule' for the web platform.
* Methods here are identical to the ones found in
* the native android/ios modules e.g. `@ReactMethod` annotated
* java methods on Android.
*/
export default {
// Expose all the constants.
...CONSTANTS,
async useUserAccessGroup() {
// noop
},
configureAuthDomain() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
async getCustomAuthDomain() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
/**
* Create a new auth state listener instance for a given app.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns {Promise<void>} - Void promise.
*/
addAuthStateListener(appName) {
if (authStateListeners[appName]) {
return;
}
return guard(async () => {
const auth = getCachedAuthInstance(appName);
authStateListeners[appName] = onAuthStateChanged(auth, user => {
emitEvent('auth_state_changed', {
appName,
user: user ? userToObject(user) : null,
});
});
});
},
/**
* Remove an auth state listener instance for a given app.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns {Promise<void>} - Void promise.
*/
removeAuthStateListener(appName) {
if (authStateListeners[appName]) {
authStateListeners[appName]();
delete authStateListeners[appName];
}
},
/**
* Create a new ID token listener instance for a given app.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns {Promise<void>} - Void promise.
*/
addIdTokenListener(appName) {
if (idTokenListeners[appName]) {
return;
}
return guard(async () => {
const auth = getCachedAuthInstance(appName);
idTokenListeners[appName] = onIdTokenChanged(auth, user => {
emitEvent('auth_id_token_changed', {
authenticated: !!user,
appName,
user: user ? userToObject(user) : null,
});
});
});
},
/**
* Remove an ID token listener instance for a given app.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns {Promise<void>} - Void promise.
*/
removeIdTokenListener(appName) {
if (idTokenListeners[appName]) {
idTokenListeners[appName]();
delete idTokenListeners[appName];
}
},
async forceRecaptchaFlowForTesting() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
async setAutoRetrievedSmsCodeForPhoneNumber() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
async setAppVerificationDisabledForTesting() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
/**
* Sign out the current user.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns {Promise<void>} - Void promise.
*/
signOut(appName) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await auth.signOut();
return promiseNoUser();
});
},
/**
* Sign in anonymously.
* @param {*} appName - The name of the app to get the auth instance for.
* @returns
*/
signInAnonymously(appName) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const credential = await signInAnonymously(auth);
return authResultToObject(credential);
});
},
/**
* Sign in with email and password.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} email - The email to sign in with.
* @param {string} password - The password to sign in with.
* @returns {Promise<object>} - The result of the sign in.
*/
async createUserWithEmailAndPassword(appName, email, password) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const credential = await createUserWithEmailAndPassword(auth, email, password);
return authResultToObject(credential);
});
},
/**
* Sign in with email and password.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} email - The email to sign in with.
* @param {string} password - The password to sign in with.
* @returns {Promise<object>} - The result of the sign in.
*/
async signInWithEmailAndPassword(appName, email, password) {
// The default guard / getWebError process doesn't work well here,
// since it creates a new error object that is then passed through
// a native module proxy and gets processed again.
// We need lots of information from the error so that MFA will work
// later if needed. So we handle the error custom here.
// return guard(async () => {
try {
const credential = await signInWithEmailAndPassword(
getCachedAuthInstance(appName),
email,
password,
);
return authResultToObject(credential);
} catch (e) {
e.userInfo = {
code: e.code.split('/')[1],
message: e.message,
customData: e.customData,
};
throw e;
}
// });
},
/**
* Check if a sign in with email link is valid
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} emailLink - The email link to sign in with.
* @returns {Promise<boolean>} - Whether the link is a valid sign in with email link.
*/
async isSignInWithEmailLink(appName, emailLink) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
return await isSignInWithEmailLink(auth, emailLink);
});
},
/**
* Sign in with email link.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} email - The email to sign in with.
* @param {string} emailLink - The email link to sign in with.
* @returns {Promise<object>} - The result of the sign in.
*/
async signInWithEmailLink(appName, email, emailLink) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const credential = await signInWithEmailLink(auth, email, emailLink);
return authResultToObject(credential);
});
},
/**
* Sign in with a custom token.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} token - The token to sign in with.
* @returns {Promise<object>} - The result of the sign in.
*/
async signInWithCustomToken(appName, token) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const credential = await signInWithCustomToken(auth, token);
return authResultToObject(credential);
});
},
/**
* Not implemented on web.
*/
async revokeToken() {
return promiseNoUser();
},
/**
* Send a password reset email.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} email - The email to send the password reset email to.
* @param {ActionCodeSettings} settings - The settings to use for the password reset email.
* @returns {Promise<null>}
*/
async sendPasswordResetEmail(appName, email, settings) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
await sendPasswordResetEmail(auth, email, settings);
return promiseNoUser();
});
},
/**
* Send a sign in link to an email.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} email - The email to send the password reset email to.
* @param {ActionCodeSettings} settings - The settings to use for the password reset email.
* @returns {Promise<null>}
*/
async sendSignInLinkToEmail(appName, email, settings) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
await sendSignInLinkToEmail(auth, email, settings);
return promiseNoUser();
});
},
/* ----------------------
* .currentUser methods
* ---------------------- */
/**
* Delete the current user.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns {Promise<null>}
*/
async delete(appName) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await auth.currentUser.delete();
return promiseNoUser();
});
},
/**
* Reload the current user.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns {Promise<object>} - The current user object.
*/
async reload(appName) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await auth.currentUser.reload();
return userToObject(auth.currentUser);
});
},
/**
* Send a verification email to the current user.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {ActionCodeSettings} actionCodeSettings - The settings to use for the email verification.
* @returns {Promise<object>} - The current user object.
*/
async sendEmailVerification(appName, actionCodeSettings) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await sendEmailVerification(auth.currentUser, actionCodeSettings);
return userToObject(auth.currentUser);
});
},
/**
* Verify the email before updating it.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} email - The email to verify.
* @param {ActionCodeSettings} actionCodeSettings - The settings to use for the email verification.
* @returns {Promise<object>} - The current user object.
*/
async verifyBeforeUpdateEmail(appName, email, actionCodeSettings) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await verifyBeforeUpdateEmail(auth.currentUser, email, actionCodeSettings);
return userToObject(auth.currentUser);
});
},
/**
* Update the current user's email.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} email - The email to update.
* @returns {Promise<object>} - The current user object.
*/
async updateEmail(appName, email) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await updateEmail(auth.currentUser, email);
return userToObject(auth.currentUser);
});
},
/**
* Update the current user's password.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} password - The password to update.
* @returns {Promise<object>} - The current user object.
*/
async updatePassword(appName, password) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await updatePassword(auth.currentUser, password);
return userToObject(auth.currentUser);
});
},
/**
* Update the current user's phone number.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} provider - The provider to update the phone number with.
* @param {string} authToken - The auth token to update the phone number with.
* @param {string} authSecret - The auth secret to update the phone number with.
* @returns {Promise<object>} - The current user object.
*/
async updatePhoneNumber(appName, provider, authToken, authSecret) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
if (provider !== 'phone') {
return rejectPromiseWithCodeAndMessage(
'invalid-credential',
'The supplied auth credential does not have a phone provider.',
);
}
const credential = getAuthCredential(auth, provider, authToken, authSecret);
if (!credential) {
return rejectPromiseWithCodeAndMessage(
'invalid-credential',
'The supplied auth credential is malformed, has expired or is not currently supported.',
);
}
await updatePhoneNumber(auth.currentUser, credential);
return userToObject(auth.currentUser);
});
},
/**
* Update the current user's profile.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {object} props - The properties to update.
* @returns {Promise<object>} - The current user object.
*/
async updateProfile(appName, props) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await updateProfile(auth.currentUser, {
displayName: props.displayName,
photoURL: props.photoURL,
});
return userToObject(auth.currentUser);
});
},
/**
* Sign in with a credential.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} provider - The provider to sign in with.
* @param {string} authToken - The auth token to sign in with.
* @param {string} authSecret - The auth secret to sign in with.
* @returns {Promise<object>} - The result of the sign in.
*/
async signInWithCredential(appName, provider, authToken, authSecret) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const credential = getAuthCredential(auth, provider, authToken, authSecret);
if (credential === null) {
return rejectPromiseWithCodeAndMessage(
'invalid-credential',
'The supplied auth credential is malformed, has expired or is not currently supported.',
);
}
const credentialResult = await signInWithCredential(auth, credential);
return authResultToObject(credentialResult);
});
},
async signInWithProvider() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
async signInWithPhoneNumber() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
/**
* Get a multi-factor session.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns {Promise<string>} - The session ID.
*/
async getSession(appName) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
const session = await multiFactor(auth.currentUser).getSession();
// Increment the session ID.
sessionId++;
const key = `${sessionId}`;
sessionMap.set(key, session);
return key;
});
},
verifyPhoneNumberForMultiFactor() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
finalizeMultiFactorEnrollment() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
resolveMultiFactorSignIn() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
confirmationResultConfirm() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
verifyPhoneNumber() {
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
/**
* Confirm the password reset code.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} code - The code to confirm.
* @param {string} newPassword - The new password to set.
* @returns {Promise<null>}
*/
async confirmPasswordReset(appName, code, newPassword) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
await confirmPasswordReset(auth, code, newPassword);
return promiseNoUser();
});
},
/**
* Apply an action code.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} code - The code to apply.
* @returns {Promise<void>} - Void promise.
*/
async applyActionCode(appName, code) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
await applyActionCode(auth, code);
});
},
/**
* Check an action code.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} code - The code to check.
* @returns {Promise<object>} - The result of the check.
*/
async checkActionCode(appName, code) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const result = await checkActionCode(auth, code);
return {
operation: result.operation,
data: {
email: result.data.email,
fromEmail: result.data.previousEmail,
// multiFactorInfo - not implemented
},
};
});
},
/**
* Link a credential to the current user.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} provider - The provider to link.
* @param {string} authToken - The auth token to link.
* @param {string} authSecret - The auth secret to link.
* @returns {Promise<object>} - The current user object.
*/
async linkWithCredential(appName, provider, authToken, authSecret) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const credential = getAuthCredential(auth, provider, authToken, authSecret);
if (credential === null) {
return rejectPromiseWithCodeAndMessage(
'invalid-credential',
'The supplied auth credential is malformed, has expired or is not currently supported.',
);
}
if (auth.currentUser === null) {
return promiseNoUser(true);
}
return authResultToObject(await linkWithCredential(auth.currentUser, credential));
});
},
async linkWithProvider() {
// TODO: We could check if window is available here, but for now it's not supported.
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
/**
* Unlink a provider from the current user.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} providerId - The provider ID to unlink.
* @returns {Promise<object>} - The current user object.
*/
async unlink(appName, providerId) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
const user = await unlink(auth.currentUser, providerId);
return userToObject(user);
});
},
/**
* Reauthenticate with a credential.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} provider - The provider to reauthenticate with.
* @param {string} authToken - The auth token to reauthenticate with.
* @param {string} authSecret - The auth secret to reauthenticate with.
* @returns {Promise<object>} - The current user object.
*/
async reauthenticateWithCredential(appName, provider, authToken, authSecret) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const credential = getAuthCredential(auth, provider, authToken, authSecret);
if (credential === null) {
return rejectPromiseWithCodeAndMessage(
'invalid-credential',
'The supplied auth credential is malformed, has expired or is not currently supported.',
);
}
if (auth.currentUser === null) {
return promiseNoUser(true);
}
return authResultToObject(await reauthenticateWithCredential(auth.currentUser, credential));
});
},
async reauthenticateWithProvider() {
// TODO: We could check if window is available here, but for now it's not supported.
return rejectPromiseWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
/**
* Get the ID token for the current user.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {boolean} forceRefresh - Whether to force a token refresh.
* @returns {Promise<string>} - The ID token.
*/
async getIdToken(appName, forceRefresh) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
const token = await getIdToken(auth.currentUser, forceRefresh);
return token;
});
},
/**
* Get the ID token result for the current user.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {boolean} forceRefresh - Whether to force a token refresh.
* @returns {Promise<object>} - The ID token result.
*/
async getIdTokenResult(appName, forceRefresh) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
const result = await getIdTokenResult(auth.currentUser, forceRefresh);
// TODO(ehesp): Result looks expected, might be safer to keep fixed object?
return {
authTime: result.authTime,
expirationTime: result.expirationTime,
issuedAtTime: result.issuedAtTime,
claims: result.claims,
signInProvider: result.signInProvider,
token: result.token,
};
});
},
/**
* Get a MultiFactorResolver from the underlying SDK
* @param {*} _appName the name of the app to get the auth instance for
* @param {*} uid the uid of the TOTP MFA attempt
* @param {*} code the code from the user TOTP app
* @return TotpMultiFactorAssertion to use for resolving
*/
assertionForSignIn(_appName, uid, code) {
return TotpMultiFactorGenerator.assertionForSignIn(uid, code);
},
/**
* Get a MultiFactorResolver from the underlying SDK
* @param {*} appName the name of the app to get the auth instance for
* @param {*} error the MFA error returned from initial factor login attempt
* @return MultiFactorResolver to use for verifying the second factor
*/
getMultiFactorResolver(appName, error) {
return getMultiFactorResolver(getCachedAuthInstance(appName), error);
},
/**
* generate a TOTP secret
* @param {*} _appName - The name of the app to get the auth instance for.
* @param {*} session - The MultiFactorSession to associate with the secret
* @returns object with secretKey to associate with TotpSecret
*/
async generateTotpSecret(_appName, session) {
return guard(async () => {
const totpSecret = await TotpMultiFactorGenerator.generateSecret(sessionMap.get(session));
totpSecretMap.set(totpSecret.secretKey, totpSecret);
return { secretKey: totpSecret.secretKey };
});
},
/**
* unenroll from TOTP
* @param {*} appName - The name of the app to get the auth instance for.
* @param {*} enrollmentId - The ID to associate with the enrollment
* @returns
*/
async unenrollMultiFactor(appName, enrollmentId) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
await multiFactor(auth.currentUser).unenroll(enrollmentId);
});
},
/**
* finalize a TOTP enrollment
* @param {*} appName - The name of the app to get the auth instance for.
* @param {*} secretKey - The secretKey to associate native TotpSecret
* @param {*} verificationCode - The TOTP to verify
* @param {*} displayName - The name to associate as a hint
* @returns
*/
async finalizeTotpEnrollment(appName, secretKey, verificationCode, displayName) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
if (auth.currentUser === null) {
return promiseNoUser(true);
}
const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment(
totpSecretMap.get(secretKey),
verificationCode,
);
await multiFactor(auth.currentUser).enroll(multiFactorAssertion, displayName);
});
},
/**
* generate a TOTP QR Code URL
* @param {*} _appName - The name of the app to get the auth instance for.
* @param {*} secretKey - The secretKey to associate with the TotpSecret
* @param {*} accountName - The account name to use in auth app
* @param {*} issuer - The issuer to use in auth app
* @returns QR Code URL
*/
generateQrCodeUrl(_appName, secretKey, accountName, issuer) {
return totpSecretMap.get(secretKey).generateQrCodeUrl(accountName, issuer);
},
/**
* open a QR Code URL in an app directly
* @param {*} appName - The name of the app to get the auth instance for.
* @param {*} qrCodeUrl the URL to open in the app, from generateQrCodeUrl
* @throws Error not supported in this environment
*/
openInOtpApp() {
return rejectWithCodeAndMessage(
'unsupported',
'This operation is not supported in this environment.',
);
},
/* ----------------------
* other methods
* ---------------------- */
/**
* Fetch the sign in methods for an email.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} email - The email to fetch the sign in methods for.
* @returns {Promise<string[]>} - The sign in methods for the email.
*/
async fetchSignInMethodsForEmail(appName, email) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const methods = await fetchSignInMethodsForEmail(auth, email);
return methods;
});
},
/**
* Set the language code.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} code - The language code to set.
* @returns {void}
*/
setLanguageCode(appName, code) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
auth.languageCode = code;
});
},
/**
* Set the tenant ID.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} tenantId - The tenant ID to set.
* @returns {void}
*/
setTenantId(appName, tenantId) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
auth.tenantId = tenantId;
});
},
/**
* Use the device language.
* @param {string} appName - The name of the app to get the auth instance for.
* @returns void
*/
useDeviceLanguage(appName) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
useDeviceLanguage(auth);
});
},
/**
* Verify the provided password reset code.
* @returns {string} - The users email address if valid.
*/
verifyPasswordResetCode(appName, code) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
const email = await verifyPasswordResetCode(auth, code);
return email;
});
},
/**
* Connect to the auth emulator.
* @param {string} appName - The name of the app to get the auth instance for.
* @param {string} host - The host to use for the auth emulator.
* @param {number} port - The port to use for the auth emulator.
* @returns {void}
*/
useEmulator(appName, host, port) {
return guard(async () => {
const auth = getCachedAuthInstance(appName);
connectAuthEmulator(auth, `http://${host}:${port}`);
});
},
};