feat: change and verify user's email address
This commit is contained in:
@@ -17,6 +17,9 @@ import {
|
|||||||
AuthCredential,
|
AuthCredential,
|
||||||
UserCredential,
|
UserCredential,
|
||||||
updateProfile,
|
updateProfile,
|
||||||
|
updateEmail,
|
||||||
|
sendEmailVerification,
|
||||||
|
reauthenticateWithCredential,
|
||||||
} from "firebase/auth"
|
} from "firebase/auth"
|
||||||
import {
|
import {
|
||||||
onSnapshot,
|
onSnapshot,
|
||||||
@@ -318,6 +321,78 @@ export async function setDisplayName(name: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send user's email address verification mail
|
||||||
|
*/
|
||||||
|
export async function verifyEmailAddress() {
|
||||||
|
if (!currentUser$.value) throw new Error("No user has logged in")
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendEmailVerification(currentUser$.value).catch((e) =>
|
||||||
|
console.error("error updating", e)
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
console.error("error updating", e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user's email address
|
||||||
|
*
|
||||||
|
* @param email - The new email address
|
||||||
|
*/
|
||||||
|
export async function setEmailAddress(email: string) {
|
||||||
|
if (!currentUser$.value) throw new Error("No user has logged in")
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateEmail(currentUser$.value, email).catch(async (e) => {
|
||||||
|
await reauthenticateUser()
|
||||||
|
console.error("error updating", email, e)
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error("error updating", e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reauthenticate the user with the given credential
|
||||||
|
*/
|
||||||
|
async function reauthenticateUser() {
|
||||||
|
if (!currentUser$.value) throw new Error("No user has logged in")
|
||||||
|
const currentAuthMethod = await fetchSignInMethodsForEmail(
|
||||||
|
getAuth(),
|
||||||
|
currentUser$.value.email as string
|
||||||
|
)
|
||||||
|
console.log(currentAuthMethod)
|
||||||
|
|
||||||
|
let credential
|
||||||
|
if (currentAuthMethod.includes("github.com")) {
|
||||||
|
const result = await signInUserWithGithub()
|
||||||
|
credential = GithubAuthProvider.credentialFromResult(result)
|
||||||
|
} else if (currentAuthMethod.includes("google.com")) {
|
||||||
|
const result = await signInUserWithGoogle()
|
||||||
|
credential = GoogleAuthProvider.credentialFromResult(result)
|
||||||
|
} else if (currentAuthMethod.includes("emailLink")) {
|
||||||
|
const email = prompt("Email:")
|
||||||
|
const actionCodeSettings = {
|
||||||
|
url: `${process.env.BASE_URL}/enter`,
|
||||||
|
handleCodeInApp: true,
|
||||||
|
}
|
||||||
|
await signInWithEmail(email as string, actionCodeSettings)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await reauthenticateWithCredential(
|
||||||
|
currentUser$.value,
|
||||||
|
credential as AuthCredential
|
||||||
|
).catch((e) => console.error("error updating", e))
|
||||||
|
} catch (e) {
|
||||||
|
console.error("error updating", e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getGithubCredentialFromResult(result: UserCredential) {
|
export function getGithubCredentialFromResult(result: UserCredential) {
|
||||||
return GithubAuthProvider.credentialFromResult(result)
|
return GithubAuthProvider.credentialFromResult(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,8 +178,8 @@
|
|||||||
"json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again",
|
"json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again",
|
||||||
"network_error": "There seems to be a network error. Please try again.",
|
"network_error": "There seems to be a network error. Please try again.",
|
||||||
"network_fail": "Could not send request",
|
"network_fail": "Could not send request",
|
||||||
"script_fail": "Could not execute pre-request script",
|
|
||||||
"no_duration": "No duration",
|
"no_duration": "No duration",
|
||||||
|
"script_fail": "Could not execute pre-request script",
|
||||||
"something_went_wrong": "Something went wrong"
|
"something_went_wrong": "Something went wrong"
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
@@ -210,11 +210,11 @@
|
|||||||
"authorization": "The authorization header will be automatically generated when you send the request.",
|
"authorization": "The authorization header will be automatically generated when you send the request.",
|
||||||
"generate_documentation_first": "Generate documentation first",
|
"generate_documentation_first": "Generate documentation first",
|
||||||
"network_fail": "Unable to reach the API endpoint. Check your network connection or turn on Proxy Interceptor and try again.",
|
"network_fail": "Unable to reach the API endpoint. Check your network connection or turn on Proxy Interceptor and try again.",
|
||||||
"script_fail": "It seems there is a glitch in the pre-request script. Check the error below and fix the script accordingly.",
|
|
||||||
"offline": "You seem to be offline. Data in this workspace might not be up to date.",
|
"offline": "You seem to be offline. Data in this workspace might not be up to date.",
|
||||||
"offline_short": "You seem to be offline.",
|
"offline_short": "You seem to be offline.",
|
||||||
"post_request_tests": "Test scripts are written in JavaScript, and are run after the response is received.",
|
"post_request_tests": "Test scripts are written in JavaScript, and are run after the response is received.",
|
||||||
"pre_request_script": "Pre-request scripts are written in JavaScript, and are run before the request is sent.",
|
"pre_request_script": "Pre-request scripts are written in JavaScript, and are run before the request is sent.",
|
||||||
|
"script_fail": "It seems there is a glitch in the pre-request script. Check the error below and fix the script accordingly.",
|
||||||
"tests": "Write a test script to automate debugging."
|
"tests": "Write a test script to automate debugging."
|
||||||
},
|
},
|
||||||
"hide": {
|
"hide": {
|
||||||
@@ -273,6 +273,7 @@
|
|||||||
"app_settings": "App Settings",
|
"app_settings": "App Settings",
|
||||||
"editor": "Editor",
|
"editor": "Editor",
|
||||||
"editor_description": "Editors can add, edit, and delete requests.",
|
"editor_description": "Editors can add, edit, and delete requests.",
|
||||||
|
"email_verification_mail": "A verification email has been sent to your email address. Please click on the link to verify your email address.",
|
||||||
"no_permission": "You do not have permission to perform this action.",
|
"no_permission": "You do not have permission to perform this action.",
|
||||||
"owner": "Owner",
|
"owner": "Owner",
|
||||||
"owner_description": "Owners can add, edit, and delete requests, collections and team members.",
|
"owner_description": "Owners can add, edit, and delete requests, collections and team members.",
|
||||||
@@ -359,6 +360,7 @@
|
|||||||
"official_proxy_hosting": "Official Proxy is hosted by Hoppscotch.",
|
"official_proxy_hosting": "Official Proxy is hosted by Hoppscotch.",
|
||||||
"profile": "Profile",
|
"profile": "Profile",
|
||||||
"profile_description": "Update your profile details",
|
"profile_description": "Update your profile details",
|
||||||
|
"profile_email": "Email address",
|
||||||
"profile_name": "Profile name",
|
"profile_name": "Profile name",
|
||||||
"proxy": "Proxy",
|
"proxy": "Proxy",
|
||||||
"proxy_url": "Proxy URL",
|
"proxy_url": "Proxy URL",
|
||||||
@@ -377,7 +379,8 @@
|
|||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"theme_description": "Customize your application theme.",
|
"theme_description": "Customize your application theme.",
|
||||||
"use_experimental_url_bar": "Use experimental URL bar with environment highlighting",
|
"use_experimental_url_bar": "Use experimental URL bar with environment highlighting",
|
||||||
"user": "User"
|
"user": "User",
|
||||||
|
"verify_email": "Verify email"
|
||||||
},
|
},
|
||||||
"shortcut": {
|
"shortcut": {
|
||||||
"general": {
|
"general": {
|
||||||
|
|||||||
@@ -46,6 +46,15 @@
|
|||||||
name="verified"
|
name="verified"
|
||||||
class="ml-2 text-green-500 svg-icons"
|
class="ml-2 text-green-500 svg-icons"
|
||||||
/>
|
/>
|
||||||
|
<ButtonSecondary
|
||||||
|
v-else
|
||||||
|
:label="t('settings.verify_email')"
|
||||||
|
svg="verified"
|
||||||
|
class="ml-2"
|
||||||
|
filled
|
||||||
|
:loading="verifyingEmailAddress"
|
||||||
|
@click.native="sendEmailVerification"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -99,6 +108,31 @@
|
|||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="py-4">
|
||||||
|
<label for="emailAddress">
|
||||||
|
{{ t("settings.profile_email") }}
|
||||||
|
</label>
|
||||||
|
<form
|
||||||
|
class="flex mt-2 md:max-w-sm"
|
||||||
|
@submit.prevent="updateEmailAddress"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="emailAddress"
|
||||||
|
v-model="emailAddress"
|
||||||
|
class="input"
|
||||||
|
:placeholder="`${t('settings.profile_name')}`"
|
||||||
|
type="email"
|
||||||
|
autocomplete="off"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<ButtonPrimary
|
||||||
|
:label="t('action.save')"
|
||||||
|
class="ml-2 min-w-16"
|
||||||
|
type="submit"
|
||||||
|
:loading="updatingEmailAddress"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="p-4">
|
<section class="p-4">
|
||||||
<h4 class="font-semibold text-secondaryDark">
|
<h4 class="font-semibold text-secondaryDark">
|
||||||
@@ -150,11 +184,21 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, useMeta, defineComponent } from "@nuxtjs/composition-api"
|
import { ref, useMeta, defineComponent } from "@nuxtjs/composition-api"
|
||||||
import { currentUser$, setDisplayName } from "~/helpers/fb/auth"
|
import {
|
||||||
import { useReadonlyStream, useI18n } from "~/helpers/utils/composables"
|
currentUser$,
|
||||||
|
setDisplayName,
|
||||||
|
setEmailAddress,
|
||||||
|
verifyEmailAddress,
|
||||||
|
} from "~/helpers/fb/auth"
|
||||||
|
import {
|
||||||
|
useReadonlyStream,
|
||||||
|
useI18n,
|
||||||
|
useToast,
|
||||||
|
} from "~/helpers/utils/composables"
|
||||||
import { toggleSetting, useSetting } from "~/newstore/settings"
|
import { toggleSetting, useSetting } from "~/newstore/settings"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const showLogin = ref(false)
|
const showLogin = ref(false)
|
||||||
|
|
||||||
@@ -168,11 +212,31 @@ const updatingDisplayName = ref(false)
|
|||||||
|
|
||||||
const updateDisplayName = () => {
|
const updateDisplayName = () => {
|
||||||
updatingDisplayName.value = true
|
updatingDisplayName.value = true
|
||||||
setDisplayName(displayName.value).finally(() => {
|
setDisplayName(displayName.value as string).finally(() => {
|
||||||
updatingDisplayName.value = false
|
updatingDisplayName.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const emailAddress = ref(currentUser$.value?.email)
|
||||||
|
const updatingEmailAddress = ref(false)
|
||||||
|
|
||||||
|
const updateEmailAddress = () => {
|
||||||
|
updatingEmailAddress.value = true
|
||||||
|
setEmailAddress(emailAddress.value as string).finally(() => {
|
||||||
|
updatingEmailAddress.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyingEmailAddress = ref(false)
|
||||||
|
|
||||||
|
const sendEmailVerification = () => {
|
||||||
|
verifyingEmailAddress.value = true
|
||||||
|
verifyEmailAddress().finally(() => {
|
||||||
|
verifyingEmailAddress.value = false
|
||||||
|
toast.success(`${t("profile.email_verification_mail")}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
useMeta({
|
useMeta({
|
||||||
title: `${t("navigation.profile")} • Hoppscotch`,
|
title: `${t("navigation.profile")} • Hoppscotch`,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user