feat: enhance party user authentication endpoint with improved error handling, logging, and PartyUser model integration
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
import type { User } from '@prisma/client';
|
// src/app/api/party-user-auth/me/route.ts
|
||||||
|
//
|
||||||
|
// PURPOSE:
|
||||||
|
// - Handle authentication for party users via JWT
|
||||||
|
// - Verify and decode JWT tokens
|
||||||
|
// - Return current authenticated party user details
|
||||||
|
// - Log all access attempts (success/failure)
|
||||||
|
// - Validate token structure and user existence
|
||||||
|
//
|
||||||
import type { NextRequest } from 'next/server';
|
import type { NextRequest } from 'next/server';
|
||||||
|
import type { PartyUser } from '@prisma/client';
|
||||||
|
|
||||||
import { headers } from 'next/headers';
|
import { headers } from 'next/headers';
|
||||||
|
|
||||||
@@ -7,15 +16,13 @@ import { verify } from 'src/utils/jwt';
|
|||||||
import { STATUS, response, handleError } from 'src/utils/response';
|
import { STATUS, response, handleError } from 'src/utils/response';
|
||||||
|
|
||||||
import { JWT_SECRET } from 'src/_mock/_auth';
|
import { JWT_SECRET } from 'src/_mock/_auth';
|
||||||
import { getUserById } from 'src/app/services/user.service';
|
|
||||||
import { createAccessLog } from 'src/app/services/access-log.service';
|
import { createAccessLog } from 'src/app/services/access-log.service';
|
||||||
|
import { getPartyUserById } from 'src/app/services/party-user.service';
|
||||||
|
|
||||||
import { flattenNextjsRequest } from '../sign-in/flattenNextjsRequest';
|
import { flattenNextjsRequest } from '../sign-in/flattenNextjsRequest';
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
// export const runtime = 'edge';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This API is used for demo purpose only
|
* This API is used for demo purpose only
|
||||||
* You should use a real database
|
* You should use a real database
|
||||||
@@ -24,11 +31,12 @@ import { flattenNextjsRequest } from '../sign-in/flattenNextjsRequest';
|
|||||||
* You should not expose the JWT_SECRET in the client side
|
* You should not expose the JWT_SECRET in the client side
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const USER_TOKEN_CHECK_FAILED = 'user token check failed';
|
const ERR_USER_TOKEN_CHECK_FAILED = 'user token check failed';
|
||||||
const INVALID_AUTH_TOKEN = 'Invalid authorization token';
|
const ERR_INVALID_AUTH_TOKEN = 'Invalid authorization token';
|
||||||
const USER_ID_NOT_FOUND = 'userId not found';
|
const ERR_USER_ID_NOT_FOUND = 'userId not found';
|
||||||
|
const ERR_AUTHORIZATION_TOKEN_MISSING_OR_INVALID = 'Authorization token missing or invalid';
|
||||||
|
|
||||||
const USER_TOKEN_OK = 'user token check ok';
|
const USER_TOKEN_OK = 'user token check ok';
|
||||||
const AUTHORIZATION_TOKEN_MISSING_OR_INVALID = 'Authorization token missing or invalid';
|
|
||||||
|
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
const debug = { 'req.headers': flattenNextjsRequest(req) };
|
const debug = { 'req.headers': flattenNextjsRequest(req) };
|
||||||
@@ -38,29 +46,29 @@ export async function GET(req: NextRequest) {
|
|||||||
const authorization = headersList.get('authorization');
|
const authorization = headersList.get('authorization');
|
||||||
|
|
||||||
if (!authorization || !authorization.startsWith('Bearer ')) {
|
if (!authorization || !authorization.startsWith('Bearer ')) {
|
||||||
return response({ message: AUTHORIZATION_TOKEN_MISSING_OR_INVALID }, STATUS.UNAUTHORIZED);
|
return response({ message: ERR_AUTHORIZATION_TOKEN_MISSING_OR_INVALID }, STATUS.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessToken = `${authorization}`.split(' ')[1];
|
const accessToken = `${authorization}`.split(' ')[1];
|
||||||
const data = await verify(accessToken, JWT_SECRET);
|
const data = await verify(accessToken, JWT_SECRET);
|
||||||
console.log(data.userId);
|
|
||||||
|
|
||||||
if (data.userId) {
|
if (data.userId) {
|
||||||
// TODO: remove me
|
// TODO: remove me
|
||||||
// const currentUser = _users.find((user) => user.id === data.userId);
|
// const currentUser = _users.find((user) => user.id === data.userId);
|
||||||
const currentUser: User | null = await getUserById(data.userId);
|
// const currentUser: User | null = await getUserById(data.userId);
|
||||||
|
const currentUser: PartyUser | null = await getPartyUserById(data.userId);
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
createAccessLog('', USER_TOKEN_CHECK_FAILED, debug);
|
createAccessLog('', ERR_USER_TOKEN_CHECK_FAILED, debug);
|
||||||
|
|
||||||
return response({ message: INVALID_AUTH_TOKEN }, STATUS.UNAUTHORIZED);
|
return response({ message: ERR_INVALID_AUTH_TOKEN }, STATUS.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
createAccessLog(currentUser.id, USER_TOKEN_OK, debug);
|
createAccessLog(currentUser.id, USER_TOKEN_OK, debug);
|
||||||
|
|
||||||
return response({ user: currentUser }, STATUS.OK);
|
return response({ user: currentUser }, STATUS.OK);
|
||||||
} else {
|
} else {
|
||||||
return response({ message: USER_ID_NOT_FOUND }, STATUS.ERROR);
|
return response({ message: ERR_USER_ID_NOT_FOUND }, STATUS.ERROR);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleError('[Auth] - Me', error);
|
return handleError('[Auth] - Me', error);
|
||||||
|
@@ -1,22 +1,35 @@
|
|||||||
|
// Test cases for Party User Authentication endpoints
|
||||||
|
// Tests both successful and error scenarios
|
||||||
|
// Environment: http://localhost:7272
|
||||||
|
// Expected responses:
|
||||||
|
// - 200 OK with user data for valid tokens
|
||||||
|
// - 401 Unauthorized for invalid/missing tokens
|
||||||
|
// - 400 Bad Request for invalid credentials
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# username and password ok
|
# username and password ok
|
||||||
GET http://localhost:7272/api/auth/me
|
|
||||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWJnbnUyengwMDBjaHEzaGZ3dmtjejlvIiwiaWF0IjoxNzQ4OTY0ODkyLCJleHAiOjE3NTAxNzQ0OTJ9.lo04laCxtm0IVeYaETEV3hXKyDmXPEn7SyWtY2VR4dI
|
|
||||||
|
|
||||||
|
GET http://localhost:7272/api/party-user-auth/me
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbWMwdWo4azIwMDBxM2Y1eTZlNXJzejRxIiwiaWF0IjoxNzUwMjEzOTkwLCJleHAiOjE3NTE0MjM1OTB9.MoKv3Nmrp_blE0jQ1rG1WyQ_TrJeF7kSe5xfHrF8b64
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# There is no user corresponding to the email address.
|
# There is no user corresponding to the email address.
|
||||||
POST http://localhost:7272/api/auth/sign-in
|
|
||||||
|
POST http://localhost:7272/api/party-user-auth/sign-in
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"email": "demo@minimals1.cc",
|
"email": "demo@minimals.cc",
|
||||||
"password": "@2Minimal"
|
"password": "@2Minimal"
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# Wrong password
|
# Wrong password
|
||||||
POST http://localhost:7272/api/auth/sign-in
|
|
||||||
|
POST http://localhost:7272/api/party-user-auth/sign-in
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@@ -1,14 +1,18 @@
|
|||||||
// src/app/services/user.service.ts
|
// src/app/services/party-user.service.ts
|
||||||
//
|
//
|
||||||
// PURPOSE:
|
// PURPOSE:
|
||||||
// - Handle User Record CRUD operations
|
// - Handle Party User Record CRUD operations
|
||||||
|
// - Manage party member data and permissions
|
||||||
|
// - Interface between controllers and database
|
||||||
//
|
//
|
||||||
// RULES:
|
// RULES:
|
||||||
// - Follow Prisma best practices for database operations
|
// - Follow Prisma best practices for database operations
|
||||||
// - Validate input data before processing
|
// - Validate all party user data before processing
|
||||||
|
// - Enforce party-specific business rules
|
||||||
|
// - Maintain audit trail for sensitive operations
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { User, PartyUser } from '@prisma/client';
|
import type { PartyUser } from '@prisma/client';
|
||||||
|
|
||||||
import prisma from '../lib/prisma';
|
import prisma from '../lib/prisma';
|
||||||
|
|
||||||
@@ -47,8 +51,8 @@ async function getPartyUserByEmail(email: string): Promise<PartyUser | null> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserById(id: string): Promise<User | null> {
|
async function getPartyUserById(id: string): Promise<PartyUser | null> {
|
||||||
return prisma.user.findFirst({ where: { id } });
|
return prisma.partyUser.findFirst({ where: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPartyUser(partyUserData: any): Promise<PartyUser> {
|
async function createPartyUser(partyUserData: any): Promise<PartyUser> {
|
||||||
@@ -69,7 +73,7 @@ async function deletePartyUser(partyUserId: string): Promise<PartyUser | null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getUserById,
|
getPartyUserById,
|
||||||
getPartyUser,
|
getPartyUser,
|
||||||
listPartyUsers,
|
listPartyUsers,
|
||||||
createPartyUser,
|
createPartyUser,
|
||||||
|
Reference in New Issue
Block a user