Skip to main content

Security

Overview

The security of the application is managed by permissions. Permissions are used to define the actions that the user group can perform in the application. Permissions are used to control the visibility of components, the behavior of components, and the behavior of the application. Permissions are defined in the app modules. Each component, action, and route can have a permission. If the user does not have the required permission, the component, action, or route will not be rendered or executed.

Permissions are assigned to roles. Roles are assigned to user groups. To receive a permission, the user must be a member of a user group that has a role with the permission.

On mobile cold boot, the renderer restores the last persisted permissions for the same user while the current-user request is still loading. This prevents permission-gated SDUI components from briefly disappearing after token or domain restoration. The fresh organization-scoped profile replaces the restored permissions as soon as it arrives; permissions are never reused for a different user.

Mobile authentication tokens are stored in the native secure store with iOS AFTER_FIRST_UNLOCK accessibility. This allows token rotation and cold-start restoration after the device has been unlocked once, including when the screen is currently locked. If a transient locked-keychain read returns no refresh token, the renderer falls back to the in-memory token. Network or temporary refresh failures preserve the stored session for a later retry; only an identity-server rejection that marks the refresh token expired clears the session.

The identity server allows a configurable refresh-token reuse window (OpenIddict:RefreshTokenReuseLeewaySeconds, 600 seconds by default). This covers mobile clients that are suspended after the server rotates a token but before the response reaches the device; a retry with the previous token inside the window does not revoke the authorization chain. Expired and revoked OpenIddict tokens and tokenless authorizations older than 14 days are pruned by the openiddict-token-pruning Hangfire job each day at 03:00 UTC.

The mobile sign-in screen supports password, password reset, configured demo accounts, and available Google, Microsoft, and Apple providers. Provider slots remain stable while native readiness is resolved, and Apple is omitted when it is unavailable. Remember-me storage contains only the username preference; demo credentials bypass it. Authentication state and behavior are shared by both mobile login skins.

(Add a diagram to show the relationship between permissions, roles, and user groups)

Permissions

Permissions are defined in the app modules. Each component, action, and route can have a permission. If the user does not have the required permission, the component, action, or route will not be rendered or executed.

Example:

permissions:
- name: "System/Countries/Read"
description: "Read countries"
roles: # Assign roles to the permission
- "Administrator"
- "Operator"

Organization Contacts

Organization contacts are used to link application users to employees or other entities in the organization. Organization contacts are used to define the relationship between the user and the organization. For example, the user can be an employee, a customer, or a supplier.

Passkeys (WebAuthn)

Users can register passkey credentials and use the OpenID Connect passkey grant to authenticate without a password. Registration and assertion challenges are short-lived and scoped to the user and relying party. Credentials store the public key and signature counter; the private key remains on the user's authenticator.

The API supports listing, registering, renaming, and deleting the current user's passkeys. Relying-party identifiers are resolved from the request host or configured passkey settings. Treat registration and deletion as account-security operations requiring an authenticated user context.

GraphQL tenant gate

GraphQL requests are evaluated against the current organization. Normal users can access only organizations where they have membership, and deleted organizations are excluded. Members of the seeded System Administration organization with the Administrator role can perform authorized cross-organization administration. The system organization type cannot be claimed through a dynamic mutation.

When a request changes organization context, the API rebuilds the current-user data and its contact lookups for the new organization before continuing. This prevents stale per-request contact data from producing an empty user context or being inherited by background work queued from the request.