Assignments & Landing Guards
Post-login landing routes a session to the right page. Two mechanisms make that routing correct: the token that resolves which records a user may reach, and the page that stops an anonymous visitor before the resolver ever runs.
The Assignment Token
$currentUser.assignments.<table> resolves to the set of record IDs from the user's user_access rows for <table> — a scope table declared in auth.scopeTables. It appears in two forms, which behave differently.
Indexed — in a landing URL
The single-record form $currentUser.assignments.<table>[0] substitutes the first assignment ID, producing a deep link straight to that record:
roles:
- name: customer-admin
defaultLanding: /portal/clients/$currentUser.assignments.clients[0]
pickerLanding: /portal/select/clients
A role may contain at most one such token — with two, the resolver cannot infer which scope to count against. A pickerLanding must contain none: by definition the multi-record case has no single ID to substitute. Both rules are enforced at config decode.
Un-indexed — in a dataSource filter
The bare form $currentUser.assignments.<table> resolves to the full ID list, so a picker page lists exactly the records the user can reach — and no others:
pages:
- name: client-picker
path: /portal/select/clients
access: authenticated
components:
- type: list
props: { id: clients }
dataSource:
table: clients
filter:
- field: id
operator: in
value: $currentUser.assignments.clients
children:
- type: text
content: $record.name
This is what makes the picker safe to expose: the filter is resolved server-side from the session, so a user cannot widen it by editing a request.
The landingPath Page Is the Access Guard
landingPath must be backed by a co-located page declared at the same path. That page is the unauthenticated-access guard: its access block bounces anonymous visitors to /login before the landing resolver runs. For an authenticated visitor the resolver redirects away first, so the page itself is typically empty and never renders.
auth:
landingPath: /portal
# ...
pages:
- name: portal-landing
path: /portal
access:
require: authenticated
redirectTo: /login
components: [] # never rendered for authed users — the resolver redirects first
Forgetting the guard page is the most common mistake here. landingPath: /portal with no /portal page leaves anonymous visitors unguarded at the mount path. Always pair the mount path with a page that requires authentication.
Earlier drafts of this design forbade a page colliding with landingPath. The implementation reverses that: the collision is the mechanism, and the colliding page is required.
Related Pages
- Post-Login Landing —
landingPath,defaultLanding, and the resolution table. - Sessions —
scopeTables,user_access, and$currentUser.activeAssignment. - Roles & RBAC — the roles landing rules are declared on.
- Pages — page
accessanddataSource.filter.
Last updated July 27, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta — contributions and corrections are welcome.