feat: add Google OAuth login with USER role and pending approval flow

- Add GET /api/auth/google and GET /api/auth/callback/google routes with CSRF state protection and account linking via googleId
- Add getPublicOrigin() for dynamic redirect_uri (supports reverse proxy via X-Forwarded-Proto)
- Add USER role to schema (default for new Google sign-ins), make password optional, add googleId and image fields
- Role-based redirect after login: USER → /profile, ADMIN/DEVELOPER → /dashboard
- Profile page shows pending approval alert for USER role
- Dashboard redirects USER role back to profile
- Login page shows specific error messages per OAuth error code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-28 15:06:13 +08:00
parent 9d80eb3b85
commit 94724a5081
9 changed files with 219 additions and 21 deletions

View File

@@ -0,0 +1,21 @@
-- AlterEnum: add USER back to Role
BEGIN;
CREATE TYPE "Role_new" AS ENUM ('USER', 'ADMIN', 'DEVELOPER');
ALTER TABLE "public"."user" ALTER COLUMN "role" DROP DEFAULT;
ALTER TABLE "user" ALTER COLUMN "role" TYPE "Role_new" USING ("role"::text::"Role_new");
ALTER TYPE "Role" RENAME TO "Role_old";
ALTER TYPE "Role_new" RENAME TO "Role";
DROP TYPE "public"."Role_old";
ALTER TABLE "user" ALTER COLUMN "role" SET DEFAULT 'USER';
COMMIT;
-- AlterTable: make password nullable, change default role
ALTER TABLE "user"
ALTER COLUMN "password" DROP NOT NULL,
ALTER COLUMN "role" SET DEFAULT 'USER';
-- AlterTable: add googleId column
ALTER TABLE "user" ADD COLUMN "googleId" TEXT;
-- CreateIndex
CREATE UNIQUE INDEX "user_googleId_key" ON "user"("googleId");

View File

@@ -9,6 +9,7 @@ datasource db {
}
enum Role {
USER
ADMIN
DEVELOPER
}
@@ -41,8 +42,9 @@ model User {
id String @id @default(uuid())
name String
email String @unique
password String
role Role @default(ADMIN)
password String?
googleId String? @unique
role Role @default(USER)
active Boolean @default(true)
image String?
createdAt DateTime @default(now())