mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-09 20:27:44 +00:00
feat(nuxt-example): add sign-up and password reset pages, update auth client
This commit is contained in:
@@ -1,3 +1,14 @@
|
||||
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;
|
||||
|
||||
@@ -11,4 +11,10 @@ export const auth = betterAuth({
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
|
||||
},
|
||||
},
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
async sendResetPassword(url, user) {
|
||||
console.log("Reset password url:", url);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -16,8 +16,8 @@ const router = useRouter();
|
||||
<CardContent>
|
||||
<div class="flex gap-2 items-center">
|
||||
<Avatar>
|
||||
<AvatarImage :src="session.data?.user.image || ''" alt="@radix-vue" />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
<AvatarImage :src="session.data?.user.image || ''" alt="User profile" />
|
||||
<AvatarFallback>{{ session.data?.user.name[0] }}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<p class="text-sm">
|
||||
@@ -30,10 +30,10 @@ const router = useRouter();
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button @click="async()=>{
|
||||
<Button @click="async () => {
|
||||
await signOut()
|
||||
router.push('/')
|
||||
}" variant="secondary" >
|
||||
}" variant="secondary">
|
||||
Sing Out
|
||||
</Button>
|
||||
</CardFooter>
|
||||
|
||||
56
examples/nuxt-example/pages/forget-password.vue
Normal file
56
examples/nuxt-example/pages/forget-password.vue
Normal 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>
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
const features = ["social sign-in", "email and password", "two-factor"];
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useSession } from "~/lib/auth-client";
|
||||
|
||||
@@ -7,6 +6,17 @@ definePageMeta({
|
||||
layout: "default",
|
||||
});
|
||||
|
||||
const features = [
|
||||
"Email & Password",
|
||||
"Organization | Teams",
|
||||
"Passkeys",
|
||||
"Multi Factor",
|
||||
"Password Reset",
|
||||
"Email Verification",
|
||||
"Roles & Permissions",
|
||||
"Rate Limiting",
|
||||
"Session Management",
|
||||
];
|
||||
const session = useSession();
|
||||
</script>
|
||||
|
||||
@@ -18,8 +28,15 @@ const session = useSession();
|
||||
Better Auth.
|
||||
</h3>
|
||||
|
||||
<p class="text-center">
|
||||
Official <span class="italic underline">better-auth</span> Svelte-Kit demo
|
||||
<p class="text-center break-words text-sm md:text-base">
|
||||
Official demo to showcase
|
||||
<a
|
||||
href="https://better-auth.com"
|
||||
target="_blank"
|
||||
className="italic underline"
|
||||
>better-auth
|
||||
</a>
|
||||
features and capabilities. <br />
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col gap-3 pt-2 flex-wrap">
|
||||
@@ -46,8 +63,8 @@ const session = useSession();
|
||||
Sign In
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/dashboard">
|
||||
<Button variant="outline" class="rounded-none" v-if="session.data">
|
||||
<NuxtLink to="/dashboard" v-if="session.data">
|
||||
<Button variant="outline" class="rounded-none">
|
||||
Dashboard
|
||||
</Button>
|
||||
</NuxtLink>
|
||||
|
||||
50
examples/nuxt-example/pages/reset-password.vue
Normal file
50
examples/nuxt-example/pages/reset-password.vue
Normal 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>
|
||||
@@ -9,7 +9,27 @@ import {
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
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>
|
||||
|
||||
<template>
|
||||
@@ -27,18 +47,18 @@ import { signIn } from "@/lib/auth-client.js";
|
||||
<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 />
|
||||
<Input id="email" type="email" placeholder="m@example.com" v-model="email" required />
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<div class="flex items-center">
|
||||
<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?
|
||||
</a>
|
||||
</div>
|
||||
<Input id="password" type="password" required placeholder="password" />
|
||||
<Input id="password" type="password" placeholder="password" v-model="password" required />
|
||||
</div>
|
||||
<Button type="submit" class="w-full">
|
||||
<Button type="submit" class="w-full" @click="handleSignIn">
|
||||
Login
|
||||
</Button>
|
||||
<Button variant="outline" class="w-full" @click="async () => {
|
||||
@@ -52,7 +72,7 @@ import { signIn } from "@/lib/auth-client.js";
|
||||
</div>
|
||||
<div class="mt-4 text-center text-sm">
|
||||
Don't have an account?
|
||||
<a href="#" class="underline">
|
||||
<a href="/sign-up" class="underline">
|
||||
Sign up
|
||||
</a>
|
||||
</div>
|
||||
|
||||
68
examples/nuxt-example/pages/sign-up.vue
Normal file
68
examples/nuxt-example/pages/sign-up.vue
Normal 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>
|
||||
Reference in New Issue
Block a user