Files
carmanagement/docs/subscription-page-redirect-fix-plan.md
T
root f22e0d45e1
Build & Deploy / Build & Push Docker Image (push) Successful in 7m57s
Test / Type Check (all packages) (push) Successful in 4m27s
Build & Deploy / Deploy to VPS (push) Successful in 7s
Test / API Unit Tests (push) Failing after 3m2s
Test / Homepage Unit Tests (push) Successful in 4m3s
Test / Storefront Unit Tests (push) Successful in 3m32s
Test / Admin Unit Tests (push) Successful in 3m27s
Test / Dashboard Unit Tests (push) Successful in 3m3s
Test / API Integration Tests (push) Failing after 3m53s
fix subscription page
2026-06-29 23:15:55 -04:00

539 lines
14 KiB
Markdown

# Subscription Page Redirect Fix Plan
## 1. Objective
Restore reliable access to:
```text
http://localhost:3000/dashboard/subscription
```
The subscription page must remain accessible to authorized account owners, including owners whose subscription is expired, unpaid, past due, canceled, or suspended.
The fix must not weaken backend authorization or allow non-owner employees to manage billing.
---
## 2. Current Problem
The subscription page exists, but authorized users are redirected to:
```text
http://localhost:3000/dashboard
```
The likely causes are:
1. The dashboard route guard treats the generated sidebar menu as a route-authorization list.
2. `/subscription` may not be returned by `GET /auth/employee/menu`.
3. The subscription page trusts the cached `employee_profile` value in `localStorage`.
4. Subscription enforcement may return `402 Payment Required` before the owner can reach the billing recovery page.
5. Redirect targets use ambiguous paths such as `/`, which resolve to `/dashboard` because of the configured Next.js base path.
---
## 3. Required Access Policy
The subscription page must be classified as an owner-only system route.
| User state | Expected access |
|---|---|
| Authenticated owner with active subscription | Allowed |
| Authenticated owner in trial | Allowed |
| Authenticated owner with expired subscription | Allowed |
| Authenticated owner with past-due subscription | Allowed |
| Authenticated owner with unpaid subscription | Allowed |
| Authenticated owner with canceled subscription | Allowed |
| Authenticated owner with suspended account | Allowed for billing recovery |
| Manager | Denied |
| Agent or regular employee | Denied |
| Unauthenticated user | Redirect to sign-in |
The route must not depend on whether it appears in the configurable dashboard menu.
---
## 4. Separate Navigation From Authorization
### Problem
The dashboard menu currently appears to serve two different purposes:
- Controlling which sidebar links are displayed
- Controlling which routes a user may access
These responsibilities must be separated.
### Required change
Use the menu response only for navigation visibility.
Use a dedicated access policy for route authorization.
The following route categories should be defined:
- Public authentication routes
- Core authenticated dashboard routes
- Owner-only system routes
- Subscription-dependent feature routes
- Admin-configurable feature routes
- Billing recovery routes
Register `/subscription` as:
```ts
{
authenticationRequired: true,
allowedRoles: ["OWNER"],
subscriptionRequired: false,
menuRegistrationRequired: false
}
```
The exact implementation may differ, but there must be one authoritative route policy.
---
## 5. Create a Central Route Access Policy
Add a shared route-policy configuration.
Example:
```ts
export const dashboardRoutePolicies = {
"/subscription": {
authenticationRequired: true,
allowedRoles: ["OWNER"],
subscriptionRequired: false,
menuRegistrationRequired: false,
},
};
```
The policy should be used by:
- `DashboardAccessGuard`
- Dashboard layout
- Subscription page
- Sidebar rendering
- Subscription-status middleware
- Automated tests
Do not duplicate owner checks and subscription checks across unrelated components.
---
## 6. Update `DashboardAccessGuard`
### Current behavior to remove
The guard must not redirect a user merely because the current route is absent from `/auth/employee/menu`.
### Required behavior
The guard should:
1. Resolve the current route policy.
2. Confirm authentication.
3. Confirm the authenticated role.
4. Apply subscription restrictions only when the route requires an active subscription.
5. Allow billing recovery routes regardless of subscription state.
6. Redirect only after all required state has finished loading.
7. Avoid redirecting to the current route.
8. Use a stable role-based fallback route.
### Expected error handling
| Response | Required behavior |
|---|---|
| `401 Unauthorized` | Redirect to sign-in |
| `403 Forbidden` | Show access denied or redirect to an allowed dashboard route |
| `402 Payment Required` | Redirect owner to `/subscription`, unless already there |
| Network failure | Show retryable error |
| Server failure | Show error state without pretending it is an authorization failure |
---
## 7. Fix Owner Verification on the Subscription Page
### Current risk
The page uses `employee_profile` from `localStorage` as an authorization source.
Browser storage can be:
- Missing
- Stale
- Malformed
- Left from another session
- Temporarily unavailable
### Required behavior
1. Treat local storage only as optional display cache.
2. Fetch the authenticated profile from:
```text
GET /auth/employee/me
```
3. Wait for the request to resolve before redirecting.
4. Allow access only when the API confirms the user is an owner.
5. Redirect confirmed non-owners to an allowed dashboard route.
6. Redirect unauthenticated users to sign-in.
7. Show a loading state during verification.
8. Show a retryable error for temporary API failures.
The page must not redirect while authentication or role state is unresolved.
---
## 8. Add Billing Recovery Route Exceptions
Subscription enforcement must never block the routes needed to repair a subscription.
Minimum allowlist:
```text
/dashboard/subscription
/dashboard/subscription/success
/dashboard/subscription/cancel
```
Depending on the product flow, also consider:
```text
/dashboard/profile
/dashboard/support
/dashboard/logout
```
These exceptions must be implemented wherever subscription enforcement occurs:
- Frontend route guards
- API middleware
- API interceptors
- Account suspension guards
- Subscription-status hooks
---
## 9. Review Backend Authorization
Inspect these API areas:
- `GET /auth/employee/menu`
- `GET /auth/employee/me`
- Authentication middleware
- Subscription-status middleware
- Account suspension middleware
- Subscription controllers
- Subscription services
- Billing portal endpoint
- Checkout endpoint
- Subscription update endpoint
- Menu configuration schema
- Menu seed data
### Backend requirements
- `/auth/employee/me` must remain available to authenticated owners during billing failure.
- Subscription read and recovery endpoints must remain available during `PAST_DUE`, `UNPAID`, `EXPIRED`, `CANCELED`, or `SUSPENDED` states.
- Owner authorization must be enforced server-side.
- Managers and employees must remain unable to manage subscriptions.
- Menu entries must not be treated as route permissions unless the API explicitly defines them as permissions.
- `402` responses must include enough structured information for the frontend to route owners to billing recovery.
---
## 10. Correct Redirect Targets
Audit redirects in:
- `DashboardAccessGuard.tsx`
- `subscription/page.tsx`
- Dashboard layout
- Authentication hooks
- API client interceptors
- Subscription hooks
- Suspended-account handling
Avoid ambiguous redirects such as:
```ts
router.replace("/");
```
Use explicit destinations based on the router and base-path convention.
Examples:
```ts
router.replace("/dashboard");
router.replace("/login");
router.replace("/dashboard/subscription");
```
Confirm whether the project router expects paths with or without the configured `/dashboard` base path, then use one convention consistently.
---
## 11. Prevent Redirect Loops
Protect against:
```text
/subscription -> /dashboard -> /subscription
/login -> /dashboard -> /login
/subscription -> / -> /dashboard
```
The route guard must:
- Compare the current path with the redirect destination.
- Never redirect to the current path.
- Avoid redirecting while authentication is loading.
- Avoid redirecting while role verification is loading.
- Avoid redirecting while route policy is loading.
- Preserve intended destinations after login where appropriate.
- Use a deterministic fallback route for each role.
---
## 12. Fix Sidebar Behavior
The sidebar must remain a navigation tool, not an authorization engine.
Required behavior:
- Show `Subscription` only to owners.
- Hide it from managers and regular employees.
- Keep it outside admin-configurable feature menu items.
- Do not allow subscription-plan configuration to disable it.
- Do not require it to be returned by `/auth/employee/menu`.
- Validate direct URL access through the central route policy.
The administrator may configure optional feature menus, but must not be able to remove the account owner's billing recovery route.
---
## 13. Add Loading and Error States
The subscription page should support these explicit states:
1. Verifying authentication
2. Verifying owner role
3. Loading subscription data
4. Subscription loaded
5. No subscription found
6. Payment required
7. Subscription expired
8. Subscription past due
9. Subscription canceled
10. Account suspended
11. Access denied
12. API unavailable
Do not briefly render protected content before redirecting.
Do not convert every API failure into a redirect.
---
## 14. Add Development Logging
During implementation, log structured route decisions:
```ts
{
pathname,
authenticated,
role,
subscriptionStatus,
menuContainsRoute,
routePolicy,
accessDecision,
redirectTarget
}
```
Do not log:
- Authentication tokens
- Payment details
- Full customer records
- Personal information
- Complete API response bodies
Remove or disable verbose logs before production release.
---
## 15. Automated Test Plan
### 15.1 Route guard tests
Test that:
- An owner can access `/subscription` when it is absent from the menu response.
- An owner with an active subscription can access the page.
- An owner in trial can access the page.
- An owner with an expired subscription can access the page.
- An owner with a past-due subscription can access the page.
- An owner with an unpaid subscription can access the page.
- An owner with a canceled subscription can access the page.
- An owner can access the page when another request returns `402`.
- Stale local storage does not override the authenticated API profile.
- A manager cannot access the page.
- An agent cannot access the page.
- An unauthenticated user is redirected to sign-in.
- A temporary API failure shows an error instead of redirecting.
- A redirect is not issued when the destination equals the current path.
### 15.2 Subscription page tests
Test that:
- No redirect occurs before `/auth/employee/me` resolves.
- A confirmed owner sees subscription information.
- A confirmed non-owner is denied.
- `401` redirects to sign-in.
- `403` shows access denied or redirects safely.
- `402` displays billing recovery.
- Network errors provide retry behavior.
- Loading state prevents protected-content flicker.
### 15.3 Integration tests
Test the complete flow:
```text
Owner login
-> open /dashboard/subscription directly
-> load current subscription
-> open checkout or billing portal
-> return through success or cancel URL
-> subscription page loads correctly
```
Test all supported statuses:
```text
ACTIVE
TRIALING
PAST_DUE
UNPAID
EXPIRED
CANCELED
SUSPENDED
```
---
## 16. Localization Validation
Validate the complete flow in:
- English
- French
- Arabic
Required checks:
- All labels are translated.
- Error messages are translated.
- Loading states are translated.
- Billing recovery messages are translated.
- Access-denied messages are translated.
- Arabic renders in true RTL.
- Buttons and status badges remain correctly aligned in RTL.
- No hard-coded English strings remain in the subscription flow.
---
## 17. Implementation Order
Execute the work in this order:
1. Define the route access policy.
2. Classify `/subscription` as an owner-only recovery route.
3. Update `DashboardAccessGuard`.
4. Remove menu-based authorization for system routes.
5. Replace local-storage-only role verification.
6. Verify the user through `/auth/employee/me`.
7. Add subscription recovery exceptions.
8. Review backend subscription enforcement.
9. Correct redirect targets and base-path handling.
10. Add redirect-loop protection.
11. Update sidebar visibility rules.
12. Add loading and error states.
13. Add unit tests.
14. Add integration tests.
15. Validate every subscription state.
16. Validate English, French, and Arabic.
17. Run type-check, lint, tests, and production build.
18. Remove development-only logging.
---
## 18. Required Code for Complete Implementation
The dashboard package is sufficient for a temporary frontend patch.
A complete fix requires these API parts:
- `/auth/employee/menu` route, controller, and service
- `/auth/employee/me` route, controller, and service
- Authentication middleware
- Role authorization middleware
- Subscription enforcement middleware
- Account suspension middleware
- Subscription routes
- Subscription controller
- Subscription service
- Billing portal integration
- Checkout integration
- API error interceptor or response-normalization logic
- Menu configuration schema
- Menu seed files
---
## 19. Acceptance Criteria
The fix is complete only when:
- `/dashboard/subscription` no longer redirects an authorized owner to `/dashboard`.
- The route works when it is absent from the generated menu.
- Owners with billing problems can access billing recovery.
- Non-owners remain blocked.
- Backend authorization remains enforced.
- Authorization does not rely on local storage.
- No redirect loop occurs.
- Direct URL access and sidebar navigation behave consistently.
- Base-path routing works correctly.
- `401`, `402`, `403`, network errors, and server errors are handled distinctly.
- English, French, and Arabic render correctly.
- Type-check passes.
- Lint passes.
- Unit tests pass.
- Integration tests pass.
- Production build succeeds.
---
## 20. Out of Scope
Do not include unrelated work in this repair:
- Subscription pricing redesign
- Billing provider migration
- Dashboard visual redesign
- New subscription tiers
- General sidebar restructuring
- Domain or publishing settings
- Unrelated permission-system changes
Any broader authorization redesign should be handled as a separate phase after the subscription route is stable.