Merge pull request 'feat: integrate Google OAuth authentication provider and add login UI button' (#5) from add-google-auth into development
Reviewed-on: https://gitea.2pidev.com/jselesan/playzer/pulls/5
This commit is contained in:
19
AGENTS.md
19
AGENTS.md
@@ -88,7 +88,26 @@ Biome is used (not ESLint). Config in `biome.json`:
|
|||||||
- `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL` - Auth core
|
- `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL` - Auth core
|
||||||
- `APP_BASE_URL` - Frontend URL for trusted origins
|
- `APP_BASE_URL` - Frontend URL for trusted origins
|
||||||
- `CORS_ORIGIN` - Frontend URL for CORS policy
|
- `CORS_ORIGIN` - Frontend URL for CORS policy
|
||||||
|
- `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` - Google OAuth (see below)
|
||||||
|
|
||||||
**Frontend** (`apps/frontend/.env`):
|
**Frontend** (`apps/frontend/.env`):
|
||||||
- `VITE_*` prefix required (Vite embeds these at build time)
|
- `VITE_*` prefix required (Vite embeds these at build time)
|
||||||
- `VITE_API_BASE_URL` - Backend URL (default: http://localhost:3000)
|
- `VITE_API_BASE_URL` - Backend URL (default: http://localhost:3000)
|
||||||
|
|
||||||
|
## Google OAuth
|
||||||
|
|
||||||
|
To enable login with Google:
|
||||||
|
|
||||||
|
1. Create OAuth credentials in Google Cloud Console:
|
||||||
|
- Application type: Web application
|
||||||
|
- Authorized redirect URI: `{BETTER_AUTH_URL}/api/auth/callback/google`
|
||||||
|
|
||||||
|
2. Add env vars to `.env.example`:
|
||||||
|
```
|
||||||
|
GOOGLE_CLIENT_ID=your-google-client-id
|
||||||
|
GOOGLE_CLIENT_SECRET=your-google-client-secret
|
||||||
|
```
|
||||||
|
|
||||||
|
3. The backend configures `socialProviders.google` automatically.
|
||||||
|
|
||||||
|
4. Frontend uses `authClient.signIn.social({ provider: 'google', callbackURL: 'http://localhost:5173' })`.
|
||||||
|
|||||||
@@ -12,3 +12,7 @@ SMTP_PORT=587
|
|||||||
SMTP_USER=user@example.com
|
SMTP_USER=user@example.com
|
||||||
SMTP_PASS=your-smtp-password
|
SMTP_PASS=your-smtp-password
|
||||||
SMTP_FROM=Playzer <no-reply@example.com>
|
SMTP_FROM=Playzer <no-reply@example.com>
|
||||||
|
|
||||||
|
# Google OAuth
|
||||||
|
GOOGLE_CLIENT_ID=your-google-client-id
|
||||||
|
GOOGLE_CLIENT_SECRET=your-google-client-secret
|
||||||
|
|||||||
@@ -17,7 +17,21 @@ export const auth = betterAuth({
|
|||||||
emailAndPassword: {
|
emailAndPassword: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
trustedOrigins: [process.env.APP_BASE_URL ?? 'http://localhost:5173'],
|
socialProviders: {
|
||||||
|
google: {
|
||||||
|
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||||
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
advanced: {
|
||||||
|
generateUsername: async (data) => {
|
||||||
|
return `user_${Date.now()}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
trustedOrigins: [
|
||||||
|
process.env.APP_BASE_URL ?? 'http://localhost:5173',
|
||||||
|
process.env.BETTER_AUTH_URL ?? 'http://localhost:3000',
|
||||||
|
],
|
||||||
plugins: [openAPI()],
|
plugins: [openAPI()],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Field, FieldError, FieldLabel } from '@/components/ui/field';
|
import { Field, FieldError, FieldLabel } from '@/components/ui/field';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { authClient } from '@/lib/api-client';
|
||||||
import { useAuth } from '@/lib/auth';
|
import { useAuth } from '@/lib/auth';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { Link, useNavigate } from '@tanstack/react-router';
|
import { Link, useNavigate } from '@tanstack/react-router';
|
||||||
@@ -48,6 +49,21 @@ export function LoginPage({ redirectTo = '/' }: LoginPageProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleGoogleSignIn = async () => {
|
||||||
|
setSubmitError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await authClient.signIn.social({
|
||||||
|
provider: 'google',
|
||||||
|
callbackURL: redirectTo.startsWith('http')
|
||||||
|
? redirectTo
|
||||||
|
: `http://localhost:5173${redirectTo}`,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
setSubmitError('No pudimos iniciar sesión con Google.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex min-h-screen w-full items-center justify-center px-6">
|
<main className="flex min-h-screen w-full items-center justify-center px-6">
|
||||||
<section className="w-full max-w-sm rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
|
<section className="w-full max-w-sm rounded-xl border bg-card p-6 text-card-foreground shadow-sm">
|
||||||
@@ -56,6 +72,37 @@ export function LoginPage({ redirectTo = '/' }: LoginPageProps) {
|
|||||||
Ingresá con tu cuenta para acceder a Home.
|
Ingresá con tu cuenta para acceder a Home.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<Button type="button" variant="outline" className="w-full" onClick={handleGoogleSignIn}>
|
||||||
|
<svg className="mr-2 h-4 w-4" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Continuar con Google
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="relative my-4">
|
||||||
|
<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-card px-2 text-muted-foreground">O</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form className="space-y-3" onSubmit={handleSubmit(onSubmit)}>
|
<form className="space-y-3" onSubmit={handleSubmit(onSubmit)}>
|
||||||
<Field data-invalid={Boolean(errors.email)}>
|
<Field data-invalid={Boolean(errors.email)}>
|
||||||
<FieldLabel htmlFor="email">Email</FieldLabel>
|
<FieldLabel htmlFor="email">Email</FieldLabel>
|
||||||
|
|||||||
Reference in New Issue
Block a user