feat: new client and more

This commit is contained in:
Bereket Engida
2024-08-24 23:34:12 +03:00
parent 7fd1a59eee
commit f53ce02ceb
44 changed files with 730 additions and 665 deletions

View File

@@ -32,6 +32,7 @@
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.52.2",
"react-qr-code": "^2.0.15",
"sonner": "^1.5.0",
"tailwind-merge": "^2.5.0",
"tailwindcss-animate": "^1.0.7",

Binary file not shown.

View File

@@ -11,8 +11,9 @@ import {
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { authClient } from "@/lib/client";
import { authClient } from "@/lib/auth-client";
import { useState } from "react";
import { Key } from "lucide-react";
export default function Page() {
const [email, setEmail] = useState("");
@@ -59,7 +60,7 @@ export default function Page() {
/>
</div>
<Button type="submit" className="w-full" onClick={async () => {
const res = await authClient.signInCredential({
await authClient.signIn.credential({
body: {
email,
password,
@@ -73,14 +74,22 @@ export default function Page() {
variant="outline"
className="w-full"
onClick={async () => {
await authClient.signInOAuth({
provider: "github",
callbackURL: "http://localhost:3000/",
await authClient.signIn.oauth({
body: {
provider: "github",
callbackURL: "http://localhost:3000",
}
});
}}
>
Login with Github
</Button>
<Button variant="secondary" className="gap-2" onClick={async () => {
}}>
<Key size={16} />
Login with Passkey
</Button>
</div>
<div className="mt-4 text-center text-sm">
Don&apos;t have an account?{" "}

View File

@@ -13,7 +13,7 @@ import {
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useState } from "react";
import { authClient } from "@/lib/client";
import { authClient } from "@/lib/auth-client";
export default function SignUpForm() {
const [firstName, setFirstName] = useState("");

View File

@@ -0,0 +1,77 @@
'use client'
import { useState } from 'react'
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"
import { AlertCircle, CheckCircle2, Mail } from "lucide-react"
import { authClient } from '@/lib/auth-client'
export default function Component() {
const [otp, setOtp] = useState('')
const [isOtpSent, setIsOtpSent] = useState(false)
const [message, setMessage] = useState('')
const [isError, setIsError] = useState(false)
const [isValidated, setIsValidated] = useState(false)
// In a real app, this email would come from your authentication context
const userEmail = "user@example.com"
const requestOTP = async () => {
await authClient.twoFactor.sendOtp();
// In a real app, this would call your backend API to send the OTP
setMessage('OTP sent to your email')
setIsError(false)
setIsOtpSent(true)
}
const validateOTP = async () => {
await authClient.twoFactor.verifyOtp({
body: {
code: otp,
}
})
}
return (
<main className='flex flex-col items-center justify-center min-h-screen'>
<Card className="w-[350px]">
<CardHeader>
<CardTitle>Two-Factor Authentication</CardTitle>
<CardDescription>Verify your identity with a one-time password</CardDescription>
</CardHeader>
<CardContent>
<div className="grid w-full items-center gap-4">
{!isOtpSent ? (
<Button onClick={requestOTP} className="w-full">
<Mail className="mr-2 h-4 w-4" /> Send OTP to Email
</Button>
) : (
<>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="otp">One-Time Password</Label>
<Input
id="otp"
placeholder="Enter 6-digit OTP"
value={otp}
onChange={(e) => setOtp(e.target.value)}
maxLength={6}
/>
</div>
<Button onClick={validateOTP} disabled={otp.length !== 6 || isValidated}>
Validate OTP
</Button>
</>
)}
</div>
{message && (
<div className={`flex items-center gap-2 mt-4 ${isError ? 'text-destructive' : 'text-primary'}`}>
{isError ? <AlertCircle className="h-4 w-4" /> : <CheckCircle2 className="h-4 w-4" />}
<p className="text-sm">{message}</p>
</div>
)}
</CardContent>
</Card>
</main>
)
}

View File

@@ -6,7 +6,8 @@ import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { AlertCircle, CheckCircle2 } from "lucide-react"
import { authClient } from "@/lib/client"
import { authClient } from "@/lib/auth-client"
import Link from "next/link"
export default function Component() {
const [totpCode, setTotpCode] = useState("")
@@ -19,10 +20,10 @@ export default function Component() {
setError("TOTP code must be 6 digits")
return
}
authClient.verifyTotp({
authClient.twoFactor.verify({
body: {
code: totpCode,
callbackURL: "/"
with: "totp",
}
}).then((res) => {
console.log(res)
@@ -78,8 +79,12 @@ export default function Component() {
</div>
)}
</CardContent>
<CardFooter className="text-sm text-muted-foreground">
Protect your account with TOTP-based authentication
<CardFooter className="text-sm text-muted-foreground gap-2">
<Link href="/two-factor/otp">
<Button variant="link" size="sm">
Switch to Email Verification
</Button>
</Link>
</CardFooter>
</Card>
</main>

View File

@@ -28,7 +28,7 @@ export default async function Home() {
<Button>signin</Button>
</Link>
)}
<Organization />
{/* <Organization /> */}
<Client />
</main>
);

View File

@@ -1,7 +1,7 @@
"use client";
import { setCounter } from "@/server/counter";
import { Button } from "./ui/button";
import { client } from "@/lib/client";
import { client } from "@/lib/auth-client";
export async function AddCount() {
return (

View File

@@ -1,10 +1,15 @@
"use client";
import { authClient } from "@/lib/client";
import { authClient } from "@/lib/auth-client";
import { useAuthStore } from "better-auth/react"
import { Button } from "./ui/button";
import QRCode from "react-qr-code";
import { useEffect, useState } from "react";
import { Card, CardContent, CardHeader } from "./ui/card";
import { Dialog, DialogContent, DialogTrigger } from "./ui/dialog";
export function Client() {
const session = useAuthStore(authClient.$session)
const [uri, setUri] = useState<string>()
const session = authClient.useSession()
type S = NonNullable<typeof session>
const a: S['user'] = {
id: "1",
@@ -14,23 +19,59 @@ export function Client() {
createdAt: new Date(),
updatedAt: new Date(),
}
useEffect(() => {
if (session?.user?.twoFactorEnabled) {
authClient.twoFactor.getTotpUri().then((res) => {
if (res.data) {
setUri(res.data.totpURI)
}
})
}
}, [session])
return (
<div>
{
session ? <div>
<Button onClick={async () => {
if (session.user.twoFactorEnabled) {
await authClient.disableTotp()
} else {
await authClient.enableTotp()
}
}}>
{
session.user.twoFactorEnabled ? "Disable" : "Enable"
}
</Button>
</div> : null
}
</div>
<Card>
<CardHeader>
</CardHeader>
<CardContent className="flex items-center justify-center gap-2">
{
session ? <div>
<Button onClick={async () => {
if (session.user?.twoFactorEnabled) {
await authClient.twoFactor.disable()
} else {
await authClient.twoFactor.enable()
}
}}>
{
session.user?.twoFactorEnabled ? "Disable 2FA" : "Enable 2FA"
}
</Button>
</div> : null
}
{
uri ? <Dialog>
<DialogTrigger asChild>
<Button variant="outline">
View TOTP URI
</Button>
</DialogTrigger>
<DialogContent className="flex flex-col items-center justify-center">
{
uri ? <div>
<p>
URI to scan
</p>
<QRCode value={uri} />
</div> : null
}
</DialogContent>
</Dialog> : null
}
</CardContent>
</Card>
)
}

View File

@@ -26,7 +26,7 @@ import {
TableHeader,
TableRow,
} from "./ui/table";
import { authClient } from "@/lib/client";
import { authClient } from "@/lib/auth-client";
import { useAuthStore } from "better-auth/react";
export const Organization = () => {

View File

@@ -1,6 +1,6 @@
"use client";
import { authClient } from "@/lib/client";
import { authClient } from "@/lib/auth-client";
import { Button } from "./ui/button";
export const SignOut = () => {

View File

@@ -1,4 +1,4 @@
import { createAuthClient } from "better-auth/client";
import { createAuthClient } from "better-auth/react";
import { auth } from "./auth";
export const authClient = createAuthClient<typeof auth>({

View File

@@ -1,6 +1,6 @@
import { betterAuth } from "better-auth";
import { github, passkey } from "better-auth/provider";
import { twoFactor } from "better-auth/plugins";
import { organization, twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
basePath: "/api/auth",
@@ -9,10 +9,6 @@ export const auth = betterAuth({
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
}),
passkey({
rpID: "localhost",
rpName: "Better Auth",
}),
],
database: {
provider: "sqlite",
@@ -23,9 +19,15 @@ export const auth = betterAuth({
enabled: true,
},
plugins: [
organization(),
twoFactor({
issuer: "BetterAuth",
twoFactorURL: "/two-factor",
otpOptions: {
async sendOTP(user, otp) {
console.log({ user, otp });
},
},
}),
],
});