# Auth Error Page — PRD

## Status

Draft

## Executive Summary

When Auth0 authentication fails, ows-coda users are stranded — they see a blank page, an infinite redirect loop, or a cryptic error with no way to recover. This PRD defines a dedicated `AuthErrorPage` component that replaces those failure modes with a clear error message, a human-readable error code, and a one-click retry button, turning a dead end into a recoverable moment.

## The Problem

Authentication is the front door to ows-coda. It is the very first experience every user has with the product, every single time they open it. And right now, when that front door jams, users are left standing outside in the dark.

Here is what actually happens today. A user opens ows-coda. Auth0 attempts its OAuth callback. Something goes wrong — maybe Auth0's servers hiccup and return `server_error`, maybe an admin misconfigured a connection and the callback comes back with `access_denied`, maybe the Auth0 SDK itself fails to initialize. The `isAuthenticated` flag stays false. The `useEffect` in the auth provider fires `loginWithRedirect()`. Auth0 fails again. The browser redirects. The provider fires again. An infinite loop. The user watches their browser churn through redirects until it gives up, leaving them on a blank page with no explanation.

A quick fix broke the redirect loop by checking for `authError` before calling `loginWithRedirect()`, and it renders a minimal inline fallback — raw text, no styling, no guidance. The loop is gone, but the experience is still broken. Users see a wall of developer-speak. They do not know if they typed their password wrong, if their account is locked, if the system is down, or if their laptop is haunted. They do not know what to do next. The most technically savvy ones will try clearing cookies or opening an incognito window. Everyone else will file a support ticket or, worse, walk away assuming the product is broken.

This matters disproportionately because auth is a gate. A user who hits a bug in the chat interface can still navigate around it, refresh, try again. A user who cannot get past authentication cannot use the product at all. Zero functionality. Zero value. And first impressions are stubborn — a user whose very first interaction with ows-coda is a cryptic error message will carry that doubt forward even after the issue resolves. Trust, once lost at the front door, is expensive to rebuild.

## The Opportunity

A polished auth error page turns a support-ticket-generating dead end into a self-service recovery flow. Specifically:

- **Self-recovery:** A clear retry button lets users resolve transient Auth0 failures (server errors, network blips) without IT intervention. Most auth errors are transient.
- **Reduced support burden:** When users can see what went wrong (in plain language) and try again themselves, support ticket volume for "I can't log in" drops.
- **Faster triage when support IS needed:** An error code badge (`server_error`, `access_denied`) gives users something concrete to share with support, replacing "it doesn't work" with actionable information.
- **Trust and polish:** A well-designed error page signals that the product team anticipated this failure and planned for it. That is the opposite of a blank page, which signals abandonment.

## Goals & Success Criteria

| Goal                                                     | Success Criteria                                                                                                     |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| Eliminate blank pages and redirect loops on auth failure | Auth errors render the `AuthErrorPage` component 100% of the time; no infinite loops under any auth failure scenario |
| Enable self-service recovery                             | Users can retry authentication with a single click; transient failures resolve without support involvement           |
| Provide actionable error context                         | Every auth error displays a human-readable message and, when available, a machine-readable error code                |
| Zero regression risk                                     | No changes to routing, providers, or mid-session error handling; the component is purely presentational              |
| Ship within a single sprint                              | Component creation and integration require only two files and no new dependencies                                    |

## User Stories

1. **As a user whose Auth0 callback fails with a transient server error,** I want to see a clear message explaining that something went wrong and a button to try again, so that I can recover without contacting IT or clearing my browser data.

2. **As a user whose access has been denied by an Auth0 rule or policy,** I want to see the specific error type (e.g., `access_denied`) so that I can communicate the issue precisely to my administrator and get it resolved quickly.

3. **As a user on a slow or unstable network where the Auth0 SDK fails to initialize,** I want to see an error page instead of a blank screen, so that I understand the app did not silently crash and I know to check my connection and retry.

4. **As a support engineer helping a user who cannot log in,** I want the user to be able to read me an error code from their screen, so that I can diagnose the issue without a screen share or guesswork.

5. **As a developer maintaining the auth flow,** I want auth error rendering to be encapsulated in a single component with a clear interface (`error` + `onRetry`), so that I can update the error page's design without touching the auth provider logic.

## Proposed Solution

A single presentational React component, `AuthErrorPage`, rendered by the existing `AuthContextProvider` when an auth error is present.

**Component:** `AuthErrorPage` at `client/src/components/auth-error-page.tsx`

```ts
interface AuthErrorPageProps {
  error: Error; // Auth0 error object
  onRetry: () => void; // Triggers loginWithRedirect()
}
```

**Error code extraction:** The component extracts a machine-readable error code from the Auth0 error object using a `getErrorCode` helper:

1. Checks for an `error` property on the error object (Auth0's `OAuthError` subclass includes this)
2. Falls back to parsing the first token from the error message if it matches a `snake_case` pattern
3. Returns `null` (and omits the badge) if neither approach yields a useful code

**Integration:** In `AuthContextProvider` (`client/src/providers/auth-provider.tsx`), the existing `if (authError)` block — which currently renders inline fallback JSX — is replaced with `<AuthErrorPage error={authError} onRetry={() => void loginWithRedirect()} />`. The existing `useEffect` guard that prevents redirect loops (`!authError` check) remains unchanged.

**What this does NOT change:**

- No routing changes — the error page is rendered by the auth provider, not a route
- No new providers or context
- No mid-session error handling (401s, silent refresh failures — those are a separate concern)
- No new dependencies — uses existing shadcn `Button` and `Badge` components

## User Experience

**Layout:** Full-viewport centered card on the `bg-background` surface. Maximum width of `md` (28rem). Respects the existing theme including dark mode via Tailwind/shadcn design tokens.

**Visual hierarchy (top to bottom):**

1. **Icon** — A large (text-6xl) lock icon to set the tone: something went wrong with access, but it is not catastrophic
2. **Headline** — "Well, that didn't work" — clear and slightly playful to reduce anxiety
3. **Error message** — The human-readable `error.message` string from Auth0, rendered as `text-sm text-muted-foreground` secondary text
4. **Error code badge** — When an error code is available (e.g., `server_error`, `access_denied`), displayed as a small monospace `outline` badge. Omitted entirely when no code can be extracted, so the page never shows an empty or confusing badge
5. **Retry button** — A primary-styled `Button` labeled "Try again" that calls `onRetry`, triggering a fresh `loginWithRedirect()` attempt

**Retry flow:** Clicking "Try again" invokes `loginWithRedirect()`, which redirects the user to Auth0's hosted login page for a fresh authentication attempt. If the underlying issue was transient, the user lands in the app. If it persists, they return to the error page with the (potentially updated) error details.

## Benefits

- **For users:** A clear, recoverable experience replaces a blank page. Most transient auth failures become self-service.
- **For support teams:** Fewer "I can't log in" tickets. When tickets do come in, they include an error code instead of "it's broken."
- **For developers:** Auth error rendering is encapsulated in one component with a two-prop interface. Design changes do not require touching the auth provider. The component is easy to test in isolation.
- **For the product:** The first thing users see when something goes wrong is polished and intentional, not raw and accidental. Trust is preserved.

## Costs

- **Engineering effort:** Minimal. One new component file, one modification to an existing file. Estimated at under half a day of work.
- **New code surface area:** ~50 lines of component code plus the `getErrorCode` helper. No new dependencies, no new state management, no new API calls.
- **Maintenance burden:** Near zero. The component is purely presentational with no side effects. It changes only if the Auth0 error shape changes or the design system evolves.

## Dependencies

| Dependency                             | Status         | Notes                                                             |
| -------------------------------------- | -------------- | ----------------------------------------------------------------- |
| shadcn `Button` component              | Already in use | Used for the retry action                                         |
| shadcn `Badge` component               | Already in use | Used for the error code display                                   |
| Auth0 React SDK (`@auth0/auth0-react`) | Already in use | Provides the `error` object and `loginWithRedirect`               |
| Tailwind CSS design tokens             | Already in use | `bg-background`, `text-foreground`, `text-muted-foreground`, etc. |

No new external dependencies are introduced.

## Risks & Mitigations

| Risk                                                                        | Likelihood | Impact                                                     | Mitigation                                                                                                                                                       |
| --------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Auth0 changes its error object shape in a future SDK version                | Low        | Medium — error code badge may stop appearing               | `getErrorCode` is defensive: it falls back gracefully and omits the badge if no code is found. The page still functions without a code.                          |
| Users click "Try again" repeatedly on a persistent (non-transient) error    | Medium     | Low — they see the same error page each time, no harm done | The error message and code give them enough context to stop retrying and contact support. A future iteration could add a "Contact support" link after N retries. |
| The error page design drifts from the rest of the app over time             | Low        | Low                                                        | The component uses design tokens, not hardcoded colors. Theme changes propagate automatically.                                                                   |
| Mid-session auth failures (out of scope) are confused with pre-app failures | Low        | Medium                                                     | Scope is explicitly limited to pre-app auth failures. Mid-session failures will be handled separately with in-app UI (not a full-page takeover).                 |

## Timeline & Milestones

| Milestone                        | Estimate      | Description                                                                            |
| -------------------------------- | ------------- | -------------------------------------------------------------------------------------- |
| Component creation               | ~2 hours      | Create `AuthErrorPage` component with error display, code extraction, and retry button |
| Integration                      | ~1 hour       | Replace inline error JSX in `AuthContextProvider` with the new component               |
| Type check and lint verification | ~30 minutes   | Confirm `tsc --noEmit` and lint pass with no new errors                                |
| Code review and merge            | ~1 day        | Standard PR review process                                                             |
| **Total**                        | **~1-2 days** | End to end, including review                                                           |

## Open Questions

1. **Should the error page include a "Contact support" link or email?** Currently omitted for simplicity, but could reduce friction for persistent (non-transient) failures. Needs a decision on what the support channel URL or email should be.
2. **Should we add a retry counter that changes the messaging after N failed attempts?** For example, after 3 retries, the page could shift from "Try again" to "This issue may require support assistance." This adds complexity but could reduce futile retry loops.
3. **Should we log auth errors to an observability backend (e.g., Sentry)?** The error page is client-side only. If we want visibility into auth failure rates, we would need to fire an event. This is orthogonal to the component itself but worth deciding.
4. **Should mid-session auth failures eventually share this component, or is a separate in-app pattern better?** The spec explicitly scopes this to pre-app failures. Mid-session failures likely warrant a modal or toast rather than a full-page takeover, but the design language should be consistent.
