You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

6.6 KiB

Auth Access Control Implementation Plan

For agentic workers: Execute tasks in order and keep scope fixed to the approved spec. Use checkbox (- [ ]) tracking and verify each task before moving on.

Goal: Implement login-first access control for both pages and APIs, with code-based allowlists, safe login redirect handling, and dual-mode homepage rendering support.

Architecture: Introduce centralized route rule utilities, a global page guard (auth.global), a server API guard middleware (10.auth-guard), and a unified auth session composable. Default policy is deny-by-default for pages and /api/**, with narrow allowlists.

Tech Stack: Nuxt 4/Nitro middleware, existing session-cookie auth service, TypeScript utilities, Bun dev/build workflow.


File Structure Map

  • Create: app/utils/auth-routes.ts
  • Create: app/composables/useAuthSession.ts
  • Create: app/middleware/auth.global.ts
  • Create: server/utils/auth-api-routes.ts
  • Create: server/middleware/10.auth-guard.ts
  • Modify: app/pages/login/index.vue (handle redirect after login success)
  • Modify: app/pages/index/index.vue (guest/authenticated split rendering)
  • Optional small adjustments: shared HTTP wrapper for uniform 401 handling if needed

Task 1: Build shared page-route auth rules and redirect sanitizer

Files:

  • Create: app/utils/auth-routes.ts

  • Test: utility behavior via unit-like local checks (or temporary assertions)

  • Step 1: Define route policy constants

Include:

  • public routes exact set (initial: /, /login, /register)

  • guest-only routes exact set (initial: /login, /register)

  • default authenticated landing path

  • Step 2: Implement safe redirect parser

Implement helper like normalizeSafeRedirect(input, fallback):

  • allow only same-site relative paths

  • must start with /

  • reject //, protocol-like payloads (http:, https:, javascript:), empty strings

  • return fallback when invalid

  • Step 3: Implement route match helpers

Provide concise helpers:

  • isPublicRoute(path)

  • isGuestOnlyRoute(path)

  • matching mode: exact + explicit prefix helper only (no broad regex)

  • Step 4: Verify sanitizer edge cases

Validate examples:

  • accept: /dashboard, /a?b=1
  • reject: http://evil.com, //evil.com, javascript:alert(1)

Task 2: Add unified auth session composable

Files:

  • Create: app/composables/useAuthSession.ts

  • Test: manual behavior from multiple pages without duplicated calls

  • Step 1: Implement canonical session state

Expose:

  • loggedIn
  • user
  • pending
  • refresh()
  • clear()

Use /api/auth/me as source of truth.

  • Step 2: Handle unauthorized consistently

When /api/auth/me returns 401:

  • clear local state

  • return loggedIn=false instead of throwing noisy UI errors

  • Step 3: Ensure composable is reusable

Guarantee all pages/middleware consume this composable, not ad-hoc login checks.


Task 3: Implement global page guard middleware (default deny)

Files:

  • Create: app/middleware/auth.global.ts

  • Reuse: app/utils/auth-routes.ts, app/composables/useAuthSession.ts

  • Step 1: Enforce default login-required policy

For every route:

  • if route is not public and user not logged in, navigate to /login?redirect=<fullPath>

  • Step 2: Enforce guest-only behavior

If logged-in user visits guest-only routes:

  • resolve safe redirect target

  • prioritize validated redirect query

  • fallback to default authenticated landing path

  • Step 3: Prevent redirect loops

Guarantee no self-loop on /login and /register.

  • Step 4: Manual route-flow verification

Check:

  • unauth -> protected page -> login redirect with query
  • logged-in -> /login -> redirected away
  • public routes remain reachable when unauthenticated

Task 4: Implement server API auth guard middleware (default deny for /api/**)

Files:

  • Create: server/utils/auth-api-routes.ts

  • Create: server/middleware/10.auth-guard.ts

  • Step 1: Define API allowlist rules

Initial allowlist:

  • /api/auth/login
  • /api/auth/register
  • required public-read APIs (e.g. GET /api/config/global)

Use exact + explicit prefix helpers only.

  • Step 2: Guard /api/** requests

Middleware logic:

  • non-API request -> skip

  • allowlisted API -> skip

  • others -> require valid session from event.context.auth.getCurrent()

  • Step 3: Return unified unauthorized response

On unauthorized:

  • return HTTP 401

  • use generic status message

  • do not leak auth internals

  • Step 4: Validate API behavior

Check with/without cookie:

  • protected APIs return 401 when unauthenticated
  • allowlisted APIs still work

Task 5: Wire login/home pages to new access-control behavior

Files:

  • Modify: app/pages/login/index.vue

  • Modify: app/pages/index/index.vue

  • Step 1: Login page post-success redirect

After successful login:

  • call useAuthSession().refresh()

  • read redirect query and sanitize

  • navigate to safe target/fallback

  • Step 2: Homepage dual-mode rendering

On /:

  • unauthenticated: show guest-facing UI

  • authenticated: show signed-in UI

  • rely on useAuthSession state

  • Step 3: Session-loss UX consistency

If session expires during interaction:

  • state clears
  • protected route access re-routes to login

Task 6: Verification matrix and polish

Files:

  • Test-focused task; no intended new feature files

  • Step 1: Route verification matrix

Run through:

  • unauth + public route

  • unauth + protected route

  • auth + guest-only route

  • auth + protected route

  • Step 2: Redirect security matrix

Try login URL examples:

  • /login?redirect=/safe/path (accepted)

  • /login?redirect=http://evil.com (rejected)

  • /login?redirect=//evil.com (rejected)

  • Step 3: API verification matrix

Confirm:

  • unauth -> protected /api/** => 401

  • allowlisted APIs accessible unauthenticated

  • expired session behaves as unauthorized

  • Step 4: Build/lint sanity

Run:

  • bun run build
  • lint/type checks used by the repo

Fix only issues introduced by this scope.


Delivery Constraints

  • Keep allowlists in code (no remote config source for auth policy)
  • No RBAC/role granularity in this iteration
  • No OAuth/SSO/password reset in this iteration
  • Avoid broad route regexes that can accidentally expand exposure

Done Criteria

All below must be true:

  • Page policy is deny-by-default with explicit public routes
  • API policy is deny-by-default under /api/** with explicit allowlist
  • Redirect handling is safe against open-redirect payloads
  • Login and homepage behavior matches agreed UX
  • Verification matrix passes without known regressions