feat(nuxt-example): add sign-up and password reset pages, update auth client

This commit is contained in:
Dyrean
2024-09-29 00:11:54 +03:00
parent 89e9b70791
commit 989510dd2d
8 changed files with 255 additions and 27 deletions

View File

@@ -1,3 +1,14 @@
import { createAuthClient } from "better-auth/vue"; import { createAuthClient } from "better-auth/vue";
export const { signIn, signOut, signUp, useSession } = createAuthClient(); export const client = createAuthClient({
baseURL: "http://localhost:3000",
});
export const {
signIn,
signOut,
signUp,
useSession,
forgetPassword,
resetPassword,
} = client;

View File

@@ -1,14 +1,20 @@
import { betterAuth } from "better-auth"; import { betterAuth } from "better-auth";
export const auth = betterAuth({ export const auth = betterAuth({
database: { database: {
provider: "sqlite", provider: "sqlite",
url: "./sqlite.db", url: "./sqlite.db",
}, },
socialProviders: { socialProviders: {
google: { google: {
clientId: process.env.GOOGLE_CLIENT_ID || "", clientId: process.env.GOOGLE_CLIENT_ID || "",
clientSecret: process.env.GOOGLE_CLIENT_SECRET || "", clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
}, },
}, },
emailAndPassword: {
enabled: true,
async sendResetPassword(url, user) {
console.log("Reset password url:", url);
},
},
}); });

View File

@@ -16,8 +16,8 @@ const router = useRouter();
<CardContent> <CardContent>
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<Avatar> <Avatar>
<AvatarImage :src="session.data?.user.image || ''" alt="@radix-vue" /> <AvatarImage :src="session.data?.user.image || ''" alt="User profile" />
<AvatarFallback>CN</AvatarFallback> <AvatarFallback>{{ session.data?.user.name[0] }}</AvatarFallback>
</Avatar> </Avatar>
<div> <div>
<p class="text-sm"> <p class="text-sm">
@@ -30,13 +30,13 @@ const router = useRouter();
</div> </div>
</CardContent> </CardContent>
<CardFooter> <CardFooter>
<Button @click="async()=>{ <Button @click="async () => {
await signOut() await signOut()
router.push('/') router.push('/')
}" variant="secondary" > }" variant="secondary">
Sing Out Sing Out
</Button> </Button>
</CardFooter> </CardFooter>
</Card> </Card>
</div> </div>
</template> </template>

View File

@@ -0,0 +1,56 @@
<script lang="ts" setup>
import { forgetPassword } from "~/lib/auth-client.js"
const email = ref("")
const handleForgetPassword = async () => {
if (!email.value) {
alert("Please enter your email address");
return;
}
await forgetPassword(
{
email: email.value,
redirectTo: "/reset-password",
},
{
// onSuccess find the url with token in server console. For detail check forgetPassword section: https://www.better-auth.com/docs/authentication/email-password
onSuccess() {
alert("Password reset link sent to your email");
window.location.href = "/sign-in";
},
onError(context) {
alert(context.error.message);
},
}
);
}
</script>
<template>
<div class="h-screen flex justify-center items-center">
<CardRoot class="mx-auto max-w-sm">
<CardHeader>
<CardTitle class="text-2xl">Reset Password</CardTitle>
<CardDescription>
Enter your email below to reset your password
</CardDescription>
</CardHeader>
<CardContent>
<div class="grid gap-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" required v-model="email" />
</div>
<Button type="button" class="w-full" @click="handleForgetPassword">
Reset Password
</Button>
</div>
<div class="mt-4 text-center text-sm">
<a href="/sign-in" class="underline">Back to Sign In </a>
</div>
</CardContent>
</CardRoot>
</div>
</template>

View File

@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
const features = ["social sign-in", "email and password", "two-factor"];
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useSession } from "~/lib/auth-client"; import { useSession } from "~/lib/auth-client";
@@ -7,6 +6,17 @@ definePageMeta({
layout: "default", layout: "default",
}); });
const features = [
"Email & Password",
"Organization | Teams",
"Passkeys",
"Multi Factor",
"Password Reset",
"Email Verification",
"Roles & Permissions",
"Rate Limiting",
"Session Management",
];
const session = useSession(); const session = useSession();
</script> </script>
@@ -18,8 +28,15 @@ const session = useSession();
Better Auth. Better Auth.
</h3> </h3>
<p class="text-center"> <p class="text-center break-words text-sm md:text-base">
Official <span class="italic underline">better-auth</span> Svelte-Kit demo Official demo to showcase
<a
href="https://better-auth.com"
target="_blank"
className="italic underline"
>better-auth
</a>
features and capabilities. <br />
</p> </p>
<div class="flex flex-col gap-3 pt-2 flex-wrap"> <div class="flex flex-col gap-3 pt-2 flex-wrap">
@@ -46,8 +63,8 @@ const session = useSession();
Sign In Sign In
</Button> </Button>
</NuxtLink> </NuxtLink>
<NuxtLink to="/dashboard"> <NuxtLink to="/dashboard" v-if="session.data">
<Button variant="outline" class="rounded-none" v-if="session.data"> <Button variant="outline" class="rounded-none">
Dashboard Dashboard
</Button> </Button>
</NuxtLink> </NuxtLink>

View File

@@ -0,0 +1,50 @@
<script lang="ts" setup>
import { resetPassword } from "~/lib/auth-client.js";
const confirmPassword = ref("");
const password = ref("");
const handleResetPassword = async () => {
if (confirmPassword.value !== password.value) {
alert("Please enter same passwords");
return;
}
await resetPassword({
newPassword: password.value,
fetchOptions: {
onSuccess(context) {
window.location.href = "/sign-in";
},
onError(context) {
alert(context.error.message);
},
},
})
}
</script>
<template>
<div class="h-screen flex justify-center items-center">
<CardRoot class="mx-auto max-w-sm">
<CardHeader>
<CardTitle class="text-2xl">Reset Password</CardTitle>
<CardDescription>Enter your new password below</CardDescription>
</CardHeader>
<CardContent>
<div class="grid gap-4">
<div class="grid gap-2">
<Label for="password">New Password</Label>
<Input id="password" type="password" required v-model="password" placeholder="New Password" />
</div>
<div class="grid gap-2">
<Label for="password">Confirm Password</Label>
<Input id="password" type="password" required placeholder="Confirm Password"
v-model="confirmPassword" />
</div>
<Button type="button" class="w-full" @click="handleResetPassword">Reset</Button>
</div>
</CardContent>
</CardRoot>
</div>
</template>

View File

@@ -9,7 +9,27 @@ import {
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { signIn } from "@/lib/auth-client.js"; import { signIn } from "~/lib/auth-client.js";
const email = ref("");
const password = ref("");
const handleSignIn = async() => {
await signIn.email(
{
email: email.value,
password: password.value,
callbackURL: "/",
},
{
onError(context) {
alert(context.error.message);
},
},
);
}
</script> </script>
<template> <template>
@@ -27,18 +47,18 @@ import { signIn } from "@/lib/auth-client.js";
<div class="grid gap-4"> <div class="grid gap-4">
<div class="grid gap-2"> <div class="grid gap-2">
<Label for="email">Email</Label> <Label for="email">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" required /> <Input id="email" type="email" placeholder="m@example.com" v-model="email" required />
</div> </div>
<div class="grid gap-2"> <div class="grid gap-2">
<div class="flex items-center"> <div class="flex items-center">
<Label for="password">Password</Label> <Label for="password">Password</Label>
<a href="#" class="ml-auto inline-block text-sm underline"> <a href="/forget-password" class="ml-auto inline-block text-sm underline">
Forgot your password? Forgot your password?
</a> </a>
</div> </div>
<Input id="password" type="password" required placeholder="password" /> <Input id="password" type="password" placeholder="password" v-model="password" required />
</div> </div>
<Button type="submit" class="w-full"> <Button type="submit" class="w-full" @click="handleSignIn">
Login Login
</Button> </Button>
<Button variant="outline" class="w-full" @click="async () => { <Button variant="outline" class="w-full" @click="async () => {
@@ -52,7 +72,7 @@ import { signIn } from "@/lib/auth-client.js";
</div> </div>
<div class="mt-4 text-center text-sm"> <div class="mt-4 text-center text-sm">
Don't have an account? Don't have an account?
<a href="#" class="underline"> <a href="/sign-up" class="underline">
Sign up Sign up
</a> </a>
</div> </div>

View File

@@ -0,0 +1,68 @@
<script lang="ts" setup>
import { signUp } from "~/lib/auth-client.js"
const firstName = ref("")
const lastName = ref("")
const email = ref("")
const password = ref("")
const handleSignUp = async () => {
const user = {
firstName: firstName.value,
lastName: lastName.value,
email: email.value,
password: password.value,
};
await signUp.email({
email: user.email,
password: user.password,
name: `${user.firstName} ${user.lastName}`,
callbackURL: "/",
fetchOptions: {
onError(context) {
alert(context.error.message);
}
}
})
}
</script>
<template>
<div class="h-screen flex justify-center items-center">
<Card class="mx-auto max-w-sm">
<CardHeader>
<CardTitle class="text-xl">Sign Up</CardTitle>
<CardDescription>
Enter your information to create an account
</CardDescription>
</CardHeader>
<CardContent>
<div class="grid gap-4">
<div class="grid grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="first-name">First name</Label>
<Input id="first-name" placeholder="Max" required v-model="firstName" />
</div>
<div class="grid gap-2">
<Label for="last-name">Last name</Label>
<Input id="last-name" placeholder="Robinson" required v-model="lastName" />
</div>
</div>
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" required v-model="email" />
</div>
<div class="grid gap-2">
<Label for="password">Password</Label>
<Input id="password" type="password" v-model="password" />
</div>
<Button type="button" class="w-full" @click="handleSignUp">Create an account</Button>
</div>
<div class="mt-4 text-center text-sm">
Already have an account?
<a href="/sign-in" class="underline"> Sign in </a>
</div>
</CardContent>
</Card>
</div>
</template>