Codex 5.3 Refactor Note: Canonical refactor plan: docs/CODEX-5.3-REFACTOR-PLAN.md. This document is retained for historical and implementation context during the refactor.
W2 Implementation: Request Access
A. Summary
Implemented a public request access page where users without accounts can submit access requests. The page includes form validation, duplicate checking, audit logging, and a success confirmation screen with a unique request code.
B. Routes Implemented
- GET /request-access – Public request access page (no auth required)
- POST /api/access-requests – Public API endpoint for submitting access requests
C. Data Model Changes
Prisma Schema Updates
Added to prisma/schema.prisma:
New Enums:
RequestedRole– HR, MANAGER, SMO, ADMINAccessRequestStatus– PENDING, APPROVED, REJECTED
New Model:
model AccessRequest {
id String @id @default(cuid())
requestCode String @unique
fullName String
workEmail String @unique
department String
requestedRole RequestedRole
reason String @db.Text
managerEmail String?
status AccessRequestStatus @default(PENDING)
decidedByUserId String?
decidedBy User? @relation(fields: [decidedByUserId], references: [id])
decidedAt DateTime?
decisionNote String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Updated Model:
AuditLog.userIdchanged from required to nullable (to support public/system audit events)
Updated Enum:
AuditEventTypeaddedACCESS_REQUEST_CREATED
Migration Steps
cd /Users/rezafahmi/projectweb-nextjs
# Create and apply migration
npx prisma migrate dev --name add_access_requests
# Verify schema
npx prisma studio
D. UI Components Created
Components
- src/components/RequestAccessForm.tsx – Full form with validation, error display, loading state, and success confirmation
- src/app/request-access/page.tsx – Page layout with form + info panel (responsive)
Form Features
- Client-side validation for all fields
- Real-time error clearing on user input
- Character counter for reason field
- Success state shows unique request code
- Role descriptions in info panel
- Responsive layout (stacks on mobile)
E. API Logic Created
Files
- src/app/api/access-requests/route.ts – POST /api/access-requests endpoint
- src/lib/validation/schemas.ts – Zod schema for request validation
Endpoint Details
POST /api/access-requests
-
Request:
{ "fullName": "string", "workEmail": "string", "department": "string", "requestedRole": "HR|MANAGER|SMO|ADMIN", "reason": "string (min 20 chars)", "managerEmail": "string (optional)" } -
Response (Success – 201):
{ "requestCode": "REQ-2026-12345", "status": "PENDING" } -
Response (Error):
- 400: Validation error
{ "error": "Validation failed", "details": { ... } } - 409: Duplicate user or pending request
{ "error": "You already have an account. Try Forgot Password." } // OR { "error": "You already have a pending request." } - 500: Server error
- 400: Validation error
Server-side Flow
- Parse and validate request body against Zod schema
- Normalize email to lowercase
- Check if user exists with that email → 409 if yes
- Check if PENDING access request exists for that email → 409 if yes
- Generate unique request code (REQ-YYYY-#####)
- Create AccessRequest with status = PENDING
- Log ACCESS_REQUEST_CREATED audit event
- Return 201 with requestCode + status
F. RBAC Checks
/request-accessis public (no authentication required)POST /api/access-requestsis public (no authentication required)- Both endpoints accept requests from any user (including unauthenticated)
G. Audit Events
ACCESS_REQUEST_CREATED
{
"eventType": "ACCESS_REQUEST_CREATED",
"userId": null,
"details": {
"requestCode": "REQ-2026-12345",
"workEmail": "user@example.com",
"accessRequestId": "cuid..."
},
"createdAt": "2026-01-23T..."
}
Note: userId is null for public actions; future wireframes (W3, W16) will log admin actions with actual userId.
H. Test Checklist
Setup
- Create
.env.localwith DATABASE_URL and JWT_SECRET (if not already done) - Run
npx prisma migrate dev --name add_access_requests - Run
npm run devto start development server
Manual Tests
Basic Form Validation
- Navigate to
http://localhost:3000/request-access - Try submitting empty form → shows "X is required" errors
- Enter invalid email → shows "Please enter a valid email"
- Enter reason < 20 chars → shows "Reason must be at least 20 characters"
- Enter invalid manager email → shows "Please enter a valid manager email"
- Verify character counter updates as you type reason
Successful Submission
- Fill form with valid data:
- Full Name: "John Doe"
- Work Email: "john@example.com"
- Department: "Engineering"
- Requested Role: "Hiring Manager"
- Reason: "I need to evaluate candidates for our engineering team"
- Manager Email: (leave blank)
- Click Submit Request
- Verify success page shows:
- "Request Submitted" message
- Reference ID like "REQ-2026-#####"
- "You'll receive an email once approved"
- "← Back to Sign In" link works
Duplicate Handling
- Submit request for email: test@example.com
- Try submitting again with same email → error "You already have a pending request."
- Verify database shows PENDING request
Existing User Handling
- Try submitting with an email that already has a User account (e.g., admin@example.com from seed)
- Verify error: "You already have an account. Try Forgot Password."
Responsive Design
- Test on mobile/tablet → form and info panel stack vertically
- Test on desktop → form on left (2/3), info panel on right (1/3)
Database Verification
# Open Prisma Studio
npx prisma studio
# Verify AccessRequest table
# - Check requestCode is unique (REQ-YYYY-#####)
# - Check all submitted requests have status PENDING
# - Check workEmail is lowercase
# Verify AuditLog table
# - Check ACCESS_REQUEST_CREATED events exist
# - Check userId is null for public actions
# - Check details contains requestCode and workEmail
API Direct Testing (curl/Postman)
# Valid request
curl -X POST http://localhost:3000/api/access-requests \
-H "Content-Type: application/json" \
-d '{
"fullName": "Jane Smith",
"workEmail": "jane@example.com",
"department": "HR",
"requestedRole": "HR",
"reason": "I need access to review candidate resumes and screen applications",
"managerEmail": "manager@example.com"
}'
# Should return 201 with requestCode
# Duplicate request
curl -X POST http://localhost:3000/api/access-requests \
-H "Content-Type: application/json" \
-d '{"fullName": "Jane Smith", ...}'
# Should return 409 with error message
# Invalid data
curl -X POST http://localhost:3000/api/access-requests \
-H "Content-Type: application/json" \
-d '{"fullName": "", ...}'
# Should return 400 with validation details
Next Steps (W3)
- Implement W3: Admin Access Requests approval interface
- Add email notifications for pending requests
- Add decision workflow (approve/reject)
Code Structure Summary
src/
├── app/
│ ├── api/
│ │ └── access-requests/
│ │ └── route.ts (POST endpoint)
│ └── request-access/
│ └── page.tsx (public page)
├── components/
│ └── RequestAccessForm.tsx (form component)
└── lib/
├── audit.ts (updated for nullable userId)
├── validation/
│ └── schemas.ts (Zod validation)
└── ... (existing auth, password, prisma files)
prisma/
├── schema.prisma (updated with AccessRequest + enums)
└── migrations/
└── [timestamp]_add_access_requests/