diff --git a/packages/core/src/auth.ts b/packages/core/src/auth.ts index bc036743..fbef599a 100644 --- a/packages/core/src/auth.ts +++ b/packages/core/src/auth.ts @@ -21,6 +21,7 @@ import { sendSignInLinkToEmail as _sendSignInLinkToEmail, signInAnonymously as _signInAnonymously, signInWithPhoneNumber as _signInWithPhoneNumber, + signInWithCredential as _signInWithCredential, ActionCodeSettings, ApplicationVerifier, AuthProvider, @@ -28,8 +29,6 @@ import { EmailAuthProvider, linkWithCredential, PhoneAuthProvider, - signInWithCredential, - signInWithRedirect, UserCredential, } from "firebase/auth"; import { getBehavior, hasBehavior } from "./behaviors"; @@ -70,7 +69,7 @@ export async function signInWithEmailAndPassword( } ui.setState("pending"); - const result = await signInWithCredential(ui.auth, credential); + const result = await _signInWithCredential(ui.auth, credential); return handlePendingCredential(ui, result); } catch (error) { handleFirebaseError(ui, error); @@ -138,7 +137,7 @@ export async function confirmPhoneNumber( } ui.setState("pending"); - const result = await signInWithCredential(ui.auth, credential); + const result = await _signInWithCredential(ui.auth, credential); return handlePendingCredential(ui, result); } catch (error) { handleFirebaseError(ui, error); @@ -193,7 +192,7 @@ export async function signInWithEmailLink( } ui.setState("pending"); - const result = await signInWithCredential(ui.auth, credential); + const result = await _signInWithCredential(ui.auth, credential); return handlePendingCredential(ui, result); } catch (error) { handleFirebaseError(ui, error); @@ -214,20 +213,21 @@ export async function signInAnonymously(ui: FirebaseUIConfiguration): Promise { +export async function signInWithProvider(ui: FirebaseUIConfiguration, provider: AuthProvider): Promise { try { if (hasBehavior(ui, "autoUpgradeAnonymousProvider")) { - await getBehavior(ui, "autoUpgradeAnonymousProvider")(ui, provider); - // If we get to here, the user is not anonymous, otherwise they - // have been redirected to the provider's sign in page. + const result = await getBehavior(ui, "autoUpgradeAnonymousProvider")(ui, provider); + + // If we hit this point, the user is either not anonymous (undefined), or they have been linked + // via a popup flow (UserCredential). If they have been redirected, they will never get to here. + if (result) { + return result; + } + // If they hit this point, they are not anonymous and we need to sign them in. } - ui.setState("pending"); - - // TODO(ehesp): Handle popup or redirect based on behavior - await signInWithRedirect(ui.auth, provider); - // We don't modify state here since the user is redirected. - // If we support popups, we'd need to modify state here. + const strategy = getBehavior(ui, "providerSignInStrategy"); + return await strategy(ui, provider); } catch (error) { handleFirebaseError(ui, error); } finally { diff --git a/packages/core/src/behaviors.ts b/packages/core/src/behaviors.ts index 43d0b8af..1fb0631e 100644 --- a/packages/core/src/behaviors.ts +++ b/packages/core/src/behaviors.ts @@ -23,6 +23,9 @@ import { User, UserCredential, RecaptchaVerifier, + signInWithRedirect, + signInWithPopup, + linkWithPopup, } from "firebase/auth"; import { FirebaseUIConfiguration } from "./config"; @@ -32,8 +35,10 @@ export type BehaviorHandlers = { ui: FirebaseUIConfiguration, credential: AuthCredential ) => Promise; - autoUpgradeAnonymousProvider: (ui: FirebaseUIConfiguration, provider: AuthProvider) => Promise; + autoUpgradeAnonymousProvider: (ui: FirebaseUIConfiguration, provider: AuthProvider) => Promise; recaptchaVerification: (ui: FirebaseUIConfiguration, element: HTMLElement) => RecaptchaVerifier; + providerSignInStrategy: (ui: FirebaseUIConfiguration, provider: AuthProvider) => Promise; + providerLinkStrategy: (ui: FirebaseUIConfiguration, user: User, provider: AuthProvider) => Promise; }; export type Behavior = Pick; @@ -107,9 +112,8 @@ export function autoUpgradeAnonymousUsers(): Behavior< } ui.setState("pending"); - await linkWithRedirect(currentUser, provider); - // We don't modify state here since the user is redirected. - // If we support popups, we'd need to modify state here. + const strategy = getBehavior(ui, "providerLinkStrategy"); + return await strategy(ui, currentUser, provider); }, }; } @@ -132,6 +136,33 @@ export function recaptchaVerification(options?: RecaptchaVerification): Behavior }; } +export function providerRedirectStrategy(): Behavior<"providerSignInStrategy" | "providerLinkStrategy"> { + return { + providerSignInStrategy: async (ui, provider) => { + ui.setState("pending"); + return signInWithRedirect(ui.auth, provider); + }, + providerLinkStrategy: async (ui, user, provider) => { + ui.setState("pending"); + return linkWithRedirect(user, provider); + }, + }; +} + +export function providerPopupStrategy(): Behavior<"providerSignInStrategy" | "providerLinkStrategy"> { + return { + providerSignInStrategy: async (ui, provider) => { + ui.setState("pending"); + return signInWithPopup(ui.auth, provider); + }, + providerLinkStrategy: async (ui, user, provider) => { + ui.setState("pending"); + return linkWithPopup(user, provider); + }, + }; +} + export const defaultBehaviors = { + ...providerRedirectStrategy(), ...recaptchaVerification(), }; \ No newline at end of file