Skip to main content
View as Markdown

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:

app.yaml
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:

app.yaml
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.

app.yaml
auth:
  landingPath: /portal
  # ...

pages:
  - name: portal-landing
    path: /portal
    access:
      require: authenticated
      redirectTo: /login
    components: [] # never rendered for authed users — the resolver redirects first

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.

  • Post-Login LandinglandingPath, defaultLanding, and the resolution table.
  • SessionsscopeTables, user_access, and $currentUser.activeAssignment.
  • Roles & RBAC — the roles landing rules are declared on.
  • Pages — page access and dataSource.filter.

Last updated September 1, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium