4IT580: Docs
4IT580 WebGitLab

Backend Finalization: Authentication, Permissions & Logging

5. Adding User Ownership to Books and Authors

Before implementing permissions, we need to add user ownership to our Book and Author models. This allows us to track who created each book and author, which is essential for permission checking.

Step 1: Update Prisma Schema

Add user relationships to Book and Author models:

Step 2: Generate Prisma Client and Migration

After updating the Prisma schema, generate the Prisma client and create a migration:

Important: Don't run the seed script yet - the seed data needs to be updated first to include user IDs.

Step 3: Update Domain Classes

Add field to your domain classes:

Step 4: Create Mappers for Prisma to Domain Conversion

To keep our repository code clean and avoid repetition, create mapper functions for converting Prisma models to domain objects:

Why use mappers?

Step 5: Update Repositories

Now use the mappers in your repositories:

Note: By accepting the full object instead of just a string, the repository has access to the complete user context (email, name, role) if needed for logging, auditing, or permission checks.

Step 6: Update Services

Pass the full object to repository methods. Even though we won't use the parameter in all methods yet, we add it for consistency and future permission checks:

Pattern: Services receive the from resolvers and pass it through to repositories. This maintains a clean separation of concerns and allows repositories to access user context. We add the parameter to update and delete methods even though we don't use them yet - this makes it easier to add permission checks later.

Step 7: Update Resolvers

You will first have to import the AuthModule in both BookModule and AuthorModule:

Do the same for BooksModule:

Add authentication guards and pass the full object:

Key Points:

Step 8: Update Seed Data

Now update your seed functions to include user relationships (this must be done before running the seed):

Then update your main seed script to pass user IDs when creating books and authors:

Note: Seed functions use plain strings since they operate outside the normal request context and don't need the full object. This is acceptable for seeding data.

Step 9: Run the Seed Script

After updating all the seed functions and the seed database script, you can now run the seed:

This will populate your database with books and authors that are properly associated with users.

6. Extending the Permissions Module

Now let's implement proper permissions for your modules. We'll follow the pattern used in the quacks module.

Understanding the Permissions Pattern

The permissions system uses an Ability Factory pattern that:

  1. Creates permission rules based on user identity
  2. Checks permissions in services before performing actions
  3. Throws appropriate exceptions when permissions are denied

Step 1: Update Your Ability Factory

Update the ability factory to include permissions for your books and authors modules. Here's the complete file:

Step 2: Import PermissionsModule

Now import the PermissionsModule in your modules to use the AbilityFactory and AuthModule to enable our auth guard:

Do the same for BooksModule:

Step 3: Use Permissions in Services

Implement permission checks in your service layer:

Key Changes:

Similarly, you can add permission checks to BookService following the same pattern.

Note: Resolvers are already protected with guards (covered in Step 7 of "Adding User Ownership"), so you don't need to add them again.

Step 4: Test Your Permissions

Test different permission scenarios using GraphQL:

6. Better Auth

1. Installing latest version

First, we need to update better-auth to the latest version as the template projects contain an outdated version.

After installation, verify the version in both files. It should be the latest version in both (>= 1.3.28).

2. Understanding better-auth

What is better-auth?

better-auth is a comprehensive authentication solution that handles all the complexity of modern authentication for you. It's a wrapper that manages:

How It Works

better-auth uses a schema-based approach where it:

  1. Creates database tables automatically - You don't need to define user, session, or verification tables
  2. Handles all authentication endpoints - Login, signup, logout, password reset, email verification
  3. Manages cookies and sessions - Secure, httpOnly cookies with automatic refresh
  4. Provides type-safe client - Full TypeScript support for frontend integration

The key benefit is that you don't have to worry about:

better-auth handles all of this behind the scenes, letting you focus on your application logic.

Database Tables Created by better-auth

When you run migrations with better-auth configured, it automatically creates these tables:

Based on which better-auth plugins you use, you might need to add additional fields to these tables or create new tables.

3. Implementing Email Verification

Backend Configuration

To require email verification, you need to update the better-auth configuration:

Important: Set the "from" address in SMTP adapter

By default, the SMTP adapter uses a placeholder sender address. Update it to an address that belongs to your domain (or your SMTP provider may reject or spam‑flag emails).

Fix: betterAuth config ordering

Previously, we spread too low inside , which could override your custom fields. To avoid overrides, spread it at the very top of the options object so anything defined below wins.

Incorrect (core config at the bottom can override your values):

Correct (core config first, then layer customizations):

This is a mistake on our part, sorry for the inconvenience.

4. Understanding better-auth Plugins

What Are Plugins?

better-auth has a powerful plugin system that extends its core functionality. Plugins can:

Plugins are completely optional but highly recommended for features like:

Using plugins saves you from writing all this code yourself - you can simply use better-auth's API endpoints and hooks.

Exploring Available APIs

All available better-auth endpoints are documented at:

This OpenAPI documentation shows all the authentication endpoints that better-auth provides. The frontend client handles most of these under the hood, so you rarely need to call them directly from the backend.

Understanding Core Config vs Provider Config

Our NestJS integration uses a two-file approach to handle better-auth configuration:

1. Core Config ()

This file contains the minimal configuration needed for database schema generation:

When to add plugins here:

2. Provider Config ()

This file contains the full runtime configuration that actually runs in your application:

When to add configuration here:

Why This Two-File Approach?

This is a slightly hacky solution to solve a specific problem:

  1. needs a standalone better-auth instance to read the schema
  2. But our runtime configuration needs NestJS dependency injection
  3. So we split: core config for schema generation, provider config for runtime

Alternative Approach (Simpler):

If this two-file approach is confusing, you can skip entirely:

  1. Delete
  2. Put everything in
  3. Manually add database tables/columns from plugin documentation to your Prisma schema
  4. Don't use

This is actually the recommended approach for most projects - it's more explicit and less magical.

Adding a Plugin (Manual Method)