From 3c5b91bf7222342cf8f80567a113c4192eca04c8 Mon Sep 17 00:00:00 2001 From: nhienlam Date: Wed, 10 Apr 2024 17:46:21 -0700 Subject: [PATCH 1/4] Add v8 and v9 snippets for Handling the account-exists-with-different-credential error --- auth-next/link-multiple-accounts.js | 56 ++++++++++++++++++ auth/link-multiple-accounts.js | 56 ++++++++++++++++++ .../account_exists_popup.js | 59 +++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 snippets/auth-next/link-multiple-accounts/account_exists_popup.js diff --git a/auth-next/link-multiple-accounts.js b/auth-next/link-multiple-accounts.js index 34c0db58..e2efd0e5 100644 --- a/auth-next/link-multiple-accounts.js +++ b/auth-next/link-multiple-accounts.js @@ -182,3 +182,59 @@ function unlink(providerId) { }); // [END auth_unlink_provider] } + +function accountExistsPopup(auth, facebookProvider, goToApp) { + // [START account_exists_popup] + // User tries to sign in with Facebook. + signInWithPopup(auth, facebookProvider).catch((error) => { + // User's email already exists. + if (error.code === 'auth/account-exists-with-different-credential') { + // The pending Facebook credential. + const pendingCred = error.credential; + // The provider account's email address. + const email = error.customData.email; + + // Present the user with a list of providers they might have + // used to create the original account. + // Then, ask the user to sign in with the existing provider. + const method = promptUserForSignInMethod(); + + if (method === 'password') { + // TODO: Ask the user for their password. + // In real scenario, you should handle this asynchronously. + const password = promptUserForPassword(); + signInWithEmailAndPassword(auth, email, password).then((result) => { + return linkWithCredential(result.user, pendingCred); + }).then(() => { + // Facebook account successfully linked to the existing user. + goToApp(); + }); + return; + } + + // All other cases are external providers. + // Construct provider object for that provider. + // TODO: Implement getProviderForProviderId. + const provider = getProviderForProviderId(method); + // At this point, you should let the user know that they already have an + // account with a different provider, and validate they want to sign in + // with the new provider. + // Note: Browsers usually block popups triggered asynchronously, so in + // real app, you should ask the user to click on a "Continue" button + // that will trigger signInWithPopup(). + signInWithPopup(auth, provider).then((result) => { + // Note: Identity Platform doesn't control the provider's sign-in + // flow, so it's possible for the user to sign in with an account + // with a different email from the first one. + + // Link the Facebook credential. We have access to the pending + // credential, so we can directly call the link method. + linkWithCredential(result.user, pendingCred).then((userCred) => { + // Success. + goToApp(); + }); + }); + } +}); +// [END account_exists_popup] +} diff --git a/auth/link-multiple-accounts.js b/auth/link-multiple-accounts.js index 30f5a2ec..d68a8667 100644 --- a/auth/link-multiple-accounts.js +++ b/auth/link-multiple-accounts.js @@ -166,3 +166,59 @@ function unlink(providerId) { }); // [END auth_unlink_provider] } + +function accountExistsPopup(facebookProvider, goToApp) { + // [START account_exists_popup] + // User tries to sign in with Facebook. + auth.signInWithPopup(facebookProvider).catch((error) => { + // User's email already exists. + if (error.code === 'auth/account-exists-with-different-credential') { + // The pending Facebook credential. + const pendingCred = error.credential; + // The provider account's email address. + const email = error.email; + + // Present the user with a list of providers they might have + // used to create the original account. + // Then, ask the user to sign in with the existing provider. + const method = promptUserForSignInMethod(); + + if (method === 'password') { + // TODO: Ask the user for their password. + // In real scenario, you should handle this asynchronously. + const password = promptUserForPassword(); + auth.signInWithEmailAndPassword(email, password).then((result) => { + return result.user.linkWithCredential(pendingCred); + }).then(() => { + // Facebook account successfully linked to the existing user. + goToApp(); + }); + return; + } + + // All other cases are external providers. + // Construct provider object for that provider. + // TODO: Implement getProviderForProviderId. + const provider = getProviderForProviderId(method); + // At this point, you should let the user know that they already have an + // account with a different provider, and validate they want to sign in + // with the new provider. + // Note: Browsers usually block popups triggered asynchronously, so in + // real app, you should ask the user to click on a "Continue" button + // that will trigger signInWithPopup(). + auth.signInWithPopup(provider).then((result) => { + // Note: Identity Platform doesn't control the provider's sign-in + // flow, so it's possible for the user to sign in with an account + // with a different email from the first one. + + // Link the Facebook credential. We have access to the pending + // credential, so we can directly call the link method. + result.user.linkWithCredential(pendingCred).then((userCred) => { + // Success. + goToApp(); + }); + }); + } +}); +// [END account_exists_popup] +} \ No newline at end of file diff --git a/snippets/auth-next/link-multiple-accounts/account_exists_popup.js b/snippets/auth-next/link-multiple-accounts/account_exists_popup.js new file mode 100644 index 00000000..90db0eb2 --- /dev/null +++ b/snippets/auth-next/link-multiple-accounts/account_exists_popup.js @@ -0,0 +1,59 @@ +// This snippet file was generated by processing the source file: +// ./auth-next/link-multiple-accounts.js +// +// To update the snippets in this file, edit the source and then run +// 'npm run snippets'. + + // [START account_exists_popup_modular] + // User tries to sign in with Facebook. + signInWithPopup(auth, facebookProvider).catch((error) => { + // User's email already exists. + if (error.code === 'auth/account-exists-with-different-credential') { + // The pending Facebook credential. + const pendingCred = error.credential; + // The provider account's email address. + const email = error.customData.email; + + // Present the user with a list of providers they might have + // used to create the original account. + // Then, ask the user to sign in with the existing provider. + const method = promptUserForSignInMethod(); + + if (method === 'password') { + // TODO: Ask the user for their password. + // In real scenario, you should handle this asynchronously. + const password = promptUserForPassword(); + signInWithEmailAndPassword(auth, email, password).then((result) => { + return linkWithCredential(result.user, pendingCred); + }).then(() => { + // Facebook account successfully linked to the existing user. + goToApp(); + }); + return; + } + + // All other cases are external providers. + // Construct provider object for that provider. + // TODO: Implement getProviderForProviderId. + const provider = getProviderForProviderId(method); + // At this point, you should let the user know that they already have an + // account with a different provider, and validate they want to sign in + // with the new provider. + // Note: Browsers usually block popups triggered asynchronously, so in + // real app, you should ask the user to click on a "Continue" button + // that will trigger signInWithPopup(). + signInWithPopup(auth, provider).then((result) => { + // Note: Identity Platform doesn't control the provider's sign-in + // flow, so it's possible for the user to sign in with an account + // with a different email from the first one. + + // Link the Facebook credential. We have access to the pending + // credential, so we can directly call the link method. + linkWithCredential(result.user, pendingCred).then((userCred) => { + // Success. + goToApp(); + }); + }); + } +}); +// [END account_exists_popup_modular] \ No newline at end of file From 3090bd43f97150725af3bc0a1d4864489d387a5c Mon Sep 17 00:00:00 2001 From: nhienlam Date: Thu, 11 Apr 2024 10:54:29 -0700 Subject: [PATCH 2/4] Import auth functions --- auth-next/link-multiple-accounts.js | 4 +++- .../auth-next/link-multiple-accounts/account_exists_popup.js | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/auth-next/link-multiple-accounts.js b/auth-next/link-multiple-accounts.js index e2efd0e5..c91d7b54 100644 --- a/auth-next/link-multiple-accounts.js +++ b/auth-next/link-multiple-accounts.js @@ -183,8 +183,10 @@ function unlink(providerId) { // [END auth_unlink_provider] } -function accountExistsPopup(auth, facebookProvider, goToApp) { +function accountExistsPopup(auth, facebookProvider, goToApp, promptUserForPassword, promptUserForSignInMethod) { // [START account_exists_popup] + const { signInWithPopup, signInWithEmailAndPassword, linkWithCredential } = require("firebase/auth"); + // User tries to sign in with Facebook. signInWithPopup(auth, facebookProvider).catch((error) => { // User's email already exists. diff --git a/snippets/auth-next/link-multiple-accounts/account_exists_popup.js b/snippets/auth-next/link-multiple-accounts/account_exists_popup.js index 90db0eb2..2c667882 100644 --- a/snippets/auth-next/link-multiple-accounts/account_exists_popup.js +++ b/snippets/auth-next/link-multiple-accounts/account_exists_popup.js @@ -5,6 +5,8 @@ // 'npm run snippets'. // [START account_exists_popup_modular] + import { signInWithPopup, signInWithEmailAndPassword, linkWithCredential } from "firebase/auth"; + // User tries to sign in with Facebook. signInWithPopup(auth, facebookProvider).catch((error) => { // User's email already exists. From 4c0917b85684f263e2751a08a171c56fafbd5106 Mon Sep 17 00:00:00 2001 From: nhienlam Date: Thu, 11 Apr 2024 10:58:26 -0700 Subject: [PATCH 3/4] Add missing methods to param --- auth-next/link-multiple-accounts.js | 2 +- auth/link-multiple-accounts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/auth-next/link-multiple-accounts.js b/auth-next/link-multiple-accounts.js index c91d7b54..1938aafc 100644 --- a/auth-next/link-multiple-accounts.js +++ b/auth-next/link-multiple-accounts.js @@ -183,7 +183,7 @@ function unlink(providerId) { // [END auth_unlink_provider] } -function accountExistsPopup(auth, facebookProvider, goToApp, promptUserForPassword, promptUserForSignInMethod) { +function accountExistsPopup(auth, facebookProvider, goToApp, promptUserForPassword, promptUserForSignInMethod, getProviderForProviderId) { // [START account_exists_popup] const { signInWithPopup, signInWithEmailAndPassword, linkWithCredential } = require("firebase/auth"); diff --git a/auth/link-multiple-accounts.js b/auth/link-multiple-accounts.js index d68a8667..98d82a0d 100644 --- a/auth/link-multiple-accounts.js +++ b/auth/link-multiple-accounts.js @@ -221,4 +221,4 @@ function accountExistsPopup(facebookProvider, goToApp) { } }); // [END account_exists_popup] -} \ No newline at end of file +} From 5b5ddba742b72a49175e512cdd375f059c6c9430 Mon Sep 17 00:00:00 2001 From: nhienlam Date: Thu, 11 Apr 2024 11:02:40 -0700 Subject: [PATCH 4/4] Add missing methods to params for legacy snippets --- auth/link-multiple-accounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/link-multiple-accounts.js b/auth/link-multiple-accounts.js index 98d82a0d..8eed77b8 100644 --- a/auth/link-multiple-accounts.js +++ b/auth/link-multiple-accounts.js @@ -167,7 +167,7 @@ function unlink(providerId) { // [END auth_unlink_provider] } -function accountExistsPopup(facebookProvider, goToApp) { +function accountExistsPopup(facebookProvider, goToApp, promptUserForPassword, promptUserForSignInMethod, getProviderForProviderId) { // [START account_exists_popup] // User tries to sign in with Facebook. auth.signInWithPopup(facebookProvider).catch((error) => {