005 Authorization Plan

Authorization Implementation Plan for Next.js App

✅ FINAL AUTHORIZATION & LICENSED SESSION SCHEMA

(Authoritative Execution Plan for Next.js + C# + Firebase)


1. Scope & Objectives

This plan defines authentication, authorization (RBAC), and license enforcement for the Next.js mobile frontend while preserving:

  • Existing MS SQL role tables
  • Existing backend authorization logic
  • Multi-tenant (database-per-tenant) isolation
  • Capacity planning guarantees
  • Zero-crash behavior
  • Backward compatibility with Blazor

2. Non-Negotiable Principles (Must Be Followed)

  1. Backend is the sole authority for authorization and licensing

  2. Frontend never decides access — it only reflects backend decisions

  3. Every protected API enforces permission checks

  4. Session ≠ JWT

    • JWT proves identity
    • Session proves license usage
  5. Sessions are time-bound leases, not permanent locks

  6. No client-side storage (localStorage) is authoritative


3. Data Model (Required Tables)

3.1 UserLicenses

UserLicenses
------------
UserId
MaxConcurrentSessions   -- typically 1

3.2 UserSessions

UserSessions
------------
SessionId (GUID, PK)
UserId
FirebaseUid
TenantId
DeviceId
IssuedAt
LastSeenAt
IsRevoked (bit)

Derived rule (important):

Session is ACTIVE if:
IsRevoked = 0
AND LastSeenAt >= (NOW - IdleTimeout)

IdleTimeout (recommended): 10 minutes


4. Authentication Flow (Firebase)

  1. User logs in via Firebase Auth (email/password or provider)

  2. Firebase issues JWT (identity only)

  3. Next.js sends JWT to backend

  4. Backend validates JWT

  5. Backend resolves:

    • FirebaseUid
    • TenantContext
    • Internal UserId

Authentication ends here.


5. Licensed Session Creation Flow (Critical)

5.1 Login Handshake Endpoint

POST /api/auth/session/start
Authorization: Bearer <Firebase JWT>

5.2 Backend Logic (Authoritative)

resolve UserId + TenantContext

activeSessions =
  SELECT *
  FROM UserSessions
  WHERE UserId = @UserId
    AND IsRevoked = 0
    AND LastSeenAt >= NOW - IdleTimeout

if activeSessions.count < MaxConcurrentSessions:
    create new session
    return SessionToken
else:
    return 409 CONFLICT
    with payload:
      {
        "reason": "ACTIVE_SESSION_EXISTS",
        "sessions": [device info, lastSeen]
      }

6. “Already Signed In on Another Device” Handling

6.1 Frontend UX (Next.js)

If backend returns ACTIVE_SESSION_EXISTS:

Display modal:

You are already signed in on another device. Continuing will sign you out from the other device.

Buttons:

  • Continue & Sign Out Other Device
  • Cancel

6.2 Takeover Endpoint (Recommended Default)

POST /api/auth/session/takeover
Authorization: Bearer <Firebase JWT>

Backend action:

revoke all active sessions for UserId
create new session
return new SessionToken

This guarantees:

  • No lockouts
  • No waiting for idle timeout
  • Clean license enforcement

7. Session Token Handling (Secure)

  • SessionToken is:

    • Opaque GUID
    • Backend-issued
  • Stored as:

    • HTTP-only cookie (preferred)
    • OR encrypted server-only storage

❌ Never store in localStorage ❌ Never trust client-provided session IDs


8. Session Heartbeat (No Lockout Guarantee)

8.1 Automatic Heartbeat

Every authenticated API call must:

UPDATE UserSessions
SET LastSeenAt = NOW
WHERE SessionId = @SessionId

Optional lightweight endpoint:

POST /api/auth/session/ping

Called every 2–5 minutes.


9. Authorization (RBAC) — Backend Only

9.1 Existing Role Logic (Reused)

Use existing tables and logic:

user_roles_link
roles
features

Existing backend method remains authoritative:

HasAccess(featureCode)

9.2 Enforcement Rule (Mandatory)

Every protected API must include:

if (!HasAccess("FeatureCode"))
    return Forbid();

Frontend checks do not replace this.


10. Frontend Permission Awareness (UX Only)

10.1 Permissions Endpoint

GET /api/auth/my-permissions

Returns:

{
  "MemberMstDetails": true,
  "MemberMstReport": false
}

Derived from backend role logic.


10.2 Usage in Next.js

  • Used to:

    • Hide menu items
    • Disable buttons
    • Control routing
  • NOT used as security boundary


11. Request Authorization Pipeline (Backend)

Order matters:

  1. JWT validation
  2. TenantContext resolution
  3. Session validation (active + heartbeat)
  4. Authorization (RBAC)
  5. Controller logic
  6. Repository execution

If any step fails → 401 or 403, never 500


12. Failure Modes (Defined, Predictable)

Scenario Response
Invalid JWT 401
No active session 401
Session expired 401
Role missing 403
Tenant invalid 401
Session takeover 200

Backend must never crash due to auth/session issues.


13. Backward Compatibility (Blazor)

  • Blazor continues using:

    • Existing role checks
    • Existing auth
  • Session enforcement can be:

    • Introduced later
    • Or applied uniformly once stable

No breaking change required.


14. Explicit Anti-Patterns (DO NOT IMPLEMENT)

❌ Client-side RBAC enforcement ❌ localStorage session validation ❌ BrowserSessionId comparisons ❌ Trusting JWT claims for licensing ❌ Blocking login without takeover option


15. One-Line Contract (Give This to Antigravity)

Authentication proves identity. Authorization proves permission. Sessions enforce licensing. Only the backend decides.


16. Execution Order (Recommended)

  1. Implement UserSessions + UserLicenses tables
  2. Implement /session/start and /session/takeover
  3. Add heartbeat update on API calls
  4. Enforce RBAC inside APIs
  5. Add permissions endpoint for Next.js
  6. Remove legacy BrowserSessionId logic

✅ This plan:

  • Solves credential sharing
  • Prevents capacity overruns
  • Avoids lockouts
  • Works with Firebase + JWT
  • Matches enterprise ERP behavior

You can now hand this directly to Antigravity and say:

“Implement exactly this. Do not invent alternatives.”