# Validation Report: URS-001

**Title:** Secure Login with Unique User Credentials
**Date:** 2026-04-23T03:33:41.949Z
**Duration:** 91.9s
**Overall Status:** ✅ PASS

## User Requirement

> The system shall provide secure login using unique user credentials.

*Source: `User_Requirement_Specifications_ZuriMED_DeviceFlow.xlsx` — the run below proves the system meets this requirement.*

## Environment

- **Inbox URL:** http://localhost:61662
- **Database:** localhost:61663/cc_repinbox_dev

## Setup

Status: ✅ PASS

## Test Steps

Each step below corresponds to one Playwright test that ran sequentially. Screenshots and video recordings provide visual evidence of the UI behaviour.

### 1. Step 1: Login page loads correctly — ✅ PASS

**What this step proves:**

The system presents a login form with email and password fields and a password reset link. This confirms the authentication entry point is functional and that all users are directed to credential-based login rather than any unauthenticated route.

**Screenshots:**

![step 01 login page](screenshots/step-01-login-page.png)

**Video recording:**

[▶ Watch step recording](videos/step-01-login-page.webm)

---

### 2. Step 2: Valid login - Distributor user — ✅ PASS

**What this step proves:**

A distributor user authenticates successfully with correct credentials. The system validates the credentials, creates a session, and redirects the user to the authenticated application. This demonstrates that valid credentials grant access as required by URS-001.

**Audit events generated by this step:**

*(Evidence matched by declared name — step timing not available or no events fell in window)*

| Time | Type | Action | User | Org | Performed |
|------|------|--------|------|-----|-----------|
| 2026-04-23 03:33:52Z | user_log | user:login | dan.distributor@stellartech.com | StellarTech Medical Solutions | — |
| 2026-04-23 03:33:59Z | user_log | user:login | mark.manufacturer@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:06Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:50Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:57Z | user_log | user:login | dan.distributor@stellartech.com | StellarTech Medical Solutions | — |
| 2026-04-23 03:34:58Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:35:06Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |

**Screenshots:**

![step 02 distributor logged in](screenshots/step-02-distributor-logged-in.png)

**Video recording:**

[▶ Watch step recording](videos/step-02-valid-login-distributor.webm)

---

### 3. Step 3: Valid login - Manufacturer user — ✅ PASS

**What this step proves:**

A manufacturer-type user authenticates successfully, confirming that the secure credential flow handles multiple organization types uniformly. No special path exists for different org types.

**Audit events generated by this step:**

*(Evidence matched by declared name — step timing not available or no events fell in window)*

| Time | Type | Action | User | Org | Performed |
|------|------|--------|------|-----|-----------|
| 2026-04-23 03:33:52Z | user_log | user:login | dan.distributor@stellartech.com | StellarTech Medical Solutions | — |
| 2026-04-23 03:33:59Z | user_log | user:login | mark.manufacturer@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:06Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:50Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:57Z | user_log | user:login | dan.distributor@stellartech.com | StellarTech Medical Solutions | — |
| 2026-04-23 03:34:58Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:35:06Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |

**Screenshots:**

![step 03 manufacturer logged in](screenshots/step-03-manufacturer-logged-in.png)

**Video recording:**

[▶ Watch step recording](videos/step-03-valid-login-manufacturer.webm)

---

### 4. Step 4: Valid login - Admin user — ✅ PASS

**What this step proves:**

An admin user authenticates successfully, confirming that all system roles use the same credential-based authentication mechanism.

**Audit events generated by this step:**

*(Evidence matched by declared name — step timing not available or no events fell in window)*

| Time | Type | Action | User | Org | Performed |
|------|------|--------|------|-----|-----------|
| 2026-04-23 03:33:52Z | user_log | user:login | dan.distributor@stellartech.com | StellarTech Medical Solutions | — |
| 2026-04-23 03:33:59Z | user_log | user:login | mark.manufacturer@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:06Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:50Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:34:57Z | user_log | user:login | dan.distributor@stellartech.com | StellarTech Medical Solutions | — |
| 2026-04-23 03:34:58Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |
| 2026-04-23 03:35:06Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — |

**Screenshots:**

![step 04 admin logged in](screenshots/step-04-admin-logged-in.png)

**Video recording:**

[▶ Watch step recording](videos/step-04-valid-login-admin.webm)

---

### 5. Step 5: Invalid credentials - Wrong password — ✅ PASS

**What this step proves:**

Attempting to log in with a valid registered email but an incorrect password is rejected. The system returns a generic error message and keeps the user on the login page. The exact error text is captured for comparison in Step 6 to verify protection against email enumeration.

**Screenshots:**

![step 05 wrong password error](screenshots/step-05-wrong-password-error.png)

**Video recording:**

[▶ Watch step recording](videos/step-05-wrong-password.webm)

---

### 6. Step 6a: Invalid credentials - Non-existent email — ✅ PASS

**What this step proves:**

Attempting to log in with an email address that has never been registered returns the exact same error message as Step 5. Identical responses for wrong-password and unknown-email requests prevent attackers from determining whether a given email address exists in the system.

**Screenshots:**

![step 06 nonexistent email error](screenshots/step-06-nonexistent-email-error.png)

**Video recording:**

[▶ Watch step recording](videos/step-06-nonexistent-email.webm)

---

### 7. Step 6b: Locked account - inactive user cannot login — ✅ PASS

**What this step proves:**

A user whose organization membership has been deactivated is blocked from logging in even when submitting the correct credentials. This confirms that deactivating a user account takes effect immediately and that credential validity alone is insufficient for access.

**Screenshots:**

![step 06b locked account error](screenshots/step-06b-locked-account-error.png)

**Video recording:**

[▶ Watch step recording](videos/step-06b-locked-account.webm)

---

### 8. Step 7: Empty field validation — ✅ PASS

**What this step proves:**

Submitting the login form with empty fields triggers validation errors before any server request is made, displaying field-level messages such as "Invalid email" and "Password is required". This confirms that required-field enforcement provides clear feedback and prevents malformed requests.

**Screenshots:**

![step 07 empty both fields](screenshots/step-07-empty-both-fields.png)

![step 07 empty password](screenshots/step-07-empty-password.png)

**Video recording:**

[▶ Watch step recording](videos/step-07-empty-fields.webm)

---

### 9. Step 8: Session security - Cookie verification — ✅ PASS

**What this step proves:**

After successful login the session cookie attributes are inspected. The httpOnly flag confirms that JavaScript cannot read the token, mitigating XSS-based session theft. The SameSite=Lax setting blocks cross-site request forgery. Cookie evidence is written to session-cookie-evidence.json.

**Screenshots:**

![step 08 session security](screenshots/step-08-session-security.png)

**Video recording:**

[▶ Watch step recording](videos/step-08-session-security.webm)

---

### 10. Step 9: Session isolation - Multiple users — ✅ PASS

**What this step proves:**

Two users log in simultaneously in separate browser contexts (equivalent to separate incognito windows) and each receives a distinct, non-overlapping session token. This confirms that sessions are fully isolated and one user's session cannot be used to access another user's account.

**Screenshots:**

![step 09 session a distributor](screenshots/step-09-session-a-distributor.png)

![step 09 session b admin](screenshots/step-09-session-b-admin.png)

**Video recording:**

[▶ Watch step recording](videos/step-09-session-isolation-a.webm)

---

### 11. Step 10: Logout and session termination — ✅ PASS

**What this step proves:**

A logged-in user opens the sidebar user menu and clicks Logout. The server invalidates the session, clears the session cookie, and redirects the browser. A subsequent attempt to access /inbox confirms that the session can no longer authenticate: the user is redirected to login.

**Screenshots:**

![step 10 before logout](screenshots/step-10-before-logout.png)

![step 10 after logout](screenshots/step-10-after-logout.png)

![step 10 protected route redirect](screenshots/step-10-protected-route-redirect.png)

**Video recording:**

[▶ Watch step recording](videos/step-10-logout.webm)

---

## Database Validations

The following SQL queries ran against the application database after the Playwright scenarios completed. Each query asserts a specific condition that proves the feature under test persisted its data correctly.

### Demo users exist — ✅ PASS

**Assertion:** All 4 demo users should exist in the users table

```sql
SELECT id, email, name, created_at
      FROM users
      WHERE email IN (
        'alex.admin@zurimed.com',
        'mark.manufacturer@zurimed.com',
        'dan.distributor@stellartech.com',
        'demo.user@zurimed.com'
      )
      ORDER BY email
```

| id | email | name | created_at |
| --- | --- | --- | --- |
| f6a7b8c9-d0e1-2345-f123-456789012345 | alex.admin@zurimed.com | Alex Admin | 2026-04-23T02:44:53.474Z |
| c3d4e5f6-a7b8-9012-cdef-123456789012 | dan.distributor@stellartech.com | Dan Distributor | 2026-04-23T02:44:53.474Z |
| e5f6a7b8-c9d0-1234-ef12-345678901234 | demo.user@zurimed.com | Demo User | 2026-04-23T02:44:53.474Z |
| d4e5f6a7-b8c9-0123-def1-234567890123 | mark.manufacturer@zurimed.com | Mark Manufacturer | 2026-04-23T02:44:53.474Z |

### No duplicate emails — ✅ PASS

**Assertion:** No email address should appear more than once in the users table

```sql
SELECT email, COUNT(*) as count
      FROM users
      GROUP BY email
      HAVING COUNT(*) > 1
```

*No rows returned*

### Passwords are hashed — ✅ PASS

**Assertion:** All password hashes should use Argon2id algorithm (not plaintext)

```sql
SELECT id, email,
        LEFT(password_hash, 10) as hash_prefix,
        password_hash LIKE '$argon2id$%' as is_argon2
      FROM users
      WHERE email IN (
        'alex.admin@zurimed.com',
        'mark.manufacturer@zurimed.com',
        'dan.distributor@stellartech.com',
        'demo.user@zurimed.com'
      )
      ORDER BY email
```

| id | email | hash_prefix | is_argon2 |
| --- | --- | --- | --- |
| f6a7b8c9-d0e1-2345-f123-456789012345 | alex.admin@zurimed.com | $argon2id$ | true |
| c3d4e5f6-a7b8-9012-cdef-123456789012 | dan.distributor@stellartech.com | $argon2id$ | true |
| e5f6a7b8-c9d0-1234-ef12-345678901234 | demo.user@zurimed.com | $argon2id$ | true |
| d4e5f6a7-b8c9-0123-def1-234567890123 | mark.manufacturer@zurimed.com | $argon2id$ | true |

### Recent sessions created — ✅ PASS

**Assertion:** Sessions should have been created within the last 10 minutes for users who logged in during the test

```sql
SELECT s.id as session_id, u.email, s.created_at, s.expires_at
      FROM session s
      JOIN users u ON s.user_id = u.id
      WHERE u.email IN (
        'alex.admin@zurimed.com',
        'mark.manufacturer@zurimed.com',
        'dan.distributor@stellartech.com'
      )
      AND s.created_at > NOW() - INTERVAL '10 minutes'
      ORDER BY s.created_at DESC
      LIMIT 10
```

| session_id | email | created_at | expires_at |
| --- | --- | --- | --- |
| 370952fde4ea3c8c4e87d89019e8baf9ee1c4ec8a8b5937ca38430f7d282b696 | alex.admin@zurimed.com | 2026-04-23T03:34:58.960Z | 2026-05-23T03:34:58.956Z |
| d786cad8127a4714bdc7f9f78b063cf67e9f35584de478c03bb216aa014cd303 | dan.distributor@stellartech.com | 2026-04-23T03:34:57.649Z | 2026-05-23T03:34:57.646Z |
| 3e39cf0b2d2aa390637548cbd09f73afb0d4cda3aa848478de746260c55b4a38 | alex.admin@zurimed.com | 2026-04-23T03:34:50.181Z | 2026-05-23T03:34:50.178Z |
| 8b27bbd681daa18e9ccb495e372a745e8badcc85fc054b84d5d5eab4910a1314 | alex.admin@zurimed.com | 2026-04-23T03:34:06.729Z | 2026-05-23T03:34:06.727Z |
| cfc932e46aa3480b74f988caa91e97e1133c39dedf01f66b480d6fc7fb39ecdc | mark.manufacturer@zurimed.com | 2026-04-23T03:33:59.805Z | 2026-05-23T03:33:59.796Z |
| 97b8f110272dbe9989e4d4498df2a3ecb034a28280537462397d30d465c3ac2e | dan.distributor@stellartech.com | 2026-04-23T03:33:52.655Z | 2026-05-23T03:33:52.646Z |

### Locked user is inactive — ✅ PASS

**Assertion:** demo.user@zurimed.com should have inactive org membership (locked account)

```sql
SELECT u.email, om.active, om.organization_id
      FROM organization_members om
      JOIN users u ON om.user_id = u.id
      WHERE u.email = 'demo.user@zurimed.com'
```

| email | active | organization_id |
| --- | --- | --- |
| demo.user@zurimed.com | false | a1b2c3d4-e5f6-7890-abcd-ef1234567890 |
| demo.user@zurimed.com | false | b2c3d4e5-f6a7-8901-bcde-f12345678901 |
| demo.user@zurimed.com | false | 6763fc17-7da1-47e3-851e-8f4fac570dc6 |

### Global email uniqueness — ✅ PASS

**Assertion:** Every user should have a unique email address

```sql
SELECT COUNT(DISTINCT email) as unique_emails, COUNT(*) as total_users
      FROM users
      WHERE email IS NOT NULL
```

| unique_emails | total_users |
| --- | --- |
| 12 | 12 |

## Audit & Email Assertion Ledger

Per-declaration outcome of every `expectedAuditActions` and `expectedEmailTemplates` entry written into the orchestrator. Missing evidence here is a real test failure, not a soft warning.

### Audit Action Assertions

Each row asserts that a declared `expectedAuditActions` entry produced a matching row in `audit_events`. A ❌ flips overall status to FAIL — the declaration is real proof, not just an annotation.

| Step | Expected Audit Action | Found |
|------|-----------------------|-------|
| Step 2: Valid login - Distributor user | `user_log:user:login` | ✅ |
| Step 3: Valid login - Manufacturer user | `user_log:user:login` | ✅ |
| Step 4: Valid login - Admin user | `user_log:user:login` | ✅ |

## Audit Log Events

Every row written to `audit_events` while this test was running (scoped to the demo organizations). Provides compliance evidence that user actions are traced end-to-end (URS-003).

**Capture window start:** 2026-04-23T03:33:40.054Z

<details><summary>Query used to capture events</summary>

```sql
SELECT
    ae.created_at,
    ae.event_type,
    ae.action,
    ae.user_id,
    u.email AS user_email,
    ae.organization_id,
    o.name AS organization_name,
    ae.object_id,
    ae.secondary_object_id,
    ae.payload,
    ae.route,
    ae.trace_id
  FROM audit_events ae
  LEFT JOIN users u ON u.id = ae.user_id
  LEFT JOIN organizations o ON o.id = ae.organization_id
  WHERE ae.created_at >= $1
    AND ae.organization_id = ANY($2::uuid[])
  ORDER BY ae.created_at ASC
```
</details>

8 event(s) captured:

| Time | Type | Action | User | Org | Object ID | Performed | Reason |
|------|------|--------|------|-----|-----------|-----------|--------|
| 2026-04-23 03:33:52Z | user_log | user:login | dan.distributor@stellartech.com | StellarTech Medical Solutions | — | — |  |
| 2026-04-23 03:33:59Z | user_log | user:login | mark.manufacturer@zurimed.com | ZuriMED | — | — |  |
| 2026-04-23 03:34:06Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — | — |  |
| 2026-04-23 03:34:33Z | user_log | user:login_deactivated | demo.user@zurimed.com | ZuriMED | — | — |  |
| 2026-04-23 03:34:50Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — | — |  |
| 2026-04-23 03:34:57Z | user_log | user:login | dan.distributor@stellartech.com | StellarTech Medical Solutions | — | — |  |
| 2026-04-23 03:34:58Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — | — |  |
| 2026-04-23 03:35:06Z | user_log | user:login | alex.admin@zurimed.com | ZuriMED | — | — |  |
