# Courrier User Management with Prisma ## Overview **Important**: Courrier (the email system) does **NOT** create User records in Prisma. It only manages email account credentials (`MailCredentials`) for users that already exist in the database. ## User Creation Flow ### 1. User Creation in Keycloak (Primary Source) Users are created in **Keycloak** first, which is the primary authentication system: **Location**: `app/api/users/route.ts` (POST method) **Process**: 1. User is created in Keycloak via Admin API 2. Roles are assigned to the user 3. User may be created in external systems: - **Leantime** (project management tool) - **Dolibarr** (if user has "Mediation" or "Expression" roles) **Key Code**: ```typescript // Create user in Keycloak const createResponse = await fetch( `${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ username: data.username, enabled: true, emailVerified: true, firstName: data.firstName, lastName: data.lastName, email: data.email, credentials: [{ type: "password", value: data.password, temporary: false }], }), } ); ``` ### 2. User Sync to Prisma Database After creation in Keycloak, users need to be synced to the Prisma database. This happens via: **Option A: Manual Sync Script** - `scripts/sync-users.ts` or `scripts/sync-users.js` - Fetches users from Keycloak API - Creates/updates User records in Prisma **Option B: API Endpoint** - `app/api/sync-users/route.ts` (GET method) - Can be called to sync users programmatically **Prisma User Creation**: ```typescript await prisma.user.create({ data: { id: user.id, // Use the Keycloak ID as primary ID email: user.email, password: tempPassword, // Temporary password (not used for auth) createdAt: new Date(), updatedAt: new Date(), }, }); ``` **Important Notes**: - The Prisma User `id` field uses the **Keycloak user ID** (UUID) - The `password` field in Prisma is not used for authentication (Keycloak handles that) - Users must exist in Prisma before they can use Courrier ### 3. Prisma Schema **User Model** (`prisma/schema.prisma`): ```prisma model User { id String @id @default(uuid()) email String @unique password String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt mailCredentials MailCredentials[] // One-to-many relationship // ... other relations } ``` **MailCredentials Model**: ```prisma model MailCredentials { id String @id @default(uuid()) userId String email String password String? // Optional (for OAuth accounts) host String port Int secure Boolean @default(true) use_oauth Boolean @default(false) refresh_token String? access_token String? token_expiry DateTime? smtp_host String? smtp_port Int? smtp_secure Boolean? @default(false) display_name String? color String? @default("#0082c9") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([userId, email]) // One email account per user @@index([userId]) } ``` ## Courrier's Role: Adding Email Accounts ### How Courrier Adds Email Accounts **Location**: `app/api/courrier/account/route.ts` (POST method) **Process**: 1. **Authentication Check**: Verifies user session exists 2. **User Existence Check**: Verifies user exists in Prisma database 3. **Connection Test**: Tests IMAP connection before saving 4. **Save Credentials**: Creates/updates `MailCredentials` record **Key Code Flow**: ```typescript // 1. Check if user exists in database const userExistsInDB = await userExists(session.user.id); if (!userExistsInDB) { return NextResponse.json({ error: 'User not found in database', details: `The user ID from your session (${session.user.id}) doesn't exist in the database.` }, { status: 400 }); } // 2. Test connection const testResult = await testEmailConnection(credentials); if (!testResult.imap) { return NextResponse.json({ error: `Connection test failed: ${testResult.error}` }, { status: 400 }); } // 3. Save credentials await saveUserEmailCredentials(session.user.id, email, credentials); ``` ### Saving Email Credentials **Location**: `lib/services/email-service.ts` → `saveUserEmailCredentials()` **Process**: 1. Prepares database credentials object (excluding OAuth tokens) 2. Uses `upsert` to create or update `MailCredentials` 3. Caches full credentials (including OAuth tokens) in Redis **Key Code**: ```typescript // Save to database using upsert await prisma.mailCredentials.upsert({ where: { // Finds existing record by userId + email userId_email: { userId: userId, email: credentials.email } }, update: dbCredentials, create: { userId, ...dbCredentials } }); // Cache full credentials (including OAuth) in Redis await cacheEmailCredentials(userId, accountId, fullCreds); ``` **Important Notes**: - OAuth tokens (access_token, refresh_token) are stored in **Redis only**, not in Prisma - The Prisma `MailCredentials` table stores IMAP/SMTP settings - The `password` field is optional (for OAuth accounts like Microsoft) ### Microsoft OAuth Flow **Location**: `app/api/courrier/microsoft/callback/route.ts` For Microsoft accounts, the flow is: 1. User authorizes via Microsoft OAuth 2. Access token and refresh token are obtained 3. Credentials are saved with `use_oauth: true` 4. OAuth tokens are cached in Redis (not in Prisma) ## Data Flow Diagram ``` ┌─────────────┐ │ Keycloak │ ← Primary user creation └──────┬──────┘ │ │ Sync ↓ ┌─────────────┐ │ Prisma │ ← User record created │ User │ └──────┬──────┘ │ │ User adds email account ↓ ┌─────────────┐ │ Prisma │ ← MailCredentials created │MailCredentials│ └──────┬──────┘ │ │ OAuth tokens (if applicable) ↓ ┌─────────────┐ │ Redis │ ← OAuth tokens cached └─────────────┘ ``` ## Key Files Reference ### User Creation - `app/api/users/route.ts` - Creates users in Keycloak - `scripts/sync-users.ts` - Syncs users from Keycloak to Prisma - `app/api/sync-users/route.ts` - API endpoint for syncing users ### Courrier Email Management - `app/api/courrier/account/route.ts` - Add/update/delete email accounts - `lib/services/email-service.ts` - Core email service functions - `saveUserEmailCredentials()` - Saves email credentials to Prisma - `getUserEmailCredentials()` - Retrieves credentials from Prisma - `testEmailConnection()` - Tests IMAP/SMTP connection ### Database Schema - `prisma/schema.prisma` - Prisma schema definitions - `lib/prisma.ts` - Prisma client instance ### Authentication - `app/api/auth/options.ts` - NextAuth configuration - `lib/auth.ts` - Authentication helpers ## Auto-Creation of Users **As of recent updates**, Courrier now automatically creates User records in Prisma if they don't exist when: - Adding an email account (`/api/courrier/account` POST) - Checking session status (`/api/courrier/session` GET) This handles cases where: - The database was reset/lost but users still exist in Keycloak - Users were created in Keycloak but never synced to Prisma The auto-creation uses session data from Keycloak to populate: - `id`: Keycloak user ID (UUID) - `email`: User's email from session - `password`: Temporary random password (not used for auth, Keycloak handles authentication) ## Common Issues & Solutions ### Issue: "User not found in database" when adding email account **Cause**: User exists in Keycloak but not in Prisma database **Solution**: - **Automatic**: The system now auto-creates users when needed - **Manual**: Run the sync script to create users in Prisma: ```bash npm run sync-users # or node scripts/sync-users.js ``` ### Issue: Email credentials not saving **Check**: 1. User exists in Prisma: `prisma.user.findUnique({ where: { id: userId } })` 2. Connection test passes before saving 3. Unique constraint `[userId, email]` is not violated ### Issue: OAuth tokens not persisting **Note**: OAuth tokens are stored in Redis, not Prisma. Check: - Redis connection and TTL settings - Redis cache functions in `lib/redis.ts` ## Summary 1. **Users are created in Keycloak first** (via `app/api/users/route.ts`) 2. **Users are synced to Prisma** (via sync scripts or API) 3. **Courrier adds email accounts** by creating `MailCredentials` records linked to existing Users 4. **OAuth tokens are cached in Redis**, not stored in Prisma 5. **Users must exist in Prisma** before they can add email accounts via Courrier Courrier is a **credentials management system** for existing users, not a user creation system.