password recovery flow

This commit is contained in:
Michael Dausmann
2023-05-06 18:31:00 +10:00
parent fe0f68087c
commit c6b48f05c1
4 changed files with 95 additions and 3 deletions

View File

@@ -20,7 +20,7 @@ Demo site [here](https://nuxt3-saas-boilerplate.netlify.app/)
### User Management ### User Management
- [x] Social Signon (e.g. google) via Supabase, Full list of available [providers](https://supabase.com/docs/guides/auth#providers) - [x] Social Signon (e.g. google) via Supabase, Full list of available [providers](https://supabase.com/docs/guides/auth#providers)
- [x] Email/Password Signon via Supabase - [x] Email/Password Signon via Supabase
- [ ] Password recovery - [x] Password recovery
- [x] User roles and permissions (admin, regular user, etc. roles defined in the [Prisma Schema](/prisma/schema.prisma)) - [x] User roles and permissions (admin, regular user, etc. roles defined in the [Prisma Schema](/prisma/schema.prisma))
- [x] User Email captured on initial login - [x] User Email captured on initial login
- [x] Initial plan and plan period controled via config to allow either a trial plan or a 'No Plan' for initial users - [x] Initial plan and plan period controled via config to allow either a trial plan or a 'No Plan' for initial users
@@ -69,6 +69,8 @@ Demo site [here](https://nuxt3-saas-boilerplate.netlify.app/)
### Look and Feel, Design System and Customisation ### Look and Feel, Design System and Customisation
- [x] Default UI isn't too crap - [x] Default UI isn't too crap
- [x] Integrated Design system including theming (Tailwind + daisyUI) - [x] Integrated Design system including theming (Tailwind + daisyUI)
- [ ] Toasts for things like reset email sent
- [ ] Modals, just because people like modals
### Demo Software (Notes) ### Demo Software (Notes)
- [x] Simple Text based Notes functionality - [x] Simple Text based Notes functionality
@@ -99,7 +101,8 @@ This solution uses Supabase for Auth and to provide a DB. In addition to Magic
1) Go to [Supabase](https://supabase.com/) and 'Start your Project' 1) Go to [Supabase](https://supabase.com/) and 'Start your Project'
2) Setup your org and project (Free tier is fine to start) 2) Setup your org and project (Free tier is fine to start)
3) Update the project's email template 3) Update the project's email template (Supabase -> Authentication -> Email Templates)
Note that the default Supabase email templates are very generic and for some reason this can lead to your emails being sent to spam folders. e.g. I to get my password reset emails to go to inbox, I needed to change the subject to "Password Reset for Nuxt 3 SAAS Boilerplate" and also the email body text.
4) Choose an OAuth provider. I have chosen Google using these [Instructions](https://supabase.com/docs/guides/auth/social-login/auth-google) for the purposes of demonstration but they all should work. 4) Choose an OAuth provider. I have chosen Google using these [Instructions](https://supabase.com/docs/guides/auth/social-login/auth-google) for the purposes of demonstration but they all should work.
5) Go to Project Settings -> API and copy Project URL and Project API Key to SUPABASE_URL and SUPABASE_KEY settings respectively in your [.env](/.env) file 5) Go to Project Settings -> API and copy Project URL and Project API Key to SUPABASE_URL and SUPABASE_KEY settings respectively in your [.env](/.env) file
6) Go to Project Settings -> Database -> Connection String -> URI and copy the uri value into the DATABASE_URL setting in your [.env](/.env) file, remembering to replace ```[YOUR-PASSWORD]``` with the password you provided when you setup the project. 6) Go to Project Settings -> Database -> Connection String -> URI and copy the uri value into the DATABASE_URL setting in your [.env](/.env) file, remembering to replace ```[YOUR-PASSWORD]``` with the password you provided when you setup the project.
@@ -192,9 +195,13 @@ Steps (Assumes your repo is in github)
5) Setup environment variables per the .env_example file (SUPABASE_URL, SUPABASE_KEY....etc) 5) Setup environment variables per the .env_example file (SUPABASE_URL, SUPABASE_KEY....etc)
6) Optionally change site name (e.g. mycoolsaas) or apply a domain name 6) Optionally change site name (e.g. mycoolsaas) or apply a domain name
7) Go to (Supabase)[https://app.supabase.com/] 7) Go to [Supabase](https://app.supabase.com/)
8) Choose your project 8) Choose your project
9) Go to URL Authentication -> URL Configuration -> Site URL 9) Go to URL Authentication -> URL Configuration -> Site URL
10) enter your new netlify URL e.g. https://mycoolsaas.netlify.app/ and click 'save' 10) enter your new netlify URL e.g. https://mycoolsaas.netlify.app/ and click 'save'
11) Add the following additional redirect URLs for local development and deployment previews:
http://localhost:3000/**
https://**--mycoolsaas.netlify.app/**
12) If you haven't already done so, edit your Supabase Email templates as the generic ones tend to get blocked by GMail.

38
pages/forgotpassword.vue Normal file
View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
const supabase = useSupabaseAuthClient();
const config = useRuntimeConfig();
const loading = ref(false)
const email = ref('')
const sendResetPasswordLink = async () => {
try {
loading.value = true
const { data, error } = await supabase.auth.resetPasswordForEmail(email.value, {
redirectTo: `${config.public.siteRootUrl}/resetpassword`,
})
if (error) throw error
else alert('Password Reset link sent, check your email.');
} catch (error) {
alert(error)
} finally {
loading.value = false
}
}
</script>
<template>
<div class="flex flex-col items-center justify-center h-screen bg-gray-100">
<div class="w-full max-w-md p-6 space-y-6 bg-white rounded-lg shadow-lg">
<h1 class="text-3xl font-bold text-center">Forgot Pasword</h1>
<form @submit.prevent="sendResetPasswordLink" class="space-y-4">
<div>
<label for="email" class="block mb-2 font-bold">Email</label>
<input v-model="email" id="email" type="email" class="w-full p-2 border border-gray-400 rounded-md"
placeholder="Enter your email" required>
</div>
<button :disabled="loading || email === ''" type="submit"
class="w-full py-2 text-white bg-indigo-600 rounded-md hover:bg-indigo-700">Send Reset Password Link</button>
</form>
</div>
</div>
</template>

46
pages/resetpassword.vue Normal file
View File

@@ -0,0 +1,46 @@
<script setup lang="ts">
const supabase = useSupabaseAuthClient();
const loading = ref(false)
const password = ref('')
const confirmPassword = ref('')
const changePassword = async () => {
try {
loading.value = true
const { data, error } = await supabase.auth.updateUser({
password: password.value
});
if (error) throw error
else {
alert('password changed');
navigateTo('/signin', {replace: true}); // navigate to signin because it is best practice although the auth session seems to be valid so it immediately redirects to dashboard
}
} catch (error) {
alert(error)
} finally {
loading.value = false
}
}
</script>
<template>
<div class="flex flex-col items-center justify-center h-screen bg-gray-100">
<div class="w-full max-w-md p-6 space-y-6 bg-white rounded-lg shadow-lg">
<h1 class="text-3xl font-bold text-center">Forgot Pasword</h1>
<form @submit.prevent="changePassword" class="space-y-4">
<div>
<label for="password" class="block mb-2 font-bold">New Password</label>
<input v-model="password" id="password" type="password" class="w-full p-2 border border-gray-400 rounded-md"
placeholder="Enter your new password" required>
</div>
<div>
<label for="confirmPassword" class="block mb-2 font-bold">Confirm New Password</label>
<input v-model="confirmPassword" id="confirmPassword" type="password" class="w-full p-2 border border-gray-400 rounded-md"
placeholder="Confirm new password" required>
</div>
<button :disabled="loading || password === '' || (confirmPassword !== password)" type="submit"
class="w-full py-2 text-white bg-indigo-600 rounded-md hover:bg-indigo-700">Change Password</button>
</form>
</div>
</div>
</template>

View File

@@ -57,6 +57,7 @@
<input v-model="password" id="password" type="password" class="w-full p-2 border border-gray-400 rounded-md" <input v-model="password" id="password" type="password" class="w-full p-2 border border-gray-400 rounded-md"
placeholder="Enter your password" required> placeholder="Enter your password" required>
</div> </div>
<NuxtLink id="forgotPasswordLink" to="/forgotpassword" class="text-right block">Forgot your password?</NuxtLink>
<button :disabled="loading || password === ''" type="submit" <button :disabled="loading || password === ''" type="submit"
class="w-full py-2 text-white bg-indigo-600 rounded-md hover:bg-indigo-700">Sign in</button> class="w-full py-2 text-white bg-indigo-600 rounded-md hover:bg-indigo-700">Sign in</button>
</form> </form>