Handlers & Routing

Every handler, its actions, and how Router.bx wires URLs to them.

On this page

Handlers & Routing

Handler map

HandlerBasePurpose
Auth.bxEventHandlerLogin, registration, invitations, password reset - all public
BaseSecureHandler.bxRestHandlerBase class for every admin handler
Dashboard.bxBaseSecureHandlerThe authenticated landing page
Main.bxEventHandlerImplicit-event handler - see Architecture
Permissions.bxBaseSecureHandlerPermission slug CRUD
Profile.bxBaseSecureHandlerSelf-service profile, password, API tokens, passkeys
Roles.bxBaseSecureHandlerRole CRUD + user assignment
Settings.bxBaseSecureHandlerApp settings registry
Users.bxBaseSecureHandlerUser administration

BaseSecureHandler

Every protected handler extends BaseSecureHandler, which forces the Admin layout in preHandler, redirects to profile/passkey-required when cbRequirePasskey is on and the user has none, and provides shared helpers (getApiResults(), ensureSortDirection(), getPagination()):

component extends="coldbox.system.RestHandler" {

    function preHandler( event, rc, prc ){
        event.setLayout( "Admin" );
        // ...passkey enforcement...
    }

}

Building a new secured handler starts the same way every time:

component extends="BaseSecureHandler" secured {

    function index( event, rc, prc ){
        prc.pageTitle = "My Page";
        event.setView( "myhandler/index" );
    }

}

Auth

No @secured annotation - these actions must stay reachable by guests:

  • login / doLogin (GET/POST) - CSRF-verified, calls securityService.login(), supports rememberMe
  • register / doRegister - gated by the cbAllowRegistration setting
  • checkEmailAvailability - JSON endpoint for live email-availability checks
  • verifyRegistration - consumes a PURPOSE_REGISTRATION action token
  • activateInvitation / doActivateInvitation - sets a password for an invited, admin-created user
  • forgotPassword / doForgotPassword - gated by cbAllowForgotPassword
  • resetPassword / doResetPassword - validates the reset token, sets a new password
  • logout - calls securityService.logout()

preHandler redirects an already-authenticated visitor straight to the dashboard, and sets the layout from prc.settings.cbLoginLayout (AuthSplit by default - see guides/security.md).

Dashboard

@secured (any authenticated user, no specific permission required):

  • index - the dashboard home
  • notAuthorized - the target of invalidAuthorizationEvent, shown when an authenticated user is missing a required permission

Permissions

@secured("permissions:admin,permissions:read") at the class level:

  • index
  • create - @secured("permissions:admin,permissions:write")
  • update / delete - @remote, same write/delete permissions

Profile

@secured self-service actions for the current user, all @remote AJAX endpoints except index:

  • index, passkeyRequired
  • save, doPasswordChange
  • listTokens / createToken / updateToken / deleteToken - API tokens
  • listPasskeys / updatePasskey / deletePasskey

A static csrfVerify map on the class lists which of these actions require CSRF verification.

Roles

@secured("roles:admin,roles:read") at the class level; every action but index is @remote:

  • index
  • create / update / delete - @secured("roles:admin,roles:write" / "...:delete")
  • users / availableUsers - list users on/available for a role
  • addUser / removeUser - @secured("roles:admin")

Settings

@secured("settings:admin,settings:read") at the class level:

  • index
  • registry / registrySearch - paginated settings registry
  • createRegistry / updateRegistry / toggleRegistryStatus / deleteRegistry - settings:admin,settings:write
  • save - bulk save of core settings
  • Admin utilities (all settings:admin): clearTemplateCache, clearSessionsCache, revokeRememberTokens, flushSettingsCache

Users

@secured("users:admin,users:read") at the class level:

  • index, search
  • create / update / delete / resendInvitation - users:admin,users:write / ...:delete
  • show - users:read
  • Admin-only (users:admin): updateProfile, setStatus, resetPassword, verify, revokeRememberTokens, addRole/removeRole, addPermission/removePermission, savePreferences, revokeToken/revokeAllTokens

ensureNotSelf() guards several of these to block an admin from demoting or removing their own roles.

CSRF is manual, not automatic

app/config/modules/cbsecurity.bx sets csrf.enableAutoVerifier: false - every state-changing action verifies CSRF itself (via a preHandler check or a static.csrfVerify map), rather than relying on a global interceptor. Follow the existing pattern in the handler you're extending.

Route map (app/config/Router.bx)

All routes are declared in one configure() function:

route( "/healthcheck" ).to( () => "Ok!" );

get( "dashboard" ).to( "Dashboard.index" );

resources( "permissions", parameterName = "permissionId" );

route( "roles/:roleId/available-users" ).to( "Roles.availableUsers" );
route( "roles/:roleId/users" ).toAction( { POST: "addUser" } );
route( "roles/:roleId/users/:userId" ).toAction( { DELETE: "removeUser" } );
resources( "roles", parameterName = "roleId" );

resources( "users", parameterName = "userId" );

route( "profile" ).toAction( { GET: "index", POST: "save" } );

// @app_routes@  ← insertion point for module/scaffold-generated routes

route( ":handler/:action?" ).end(); // conventions-based catch-all

See Reference: Route Map for the full table of every method, URL, target action, and required permission.

Edit this page Download Markdown Last updated Sep 1, 2026, 7:55:53 PM