Skip to content
Closed
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
30 changes: 15 additions & 15 deletions packages/core/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@ import {
sendSignInLinkToEmail as _sendSignInLinkToEmail,
signInAnonymously as _signInAnonymously,
signInWithPhoneNumber as _signInWithPhoneNumber,
signInWithCredential as _signInWithCredential,
ActionCodeSettings,
ApplicationVerifier,
AuthProvider,
ConfirmationResult,
EmailAuthProvider,
linkWithCredential,
PhoneAuthProvider,
signInWithCredential,
signInWithRedirect,
UserCredential,
} from "firebase/auth";
import { getBehavior, hasBehavior } from "./behaviors";
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -214,20 +213,21 @@ export async function signInAnonymously(ui: FirebaseUIConfiguration): Promise<Us
}
}

export async function signInWithProvider(ui: FirebaseUIConfiguration, provider: AuthProvider): Promise<void> {
export async function signInWithProvider(ui: FirebaseUIConfiguration, provider: AuthProvider): Promise<never | UserCredential> {
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 {
Expand Down
39 changes: 35 additions & 4 deletions packages/core/src/behaviors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import {
User,
UserCredential,
RecaptchaVerifier,
signInWithRedirect,
signInWithPopup,
linkWithPopup,
} from "firebase/auth";
import { FirebaseUIConfiguration } from "./config";

Expand All @@ -32,8 +35,10 @@ export type BehaviorHandlers = {
ui: FirebaseUIConfiguration,
credential: AuthCredential
) => Promise<UserCredential | undefined>;
autoUpgradeAnonymousProvider: (ui: FirebaseUIConfiguration, provider: AuthProvider) => Promise<undefined | never>;
autoUpgradeAnonymousProvider: (ui: FirebaseUIConfiguration, provider: AuthProvider) => Promise<never | undefined | UserCredential>;
recaptchaVerification: (ui: FirebaseUIConfiguration, element: HTMLElement) => RecaptchaVerifier;
providerSignInStrategy: (ui: FirebaseUIConfiguration, provider: AuthProvider) => Promise<never | UserCredential>;
providerLinkStrategy: (ui: FirebaseUIConfiguration, user: User, provider: AuthProvider) => Promise<never | UserCredential>;
};

export type Behavior<T extends keyof BehaviorHandlers = keyof BehaviorHandlers> = Pick<BehaviorHandlers, T>;
Expand Down Expand Up @@ -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);
},
};
}
Expand All @@ -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(),
};
Loading