From 4993b081515379f69cb6a2d16739575689e5b354 Mon Sep 17 00:00:00 2001 From: Smnthjm08 Date: Sat, 7 Dec 2024 13:15:29 +0530 Subject: [PATCH] feat: update lastLoginIp saving on login --- .../migration.sql | 2 ++ prisma/schema.prisma | 1 + src/lib/auth.ts | 13 +++++++---- src/middleware.ts | 23 +++++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 prisma/migrations/20241207065505_added_last_login_ip_in_user_model/migration.sql diff --git a/prisma/migrations/20241207065505_added_last_login_ip_in_user_model/migration.sql b/prisma/migrations/20241207065505_added_last_login_ip_in_user_model/migration.sql new file mode 100644 index 00000000..db246a59 --- /dev/null +++ b/prisma/migrations/20241207065505_added_last_login_ip_in_user_model/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "lastLoginIp" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ca8e4431..2162258a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -157,6 +157,7 @@ model User { appxUserId String? appxUsername String? appxAuthToken String? + lastLoginIp String? questions Question[] answers Answer[] certificate Certificate[] diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 9f52f422..92a9e0d4 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -39,13 +39,14 @@ interface user { token: string; } -const generateJWT = async (payload: JWTPayload) => { +const generateJWT = async (payload: JWTPayload, userIp: string) => { const secret = process.env.JWT_SECRET || 'secret'; const jwk = await importJWK({ k: secret, alg: 'HS256', kty: 'oct' }); const jwt = await new SignJWT({ ...payload, + userIp, iat: Math.floor(Date.now() / 1000), jti: randomUUID(), // Adding a unique jti to ensure each token is unique. This helps generate a unique jwtToken on every login }) @@ -119,8 +120,9 @@ export const authOptions = { username: { label: 'email', type: 'text', placeholder: '' }, password: { label: 'password', type: 'password', placeholder: '' }, }, - async authorize(credentials: any) { + async authorize(credentials: any, req: any) { try { + const userIp = req.headers['x-forwarded-for']?.split(',')[0] || req.socket.remoteAddress; if (process.env.LOCAL_CMS_PROVIDER) { return { id: '1', @@ -128,7 +130,7 @@ export const authOptions = { email: 'test@gmail.com', token: await generateJWT({ id: '1', - }), + }, userIp), }; } const hashedPassword = await bcrypt.hash(credentials.password, 10); @@ -152,13 +154,14 @@ export const authOptions = { ) { const jwt = await generateJWT({ id: userDb.id, - }); + }, userIp); await db.user.update({ where: { id: userDb.id, }, data: { token: jwt, + lastLoginIp: userIp }, }); @@ -177,7 +180,7 @@ export const authOptions = { const jwt = await generateJWT({ id: user.data?.userid, - }); + }, userIp); if (user.data) { try { diff --git a/src/middleware.ts b/src/middleware.ts index ffae8401..2bbbac4a 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -14,6 +14,14 @@ interface RequestWithUser extends NextRequest { }; } +export const getClientIP = (req: NextRequest | NextRequestWithAuth): string => { + return ( + req.headers.get('x-forwarded-for')?.split(',')[0] || + req.ip || + '127.0.0.1' + ); +}; + export const verifyJWT = async (token: string): Promise => { const secret = process.env.JWT_SECRET || ''; @@ -29,6 +37,8 @@ export const verifyJWT = async (token: string): Promise => { }; export const withMobileAuth = async (req: RequestWithUser) => { + const clientIP = getClientIP(req); + if (req.headers.get('Auth-Key')) { return NextResponse.next(); } @@ -41,6 +51,12 @@ export const withMobileAuth = async (req: RequestWithUser) => { if (!payload) { return NextResponse.json({ message: 'Unauthorized' }, { status: 403 }); } + + if (payload.userIp !== clientIP) { + console.warn("IP Mismatch"); + return NextResponse.json({ message: 'Unauthorized - ip verification Failed',}, { status: 403 }); + } + const newHeaders = new Headers(req.headers); /** @@ -65,6 +81,8 @@ const withAuth = async (req: NextRequestWithAuth) => { return NextResponse.redirect(new URL('/invalidsession', req.url)); } + const clientIP = getClientIP(req); + const user = await fetch( `${process.env.NEXT_PUBLIC_BASE_URL_LOCAL}/api/user?token=${token.jwtToken}`, ); @@ -73,6 +91,11 @@ const withAuth = async (req: NextRequestWithAuth) => { if (!json.user) { return NextResponse.redirect(new URL('/invalidsession', req.url)); } + + if (token.userIp !== clientIP) { + console.warn("IP Mismatch"); + return NextResponse.redirect(new URL('/invalidsession', req.url)); + } }; export async function middleware(req: NextRequestWithAuth) {