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";
|
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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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>
|
||||||
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">
|
<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>
|
||||||
|
|||||||
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";
|
} 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>
|
||||||
|
|||||||
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