mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
feat(auth): general auth improvements, added signin/up with google
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Github } from 'lucide-react'
|
||||
import { GithubIcon, GoogleIcon } from '@/components/icons'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Card,
|
||||
@@ -49,6 +49,14 @@ export default function LoginPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function signInWithGoogle() {
|
||||
try {
|
||||
await client.signIn.social({ provider: 'google' })
|
||||
} catch (err) {
|
||||
setError('Failed to sign in with Google')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-center bg-gray-50">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
@@ -60,10 +68,16 @@ export default function LoginPage() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-6">
|
||||
<Button variant="outline" onClick={signInWithGithub} className="w-full">
|
||||
<Github className="mr-2 h-4 w-4" />
|
||||
Continue with GitHub
|
||||
</Button>
|
||||
<div className="grid gap-2">
|
||||
<Button variant="outline" onClick={signInWithGithub} className="w-full">
|
||||
<GithubIcon className="mr-2 h-4 w-4" />
|
||||
Continue with GitHub
|
||||
</Button>
|
||||
<Button variant="outline" onClick={signInWithGoogle} className="w-full">
|
||||
<GoogleIcon className="mr-2 h-4 w-4" />
|
||||
Continue with Google
|
||||
</Button>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<span className="w-full border-t" />
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { GithubIcon, GoogleIcon } from '@/components/icons'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Card,
|
||||
@@ -51,6 +52,22 @@ export default function SignupPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function signUpWithGithub() {
|
||||
try {
|
||||
await client.signIn.social({ provider: 'github' })
|
||||
} catch (err) {
|
||||
addNotification('error', 'Failed to sign up with GitHub', null)
|
||||
}
|
||||
}
|
||||
|
||||
async function signUpWithGoogle() {
|
||||
try {
|
||||
await client.signIn.social({ provider: 'google' })
|
||||
} catch (err) {
|
||||
addNotification('error', 'Failed to sign up with Google', null)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-center bg-gray-50">
|
||||
<NotificationList />
|
||||
@@ -61,39 +78,61 @@ export default function SignupPage() {
|
||||
<CardTitle>Create an account</CardTitle>
|
||||
<CardDescription>Enter your details to get started</CardDescription>
|
||||
</CardHeader>
|
||||
<form onSubmit={onSubmit}>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="name">Name</Label>
|
||||
<Input id="name" name="name" type="text" required />
|
||||
<CardContent>
|
||||
<div className="grid gap-6">
|
||||
<div className="grid gap-2">
|
||||
<Button variant="outline" onClick={signUpWithGithub} className="w-full">
|
||||
<GithubIcon className="mr-2 h-4 w-4" />
|
||||
Continue with GitHub
|
||||
</Button>
|
||||
<Button variant="outline" onClick={signUpWithGoogle} className="w-full">
|
||||
<GoogleIcon className="mr-2 h-4 w-4" />
|
||||
Continue with Google
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="name@example.com"
|
||||
required
|
||||
/>
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<span className="w-full border-t" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-xs uppercase">
|
||||
<span className="bg-background px-2 text-muted-foreground">Or continue with</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input id="password" name="password" type="password" required />
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="flex flex-col space-y-4">
|
||||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||||
{isLoading ? 'Creating account...' : 'Create account'}
|
||||
</Button>
|
||||
<p className="text-sm text-gray-500">
|
||||
Already have an account?{' '}
|
||||
<Link href="/login" className="text-primary hover:underline">
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
</CardFooter>
|
||||
</form>
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="name">Name</Label>
|
||||
<Input id="name" name="name" type="text" required />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="name@example.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input id="password" name="password" type="password" required />
|
||||
</div>
|
||||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||||
{isLoading ? 'Creating account...' : 'Create account'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<p className="text-sm text-gray-500 text-center w-full">
|
||||
Already have an account?{' '}
|
||||
<Link href="/login" className="text-primary hover:underline">
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
37
lib/auth.ts
37
lib/auth.ts
@@ -22,6 +22,16 @@ export const auth = betterAuth({
|
||||
provider: 'pg',
|
||||
schema,
|
||||
}),
|
||||
socialProviders: {
|
||||
github: {
|
||||
clientId: process.env.GITHUB_CLIENT_ID as string,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
|
||||
},
|
||||
google: {
|
||||
clientId: process.env.GOOGLE_CLIENT_ID as string,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
|
||||
},
|
||||
},
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
requireEmailVerification: true,
|
||||
@@ -40,10 +50,12 @@ export const auth = betterAuth({
|
||||
},
|
||||
},
|
||||
emailVerification: {
|
||||
sendVerificationEmail: async ({ user, url }: EmailHandler) => {
|
||||
console.log('Attempting to send verification email to:', user.email)
|
||||
console.log('Verification URL:', url)
|
||||
sendVerificationEmail: async ({ user, url, token }, request) => {
|
||||
try {
|
||||
if (!user.email) {
|
||||
throw new Error('User email is required')
|
||||
}
|
||||
|
||||
const result = await resend.emails.send({
|
||||
from: 'Sim Studio <onboarding@simstudio.ai>',
|
||||
to: user.email,
|
||||
@@ -55,18 +67,21 @@ export const auth = betterAuth({
|
||||
<p>If you didn't create an account, you can safely ignore this email.</p>
|
||||
`,
|
||||
})
|
||||
console.log('Resend API response:', result)
|
||||
|
||||
if (!result) {
|
||||
throw new Error('Failed to send verification email')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sending verification email:', error)
|
||||
console.error('Error sending verification email:', {
|
||||
error,
|
||||
user: user.email,
|
||||
url,
|
||||
token,
|
||||
})
|
||||
throw error
|
||||
}
|
||||
},
|
||||
},
|
||||
socialProviders: {
|
||||
github: {
|
||||
clientId: process.env.GITHUB_CLIENT_ID!,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
||||
},
|
||||
},
|
||||
plugins: [nextCookies()],
|
||||
pages: {
|
||||
signIn: '/login',
|
||||
|
||||
Reference in New Issue
Block a user