diff --git a/packages/lib/next-auth/auth-options.ts b/packages/lib/next-auth/auth-options.ts index cd31926c2..ceaab9e7f 100644 --- a/packages/lib/next-auth/auth-options.ts +++ b/packages/lib/next-auth/auth-options.ts @@ -1,5 +1,6 @@ import { PrismaAdapter } from '@next-auth/prisma-adapter'; import { compare } from 'bcrypt'; +import { DateTime } from 'luxon'; import { AuthOptions, Session, User } from 'next-auth'; import CredentialsProvider from 'next-auth/providers/credentials'; import GoogleProvider, { GoogleProfile } from 'next-auth/providers/google'; @@ -54,6 +55,7 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = { clientId: process.env.NEXT_PRIVATE_GOOGLE_CLIENT_ID ?? '', clientSecret: process.env.NEXT_PRIVATE_GOOGLE_CLIENT_SECRET ?? '', allowDangerousEmailAccountLinking: true, + profile(profile) { return { id: Number(profile.sub), @@ -65,27 +67,50 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = { ], callbacks: { async jwt({ token, user }) { - if (!token.email) { - throw new Error('No email in token'); + const merged = { + ...token, + ...user, + }; + + if (!merged.email) { + const userId = Number(merged.id ?? token.sub); + + const retrieved = await prisma.user.findFirst({ + where: { + id: userId, + }, + }); + + if (!retrieved) { + return token; + } + + merged.id = retrieved.id; + merged.name = retrieved.name; + merged.email = retrieved.email; } - const retrievedUser = await prisma.user.findFirst({ - where: { - email: token.email, - }, - }); + if ( + !merged.lastSignedIn || + DateTime.fromISO(merged.lastSignedIn).plus({ hours: 1 }) <= DateTime.now() + ) { + merged.lastSignedIn = new Date().toISOString(); - if (!retrievedUser) { - return { - ...token, - id: user.id, - }; + await prisma.user.update({ + where: { + id: Number(merged.id), + }, + data: { + lastSignedIn: merged.lastSignedIn, + }, + }); } return { - id: retrievedUser.id, - name: retrievedUser.name, - email: retrievedUser.email, + id: merged.id, + name: merged.name, + email: merged.email, + lastSignedIn: merged.lastSignedIn, }; }, diff --git a/packages/lib/types/next-auth.d.ts b/packages/lib/types/next-auth.d.ts index e28a7cf13..102678ef5 100644 --- a/packages/lib/types/next-auth.d.ts +++ b/packages/lib/types/next-auth.d.ts @@ -19,5 +19,6 @@ declare module 'next-auth/jwt' { id: string | number; name?: string | null; email: string | null; + lastSignedIn?: string | null; } } diff --git a/packages/prisma/migrations/20231028094931_add_user_timestamp_columns/migration.sql b/packages/prisma/migrations/20231028094931_add_user_timestamp_columns/migration.sql new file mode 100644 index 000000000..657bee132 --- /dev/null +++ b/packages/prisma/migrations/20231028094931_add_user_timestamp_columns/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "lastSignedIn" TIMESTAMP(3) NOT NULL DEFAULT '1970-01-01 00:00:00 +00:00', +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/packages/prisma/migrations/20231028095542_use_now_for_last_signed_in/migration.sql b/packages/prisma/migrations/20231028095542_use_now_for_last_signed_in/migration.sql new file mode 100644 index 000000000..8d4468a69 --- /dev/null +++ b/packages/prisma/migrations/20231028095542_use_now_for_last_signed_in/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "lastSignedIn" SET DEFAULT CURRENT_TIMESTAMP; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 53e7c121b..8dfe5ca0d 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -26,6 +26,9 @@ model User { password String? source String? signature String? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + lastSignedIn DateTime @default(now()) roles Role[] @default([USER]) identityProvider IdentityProvider @default(DOCUMENSO) accounts Account[] @@ -111,10 +114,10 @@ model Document { Field Field[] ShareLink DocumentShareLink[] documentDataId String - documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade) + documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade) documentMeta DocumentMeta? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt @@unique([documentDataId]) } @@ -134,11 +137,11 @@ model DocumentData { } model DocumentMeta { - id String @id @default(cuid()) - subject String? - message String? - documentId Int @unique - document Document @relation(fields: [documentId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + subject String? + message String? + documentId Int @unique + document Document @relation(fields: [documentId], references: [id], onDelete: Cascade) } enum ReadStatus {