From 3bbe71f2c967dc13c1f99f973c877f43f7366cce Mon Sep 17 00:00:00 2001 From: Liyas Thomas Date: Fri, 28 Feb 2020 05:35:28 +0530 Subject: [PATCH] :alembic: Link multiple auth providers --- components/firebase/login.vue | 148 ++++++++++++++++++++++++++-- pages/settings.vue | 175 +++++++++++++++++++++++++++++----- 2 files changed, 292 insertions(+), 31 deletions(-) diff --git a/components/firebase/login.vue b/components/firebase/login.vue index 06594cb4a..31120ba98 100644 --- a/components/firebase/login.vue +++ b/components/firebase/login.vue @@ -66,8 +66,9 @@ export default { action: { text: this.$t("yes"), onClick: (e, toastObject) => { - fb.writeSettings("syncHistory", false) + fb.writeSettings("syncHistory", true) fb.writeSettings("syncCollections", true) + fb.writeSettings("syncEnvironments", true) this.$router.push({ path: "/settings" }) toastObject.remove() }, @@ -76,9 +77,74 @@ export default { } }) .catch(err => { - this.$toast.show(err.message || err, { - icon: "error", - }) + // An error happened. + if (err.code === "auth/account-exists-with-different-credential") { + // Step 2. + // User's email already exists. + // The pending Google credential. + var pendingCred = err.credential + // The provider account's email address. + var email = err.email + // Get sign-in methods for this email. + auth.fetchSignInMethodsForEmail(email).then(function(methods) { + // Step 3. + // If the user has several sign-in methods, + // the first method in the list will be the "recommended" method to use. + if (methods[0] === "password") { + // Asks the user their password. + // In real scenario, you should handle this asynchronously. + var password = promptUserForPassword() // TODO: implement promptUserForPassword. + auth + .signInWithEmailAndPassword(email, password) + .then(function(user) { + // Step 4a. + return user.linkWithCredential(pendingCred) + }) + .then(function() { + // Google account successfully linked to the existing Firebase user. + goToApp() + }) + return + } + + this.$toast.info(`${this.$t("login_with")}`, { + icon: "vpn_key", + duration: null, + closeOnSwipe: false, + action: { + text: this.$t("yes"), + onClick: (e, toastObject) => { + // All the other cases are external providers. + // Construct provider object for that provider. + // TODO: implement getProviderForProviderId. + var provider = new firebase.auth.GithubAuthProvider() + // At this point, you should let the user know that they already has an account + // but with a different provider, and let them validate the fact they want to + // sign in with this provider. + // Sign in to provider. Note: browsers usually block popup triggered asynchronously, + // so in real scenario you should ask the user to click on a "continue" button + // that will trigger the signInWithPopup. + auth.signInWithPopup(provider).then(function(result) { + // Remember that the user may have signed in with an account that has a different email + // address than the first one. This can happen as Firebase doesn't control the provider's + // sign in flow and the user is free to login using whichever account they own. + // Step 4b. + // Link to Google credential. + // As we have access to the pending credential, we can directly call the link method. + result.user + .linkAndRetrieveDataWithCredential(pendingCred) + .then(function(usercred) { + // Google account successfully linked to the existing Firebase user. + goToApp() + }) + }) + + toastObject.remove() + }, + }, + }) + }) + } }) }, signInWithGithub() { @@ -95,8 +161,9 @@ export default { action: { text: this.$t("yes"), onClick: (e, toastObject) => { - fb.writeSettings("syncHistory", false) + fb.writeSettings("syncHistory", true) fb.writeSettings("syncCollections", true) + fb.writeSettings("syncEnvironments", true) this.$router.push({ path: "/settings" }) toastObject.remove() }, @@ -105,9 +172,74 @@ export default { } }) .catch(err => { - this.$toast.show(err.message || err, { - icon: "error", - }) + // An error happened. + if (err.code === "auth/account-exists-with-different-credential") { + // Step 2. + // User's email already exists. + // The pending Google credential. + var pendingCred = err.credential + // The provider account's email address. + var email = err.email + // Get sign-in methods for this email. + auth.fetchSignInMethodsForEmail(email).then(function(methods) { + // Step 3. + // If the user has several sign-in methods, + // the first method in the list will be the "recommended" method to use. + if (methods[0] === "password") { + // Asks the user their password. + // In real scenario, you should handle this asynchronously. + var password = promptUserForPassword() // TODO: implement promptUserForPassword. + auth + .signInWithEmailAndPassword(email, password) + .then(function(user) { + // Step 4a. + return user.linkWithCredential(pendingCred) + }) + .then(function() { + // Google account successfully linked to the existing Firebase user. + goToApp() + }) + return + } + + this.$toast.info(`${this.$t("login_with")}`, { + icon: "vpn_key", + duration: null, + closeOnSwipe: false, + action: { + text: this.$t("yes"), + onClick: (e, toastObject) => { + // All the other cases are external providers. + // Construct provider object for that provider. + // TODO: implement getProviderForProviderId. + var provider = new firebase.auth.GoogleAuthProvider() + // At this point, you should let the user know that they already has an account + // but with a different provider, and let them validate the fact they want to + // sign in with this provider. + // Sign in to provider. Note: browsers usually block popup triggered asynchronously, + // so in real scenario you should ask the user to click on a "continue" button + // that will trigger the signInWithPopup. + auth.signInWithPopup(provider).then(function(result) { + // Remember that the user may have signed in with an account that has a different email + // address than the first one. This can happen as Firebase doesn't control the provider's + // sign in flow and the user is free to login using whichever account they own. + // Step 4b. + // Link to Google credential. + // As we have access to the pending credential, we can directly call the link method. + result.user + .linkAndRetrieveDataWithCredential(pendingCred) + .then(function(usercred) { + // Google account successfully linked to the existing Firebase user. + goToApp() + }) + }) + + toastObject.remove() + }, + }, + }) + }) + } }) }, }, diff --git a/pages/settings.vue b/pages/settings.vue index 98ec14d17..e032e65fe 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -327,8 +327,7 @@ export default { settings: { SCROLL_INTO_ENABLED: - typeof this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED !== - "undefined" + typeof this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED !== "undefined" ? this.$store.state.postwoman.settings.SCROLL_INTO_ENABLED : true, @@ -394,24 +393,6 @@ export default { this.settings[key] = value this.$store.commit("postwoman/applySetting", [key, value]) }, - toggleSetting(key) { - this.settings[key] = !this.settings[key] - this.$store.commit("postwoman/applySetting", [key, this.settings[key]]) - }, - logout() { - fb.currentUser = null - firebase - .auth() - .signOut() - .catch(err => { - this.$toast.show(err.message || err, { - icon: "error", - }) - }) - this.$toast.info(this.$t("logged_out"), { - icon: "vpn_key", - }) - }, signInWithGoogle() { const provider = new firebase.auth.GoogleAuthProvider() firebase @@ -437,9 +418,74 @@ export default { } }) .catch(err => { - this.$toast.show(err.message || err, { - icon: "error", - }) + // An error happened. + if (err.code === "auth/account-exists-with-different-credential") { + // Step 2. + // User's email already exists. + // The pending Google credential. + var pendingCred = err.credential + // The provider account's email address. + var email = err.email + // Get sign-in methods for this email. + auth.fetchSignInMethodsForEmail(email).then(function(methods) { + // Step 3. + // If the user has several sign-in methods, + // the first method in the list will be the "recommended" method to use. + if (methods[0] === "password") { + // Asks the user their password. + // In real scenario, you should handle this asynchronously. + var password = promptUserForPassword() // TODO: implement promptUserForPassword. + auth + .signInWithEmailAndPassword(email, password) + .then(function(user) { + // Step 4a. + return user.linkWithCredential(pendingCred) + }) + .then(function() { + // Google account successfully linked to the existing Firebase user. + goToApp() + }) + return + } + + this.$toast.info(`${this.$t("login_with")}`, { + icon: "vpn_key", + duration: null, + closeOnSwipe: false, + action: { + text: this.$t("yes"), + onClick: (e, toastObject) => { + // All the other cases are external providers. + // Construct provider object for that provider. + // TODO: implement getProviderForProviderId. + var provider = new firebase.auth.GithubAuthProvider() + // At this point, you should let the user know that they already has an account + // but with a different provider, and let them validate the fact they want to + // sign in with this provider. + // Sign in to provider. Note: browsers usually block popup triggered asynchronously, + // so in real scenario you should ask the user to click on a "continue" button + // that will trigger the signInWithPopup. + auth.signInWithPopup(provider).then(function(result) { + // Remember that the user may have signed in with an account that has a different email + // address than the first one. This can happen as Firebase doesn't control the provider's + // sign in flow and the user is free to login using whichever account they own. + // Step 4b. + // Link to Google credential. + // As we have access to the pending credential, we can directly call the link method. + result.user + .linkAndRetrieveDataWithCredential(pendingCred) + .then(function(usercred) { + // Google account successfully linked to the existing Firebase user. + goToApp() + }) + }) + + toastObject.remove() + }, + }, + }) + }) + } }) }, signInWithGithub() { @@ -466,11 +512,94 @@ export default { }) } }) + .catch(err => { + // An error happened. + if (err.code === "auth/account-exists-with-different-credential") { + // Step 2. + // User's email already exists. + // The pending Google credential. + var pendingCred = err.credential + // The provider account's email address. + var email = err.email + // Get sign-in methods for this email. + auth.fetchSignInMethodsForEmail(email).then(function(methods) { + // Step 3. + // If the user has several sign-in methods, + // the first method in the list will be the "recommended" method to use. + if (methods[0] === "password") { + // Asks the user their password. + // In real scenario, you should handle this asynchronously. + var password = promptUserForPassword() // TODO: implement promptUserForPassword. + auth + .signInWithEmailAndPassword(email, password) + .then(function(user) { + // Step 4a. + return user.linkWithCredential(pendingCred) + }) + .then(function() { + // Google account successfully linked to the existing Firebase user. + goToApp() + }) + return + } + + this.$toast.info(`${this.$t("login_with")}`, { + icon: "vpn_key", + duration: null, + closeOnSwipe: false, + action: { + text: this.$t("yes"), + onClick: (e, toastObject) => { + // All the other cases are external providers. + // Construct provider object for that provider. + // TODO: implement getProviderForProviderId. + var provider = new firebase.auth.GoogleAuthProvider() + // At this point, you should let the user know that they already has an account + // but with a different provider, and let them validate the fact they want to + // sign in with this provider. + // Sign in to provider. Note: browsers usually block popup triggered asynchronously, + // so in real scenario you should ask the user to click on a "continue" button + // that will trigger the signInWithPopup. + auth.signInWithPopup(provider).then(function(result) { + // Remember that the user may have signed in with an account that has a different email + // address than the first one. This can happen as Firebase doesn't control the provider's + // sign in flow and the user is free to login using whichever account they own. + // Step 4b. + // Link to Google credential. + // As we have access to the pending credential, we can directly call the link method. + result.user + .linkAndRetrieveDataWithCredential(pendingCred) + .then(function(usercred) { + // Google account successfully linked to the existing Firebase user. + goToApp() + }) + }) + + toastObject.remove() + }, + }, + }) + }) + } + }) + }, + toggleSetting(key) { + this.settings[key] = !this.settings[key] + this.$store.commit("postwoman/applySetting", [key, this.settings[key]]) + }, + logout() { + fb.currentUser = null + firebase + .auth() + .signOut() .catch(err => { this.$toast.show(err.message || err, { icon: "error", }) }) + this.$toast.info(this.$t("logged_out"), { + icon: "vpn_key", + }) }, toggleSettings(s, v) { fb.writeSettings(s, !v)