Task 3.3: Handle Google OAuth sign-in callback in src/auth.ts

- Add signIn callback: on Google sign-in, check users table by email;
  create new user (provider='google', provider_account_id, name/image
  from profile) if not found, or allow sign-in for returning users
- Update jwt callback to look up DB uuid by email for Google sign-ins,
  so token.id is always the DB uuid rather than the Google sub ID
- Mark task 3.3 as complete in tasks.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marko Djordjevic 2026-02-20 09:58:18 +01:00
parent 40afd4111c
commit a8c88f3ca2
2 changed files with 49 additions and 2 deletions

View file

@ -16,7 +16,7 @@
- [x] 3.1 `[sonnet]` Create `src/auth.ts` with Auth.js v5 config: JWT strategy, Credentials provider (email/password with bcryptjs verify), Google OAuth provider
- [x] 3.2 `[sonnet]` Add JWT callback to embed `user.id` in token and session callback to expose `session.user.id`
- [ ] 3.3 `[sonnet]` Handle Google OAuth sign-in callback: create user on first sign-in, find existing user on returning sign-in
- [x] 3.3 `[sonnet]` Handle Google OAuth sign-in callback: create user on first sign-in, find existing user on returning sign-in
- [ ] 3.4 `[haiku]` Create `src/app/api/auth/[...nextauth]/route.ts` exporting GET/POST handlers
## 4. Registration API

View file

@ -56,10 +56,57 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
}),
],
callbacks: {
async jwt({ token, user }) {
async signIn({ user, account, profile }) {
// Only handle Google OAuth sign-ins here; credentials are handled in authorize()
if (account?.provider === "google") {
const email = profile?.email ?? user.email;
if (!email) {
return false;
}
const [existingUser] = await db
.select()
.from(users)
.where(eq(users.email, email))
.limit(1);
if (!existingUser) {
// First Google sign-in: create a new user record
await db.insert(users).values({
email,
name: profile?.name ?? user.name ?? null,
image: profile?.picture ?? (user.image ?? null),
provider: "google",
provider_account_id: account.providerAccountId,
password_hash: null,
});
}
// Returning Google sign-in: existing user found, allow sign-in
return true;
}
return true;
},
async jwt({ token, user, account }) {
if (user) {
// For credentials sign-in the authorize() function already returns the DB uuid as user.id
token.id = user.id;
}
// For Google sign-in, user.id is the Google sub ID, not our DB uuid.
// Look up the DB record by email to get the correct uuid.
if (account?.provider === "google" && token.email) {
const [dbUser] = await db
.select({ id: users.id })
.from(users)
.where(eq(users.email, token.email))
.limit(1);
if (dbUser) {
token.id = dbUser.id;
}
}
return token;
},
async session({ session, token }) {