Diagram
100%

Drag to pan · Scroll to zoom · + / − to zoom · 0 to fit · Esc to close

Skip to content

15. Authentication, IAM, and Electronic Signatures ​

Authentication proves an identity. Directory owns the person. Product IAM resolves what that person may do in one product and scope. An electronic signature re-verifies the authenticated person for one action digest.

Do not collapse these into one user table or one client-side permission flag.

This chapter uses app declarations from vyuh_blueprint, authentication wire types from vyuh_blueprint_protocol, authority/runtime types from vyuh_blueprint_server, and product catalogs from vyuh_iam.

Declare the client authentication boundary ​

dart
final app = BlueprintDescriptorSet(descriptors).blueprint(
  name: 'operations',
  title: 'Operations',
  version: '1.0.0',
  authentication: const AuthenticationUI(
    enabled: true,
    isRequired: true,
    loginPath: '/login',
    callbackPath: '/auth/callback',
    authenticatedPath: '/',
    publicPaths: ['/help'],
    allowPasswordReset: true,
  ),
);

AuthenticationUI declares the paths of the authentication boundary. A feature contributes the sign-in screens through BlueprintClientDescriptor.authenticationSurfaces. It does not carry provider secrets, tenant provider availability, or effective session timeouts. Those are server-resolved runtime facts.

A location that names no route, such as / after sign-in, lands on the first route the signed-in user may open: top-level navigation items first, then group items. To land on a particular route, declare it on the navigation; it is used when the user may open it:

dart
WorkspaceDescriptor(
  navigation: Navigation(landing: LandingUI(routeRef: 'dashboard')),
)

Signing out returns to the sign-in page without a return location, so the next person to sign in lands by their own access.

Brand the sign-in page ​

The same BrandingUI that names the product in the workspace header draws the sign-in page:

dart
branding: BrandingUI(
  title: 'ELog',
  tagline: 'Controlled execution, on record.',
  mark: BrandMarkUI.image(
    'asset:assets/images/logo.png',
    darkRef: 'asset:assets/images/logo-dark.png',
  ),
  features: [
    BrandFeatureUI(
      title: 'Audit ready',
      description: 'Every change is signed and kept with its record.',
      icon: UIIcon('audit'),
    ),
    BrandFeatureUI(
      title: 'Guided work',
      description: 'Each task shows who acts next.',
      icon: UIIcon('workflow'),
    ),
  ],
  legalText: 'For authorized users only.',
),

The mark is one of three forms: BrandMarkUI.image for a square picture (an asset path or a URL, with an optional dark variant), BrandMarkUI.icon for an icon on a tile of the brand colour, or BrandMarkUI.letter for one or two characters on that tile. A product that declares none is the first letter of its title. The same mark is drawn in the application switcher, above the sign-in card and on the loading screens.

wordmark is optional: a BrandImageUI, the wide picture of the product's name. The sign-in page draws it in place of the mark and the title. The panel it sits on is the brand colour, and its darkRef is drawn when that colour is dark, so declare one made for a dark background.

On a wide page the panel beside the card shows the brand, the tagline as its headline and one feature at a time. It moves to the next feature every five seconds, waits while it is hovered or focused, and does not move by itself when the platform asks for reduced motion. A feature shows its icon, or its image when it declares one. A narrow page shows the brand alone above the card. A feature that binds the brand widget ref replaces the whole panel.

Bind the authority on the server ​

dart
final auth = BlueprintAuthenticationController(
  blueprint: blueprint,
  provider: provider,
  deploymentEnvironment: 'production',
  sessionPolicyResolver: resolveSessionPolicy,
  policyEnforcer: loginPolicy,
  electronicSignatureStore: signatureStore,
  administratorSupportHandler: createSupportRequest,
);

BlueprintAuthenticationProvider supplies login, begin/complete authentication, refresh, logout, token authentication, and provider identity. Optional provider capabilities supply password reset and credential verification. The controller binds the deployment environment into discovery, login, recovery, and session-policy resolution so the client cannot select a different authority boundary.

The controller derives its public action manifest from the declared auth.identity entity. If a required canonical action is missing, it fails closed rather than exposing a partial contract.

Resolve sessions on the authority ​

dart
const defaultSessionPolicy = BlueprintAuthenticationSessionPolicy(
  inactivityTimeout: Duration(minutes: 30),
  absoluteTimeout: Duration(hours: 12),
  refreshSkew: Duration(seconds: 30),
  warningDuration: Duration(minutes: 2),
);

Issued and refreshed sessions carry the effective policy. A resolver can vary it by tenant or environment. The client enforces the returned inactivity, absolute timeout, refresh skew, and warning duration; it does not choose them.

Provider discovery and public recovery requests also bind the deployment environment on the server, preventing a client from selecting a different operational boundary.

Map directory identity to a product actor ​

dart
final strategy = BlueprintAuthenticationStrategy(
  provider: provider,
  actorResolver: (identity) async => resolveOpsActor(
    directoryUserId: identity.directoryUserId!,
    tenantId: identity.tenantId,
  ),
);

The default strategy can create a basic UserActor, but a product resolver is where Directory identity becomes a product IAM principal with effective roles, groups, grants, and scope. Unknown authentication must not enumerate directory users.

Build the product permission inventory ​

dart
const permissions = IamProductDescriptor(
  product: 'ops',
  title: 'Operations',
  actions: [
    IamActionDescriptor(
      action: 'ops.area.governance.activate',
      title: 'Activate Area',
      entityName: 'area',
      facetName: 'governance',
    ),
    IamActionDescriptor(
      action: 'ops.auth.authentication.login',
      title: 'Sign In',
      resourceType: 'authentication',
      grantEditable: false,
    ),
  ],
);

The same qualified action keys feed grant evaluation and IAM administration. vyuh_iam_ui renders the action catalog, grant editor, principal matrices, role-level editing, and effective permissions. Effective allow/deny remains a server-side decision even when the UI hides or disables an action.

Re-verify a regulated signature ​

For an action whose ActionCapture requires signature evidence, the client opens the Studio electronic-signature dialog and asks the authority to verify credentials for the already authenticated identity. The provider's verifyCredentials receives the signer's access token beside the identity, so an authority that verifies passwords only within a session can present it.

The authority returns a one-time BlueprintElectronicSignatureAssertion with:

  • signerId and signerDisplayName;
  • actionDigest and human meaning;
  • authenticatedAt and expiresAt;
  • a one-time ElectronicSignatureProof.

The action request carries that proof. BlueprintElectronicSignatureVerifier checks the proof, action digest, signer, and expiry before the target action is allowed to execute. Reauthentication does not replace the active auth session or ask the client to assert signer facts.

Keep public recovery non-enumerating ​

Password reset and administrator-contact actions return safe acceptance receipts. Providers and handlers must respond consistently for known and unknown identifiers. Support topics use stable wire values while the UI shows human labels.

Checkpoint ​

Prove the complete chain:

  1. provider discovery is filtered by tenant and environment;
  2. login returns an effective session policy;
  3. authentication resolves a Directory user and then a product actor;
  4. IAM evaluates the qualified action with tenant/site scope;
  5. a signature-required action binds a fresh assertion to its action digest;
  6. expired, mismatched, or reused signature proof fails closed;
  7. public reset/support flows reveal no account existence.

Next: Semantic composition.