Frontend Implementation Specification
Status
Section titled “Status”This document is FINAL and ENFORCEABLE for frontend implementation.
It defines how the frontend must integrate with:
- Auth Backend
- Platform Core (indirectly only, never direct for access)
- Base Backend
- Module Backends
This document complements:
architecture/documentation.mdarchitecture/diagrams.md1.1.1-Architecture-Permissions.md1.1.2-Architecture-Permissions-Packages.md1.2.-Architecture-Blueprint.md2.1.-Backend-Auth.md2.2.-Backend-Core.md2.2.1.-Backend-Core-API.md2.3.-Backend-Base.md2.4.-Access-Control-Integration.md2.5.-Access-Matrix.md
1. Purpose
Section titled “1. Purpose”This document explains how the frontend should work in the final Kisum architecture.
It covers:
- login flow
- token usage
- access bootstrap
- company switching
- module visibility
- route guarding
- permission-aware UI
- request headers
- cache and refresh behavior
- what frontend is allowed to decide
- what frontend must never decide
2. Frontend role in the architecture
Section titled “2. Frontend role in the architecture”Frontend is the UX layer.
Frontend is responsible for:
- login screens
- app shell
- company selector
- module navigation
- page rendering
- route visibility
- button visibility
- form visibility
- user interaction
Frontend is not responsible for:
- validating JWTs as security authority
- computing effective access
- deciding entitlements
- deciding company membership
- deciding permissions as source of truth
- replacing backend authorization
3. Final system rule
Section titled “3. Final system rule”Frontend may use access data for UX,but backend remains the final authority.This means:
- frontend may hide a module
- frontend may show a module
- frontend may disable a button
- frontend may guard a route
But if frontend accidentally shows something, backend must still enforce the real rule.
4. Access model the frontend must respect
Section titled “4. Access model the frontend must respect”Frontend must understand that access is resolved in 3 layers:
Level 1 — Company entitlements
Section titled “Level 1 — Company entitlements”Owned by Platform Core, resolved through Auth.
Question:
- did the company buy this module?
Level 2 — Membership module grants
Section titled “Level 2 — Membership module grants”Owned by Auth.
Question:
- does this membership have this module?
Level 3 — Fine-grained permissions
Section titled “Level 3 — Fine-grained permissions”Owned by Auth.
Question:
- what actions inside the module are allowed?
Frontend must never try to compute these layers itself.
Frontend must consume the final result from Auth.
5. Main frontend contract
Section titled “5. Main frontend contract”Frontend must use:
Section titled “Frontend must use:”GET /auth/meGET /auth/me/accessThese are the main access/bootstrap endpoints.
/auth/me
Section titled “/auth/me”Used for:
- identity
- basic profile
- memberships list
- session info
/auth/me/access
Section titled “/auth/me/access”Used for:
- active company access
- effective modules
- permissions
- delegation info
- rendering decisions
6. Login flow
Section titled “6. Login flow”6.1 Login request
Section titled “6.1 Login request”Frontend calls:
POST /auth/loginRequest body example:
{ "email": "user@example.com", "password": "password123", "accountType": "internal"}6.2 Login success behavior
Section titled “6.2 Login success behavior”On successful login, frontend receives:
accessTokenrefreshTokenexpiresIntokenType
Frontend must then:
- store tokens securely
- load
/auth/me - determine active company
- call
/auth/me/access - bootstrap UI from returned access context
6.3 Login failure behavior
Section titled “6.3 Login failure behavior”Frontend must handle:
401 unauthorized403 pending_approval403 registration_rejected403 account_inactive429 rate_limited
Frontend should map these into clear user messages.
7. Token handling rules
Section titled “7. Token handling rules”7.1 Access token
Section titled “7.1 Access token”Use for:
- authenticated API requests
Header:
Authorization: Bearer <accessToken>Access token is short-lived.
7.2 Refresh token
Section titled “7.2 Refresh token”Use only for:
POST /auth/refreshPOST /auth/logout
Frontend must store refresh token securely.
7.3 Frontend must NOT do
Section titled “7.3 Frontend must NOT do”Frontend must not:
- put permissions in local hardcoded state as final truth
- assume JWT contains modules or permissions
- infer access from JWT payload alone
- use token contents as replacement for
/auth/me/access
8. Bootstrap flow after login
Section titled “8. Bootstrap flow after login”After login, frontend should do this sequence:
Step 1
Section titled “Step 1”Call:
GET /auth/meAuthorization: Bearer <accessToken>Step 2
Section titled “Step 2”Read memberships / available companies
Step 3
Section titled “Step 3”Select active company
Step 4
Section titled “Step 4”Call:
GET /auth/me/accessAuthorization: Bearer <accessToken>x-org: <companyId>Step 5
Section titled “Step 5”Store returned access context in frontend state
Step 6
Section titled “Step 6”Render app shell and module visibility
9. Active company model
Section titled “9. Active company model”Frontend must always have the concept of an active company.
This is the company currently selected by the user.
That company must be sent as:
x-org: <companyId>for all tenant-scoped requests.
9.1 Company switching
Section titled “9.1 Company switching”When user changes company:
- update active company state
- call
/auth/me/accessagain with newx-org - replace access context in memory
- rerender visible modules and allowed routes
- update outgoing request header source
9.2 Critical rule
Section titled “9.2 Critical rule”Frontend must never rely on an old company context after switch.
Access must be reloaded.
10. Recommended frontend state model
Section titled “10. Recommended frontend state model”Frontend should keep these as separate state domains:
10.1 Session state
Section titled “10.1 Session state”- accessToken
- refreshToken
- expiresAt
- session info
10.2 Identity state
Section titled “10.2 Identity state”- user id
- name
- globalRole
- authType
10.3 Organization state
Section titled “10.3 Organization state”- memberships
- activeCompanyId
- company list
10.4 Access state
Section titled “10.4 Access state”- tenantRole
- companyEnabledModules
- membershipGrantedModules (optional if returned)
- effectiveModules
- permissions
- delegation
- tokenVersion
- entitlementVersion
- generatedAt
11. Recommended access response shape
Section titled “11. Recommended access response shape”Frontend should be prepared for an access context similar to:
{ "companyId": "uuid", "tenantRole": "ADMIN", "companyEnabledModules": ["basic", "finance", "market"], "membershipGrantedModules": ["basic", "finance"], "effectiveModules": ["basic", "finance"], "permissions": [ "basic.dashboard.view", "basic.event.view", "finance.expense.view", "finance.expense.edit" ], "delegation": { "canBuyAddons": false, "canManageUsers": true, "grantableModules": ["basic"], "grantablePermissions": [ "basic.dashboard.view", "basic.event.view" ] }, "meta": { "tokenVersion": 2, "entitlementVersion": 8, "generatedAt": "2026-04-16T05:00:00Z" }}Frontend must treat this as rendering context, not security authority.
12. Module visibility rules
Section titled “12. Module visibility rules”Frontend should show module tiles / routes only if module exists in:
effectiveModulesExamples:
- show Basic app only if
basicexists - show Finance module only if
financeexists - show Market only if
marketexists - show Venue only if
venueexists - show AI only if
aiexists - show Touring only if
touringexists
12.1 Important rule
Section titled “12.1 Important rule”Do not show module only because role sounds related.
Example:
FINANCErole does not automatically mean Finance moduleADMINdoes not automatically mean all modulesUSERmay still have Finance if explicitly granted
Module visibility must come from effective access, not role name.
13. Route guard rules
Section titled “13. Route guard rules”Frontend may guard routes for UX purposes.
Example:
/finance/*requiresfinance/market/*requiresmarket/venue/*requiresvenue
If missing:
- redirect
- hide route
- show “access denied” UX
But backend remains the final authority.
14. Permission-aware UI rules
Section titled “14. Permission-aware UI rules”Frontend may use permissions to control UI details.
Examples:
- show “Create Expense” button only if
finance.expense.create - show “Edit Event” button only if
basic.event.edit - show “Approve Contract” only if
market.contract.approve
This is valid and recommended.
14.1 But frontend must remember
Section titled “14.1 But frontend must remember”Permission-based UI visibility is convenience only.
Backend must still enforce.
15. Delegation-aware UI rules
Section titled “15. Delegation-aware UI rules”Frontend should use delegation info to determine whether to show:
- user management screens
- module grant controls
- permission grant controls
- add-on purchase controls
Examples:
- if
canManageUsers = false, hide user-management actions - if
canBuyAddons = false, hide upgrade/purchase actions - if
grantableModulesempty, hide module assignment UI
16. Request header rules
Section titled “16. Request header rules”16.1 Authenticated tenant request
Section titled “16.1 Authenticated tenant request”For tenant-scoped backend requests, frontend must send:
Authorization: Bearer <accessToken>x-org: <companyId>16.2 Public routes
Section titled “16.2 Public routes”Do not send auth unless needed.
16.3 Vendor routes
Section titled “16.3 Vendor routes”If vendor flow exists, use vendor auth model only on vendor routes.
Do not mix tenant x-org assumptions into vendor flows unless explicitly designed.
17. Refresh behavior
Section titled “17. Refresh behavior”Frontend must refresh access in these cases:
- after login
- after token refresh
- after company switch
- after page reload / app restore
- after module grant change
- after permission grant/revoke
- after add-on/subscription change
- after receiving backend response that indicates access changed
- after invitation acceptance flow if it changes membership
18. What frontend may cache
Section titled “18. What frontend may cache”Frontend may cache:
- last
/auth/me - last
/auth/me/access - active company id
- visible modules
- permission context
Possible locations:
- in-memory state
- app store
- short-lived session storage (if product chooses)
18.1 What frontend must NOT do with cache
Section titled “18.1 What frontend must NOT do with cache”Frontend must not:
- trust cached access forever
- assume cache beats backend
- continue showing old company access after company switch
- assume stale cached permissions are correct
When in doubt, refetch.
19. Error handling model
Section titled “19. Error handling model”Frontend must distinguish:
Authentication problem
- invalid token
- expired token
- refresh needed
- session revoked
Typical frontend behavior:
- refresh token if possible
- otherwise redirect to login
Authorization problem
- user authenticated
- request forbidden
- missing module / permission / scope
Typical frontend behavior:
- show access denied
- refresh access context if this may be stale
- rerender UI
Client/request issue
- missing or invalid
x-org - malformed request
Typical frontend behavior:
- fix request context
- recover active company state
Upstream/system issue
- Auth unavailable
- Core unavailable through Auth
- access resolution unavailable
Typical frontend behavior:
- show temporary error state
- allow retry
- do not guess access
20. Frontend route classification
Section titled “20. Frontend route classification”Frontend should classify routes as:
Tenant routes
Section titled “Tenant routes”- require JWT
- require active company
- require
/auth/me/access
Public routes
Section titled “Public routes”- no auth required
Vendor routes
Section titled “Vendor routes”- vendor auth only
Mixed routes
Section titled “Mixed routes”- must be explicitly documented
21. Recommended implementation pattern
Section titled “21. Recommended implementation pattern”21.1 Global access provider
Section titled “21.1 Global access provider”Frontend should implement a central access provider/store.
Responsibilities:
- keep current access context
- expose helpers like
hasModule()andhasPermission() - expose active company
- refresh access on demand
- clear access on logout
21.2 Suggested helper API
Section titled “21.2 Suggested helper API”Frontend should centralize helpers similar to:
hasModule("finance")hasPermission("finance.expense.view")canManageUsers()canBuyAddons()isTenantRole("ADMIN")These helpers must read from current access context, not hardcoded role assumptions.
22. Suggested page-loading pattern
Section titled “22. Suggested page-loading pattern”Example for Finance page:
- ensure session valid
- ensure active company selected
- ensure access context loaded
- check
hasModule("finance") - if no → deny route
- if yes → render page
- inside page, show/hide actions by permission
23. Suggested UI states
Section titled “23. Suggested UI states”Frontend should have explicit UI states for:
- loading session
- loading memberships
- loading access
- company selection required
- access denied
- temporary service unavailable
- session expired
This avoids confusing transitions.
24. Logout behavior
Section titled “24. Logout behavior”On logout:
- call
POST /auth/logout - clear accessToken
- clear refreshToken
- clear identity state
- clear access state
- clear active company
- redirect to login
If “logout all” is used:
- same frontend clearing behavior
25. Security rules for frontend
Section titled “25. Security rules for frontend”Frontend must never:
- store internal API keys
- call Platform Core directly for access decisions
- call internal machine routes
- trust JWT payload as full access truth
- bypass backend checks
- assume UI visibility is security
26. Relationship with Platform Core
Section titled “26. Relationship with Platform Core”Frontend should not call Platform Core directly for access.
Correct pattern:
Frontend → Auth → Corenot:
Frontend → CoreFrontend may only interact with Core indirectly through Auth or admin backend flows.
27. Relationship with Base / Module Backends
Section titled “27. Relationship with Base / Module Backends”Correct pattern:
Frontend → Base/Module BackendBackend → Auth for enforcementFrontend must not assume that because /auth/me/access says “yes”, backend will never reject.
Backend is still the final enforcement point.
28. Company and company data migration note
Section titled “28. Company and company data migration note”If some legacy systems still store companies separately, frontend should still treat:
x-org = canonical company idDo not preserve legacy mixed company-id behavior in frontend forever.
Target state:
- one canonical company id across app
29. QA / test scenarios frontend must support
Section titled “29. QA / test scenarios frontend must support”- login success
- login pending approval
- login rejected
- login inactive
- token refresh flow
- logout flow
- company switch reloads access
- access denied hides module
- backend 403 updates UI correctly
- backend 503 shows retry state
- stale cached access gets replaced after refresh
- invited user accepts invitation and sees new company after reload
30. Final rules
Section titled “30. Final rules”Frontend consumes accessFrontend does not compute accessFrontend does not enforce securityFrontend does not replace backend authorization31. Final one-line summary
Section titled “31. Final one-line summary”Frontend uses Auth access context to render the app correctly,but every real security decision is still enforced by backend services.