# Users uniquely identified before access

- **Pattern:** `ab-001554` (`gov-cmmc-level-1.identification-auth.user-identification`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001554
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001554)

## Why it matters

CMMC 2.0 IA.L1-3.5.1 (NIST 800-171r2 3.5.1) requires that users and processes are uniquely identified before system access is granted. Without a uniqueness constraint on user identifiers, two accounts can share the same identity — making it impossible to attribute actions to a specific individual. CMMC accountability requires an audit trail tied to unique principals. CWE-287 (Improper Authentication) and OWASP A07 (Identification and Authentication Failures) flag non-unique identification as a foundational authentication failure. A system that cannot distinguish between two users sharing an email cannot satisfy CMMC's accountability requirements, regardless of how well other controls are implemented.

## Severity rationale

Critical because non-unique user identifiers make individual accountability impossible — a foundational CMMC requirement that cannot be compensated by other controls.

## Remediation

Enforce unique identification at the database level, not just the application layer. Application-level checks can be bypassed; database constraints cannot:

```prisma
// prisma/schema.prisma
model User {
  id        String   @id @default(cuid())
  email     String   @unique   // Database-enforced — not nullable, not optional
  role      String   @default("user")
  createdAt DateTime @default(now())
}
```

For raw SQL migrations:

```sql
CREATE TABLE users (
  id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email      TEXT NOT NULL UNIQUE,
  role       TEXT NOT NULL DEFAULT 'user',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX idx_users_email ON users(email);
```

In the registration handler, catch unique-constraint violations and return a client-safe message — do not forward the raw database error, which would expose your schema and table names.

## Detection

- **ID:** `user-identification`
- **Severity:** `critical`
- **CMMC Practice:** IA.L1-3.5.1
- **What to look for:** Examine the user model, database schema, and registration flow. Verify that each user receives a unique identifier (UUID, auto-increment ID, or email with a unique constraint). Look for database schema files (Prisma schema, Drizzle schema, SQL migrations) and confirm uniqueness constraints on the user identifier column. Check for any patterns that would allow shared or generic accounts — look for hard-coded demo users, shared test credentials committed to code, or registration flows that don't enforce unique emails. Search for any `INSERT` or `CREATE` patterns that don't enforce uniqueness.
- **Pass criteria:** Count all user identifier fields in the database schema and verify each has a uniqueness constraint. Every user receives a unique identifier (UUID preferred, or email with a unique database constraint). At least 1 unique constraint exists on user identifiers. Report: "X user identifier fields found, Y have uniqueness constraints."
- **Fail criteria:** No uniqueness constraint on user identifiers. Shared accounts enabled by design. Multiple users can register with the same email without error. No user model found despite the application having access control.
- **Skip (N/A) when:** Never — unique identification is the foundation of accountability.
- **Detail on fail:** Specify the identification gap. Example: `"User schema in prisma/schema.prisma has no @unique constraint on email field. Two users can register with identical emails — impossible to identify actions by individual."` Keep under 500 characters.
- **Remediation:** Enforce unique user identification at the database level:

  ```prisma
  // prisma/schema.prisma
  model User {
    id        String   @id @default(cuid())
    email     String   @unique          // Unique constraint is non-negotiable
    role      String   @default("user")
    createdAt DateTime @default(now())
    updatedAt DateTime @updatedAt
  }
  ```

  For SQL migrations, add a unique index explicitly:

  ```sql
  -- migrations/001_create_users.sql
  CREATE TABLE users (
    id      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email   TEXT NOT NULL UNIQUE,
    role    TEXT NOT NULL DEFAULT 'user',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  );

  CREATE UNIQUE INDEX idx_users_email ON users(email);
  ```

  In the registration handler, catch duplicate key errors and return an appropriate response rather than leaking database error details to the client.

## External references

- cmmc IA.L1-3.5.1
- cwe CWE-287
- owasp A07
- nist SP-800-171 3.5.1

Taxons: access-control, regulatory-conformance

HTML version: https://auditbuffet.com/patterns/ab-001554
