Skip to content

Pre-GA Design Partner and Early Access only. Request access

Helix Docs

ctx.identity

Reference for ctx.identity with the user and org shape, roles as the authorization source, null on excluded paths, and dev identity injection.

For developers Updated Aug 5, 2026
View as Markdown

ctx.identity carries the authenticated user and organization for HTTP-triggered functions when Helix Identity is enabled. On paths excluded from authentication it is null instead of throwing, so handlers can serve both authenticated and anonymous requests. Schedules, app triggers, and queue consumers run without a user; accessing ctx.identity there throws a clear error.

IdentityContext

interface IdentityContext {
  user: {
    id: string;            // Helix user ID
    email: string;
    name: string;
    groups: string[];      // IdP-synced group names, informational only
    roles: string[];       // role IDs from helix.config.ts, use for authorization
  };
  org: {
    id: string;            // Helix org ID
    name: string;
  };
}

ctx.identity is typed IdentityContext | null.

FieldTypeDescription
user.idstringHelix user ID. Also mirrored on the top-level userId execution metadata field.
user.emailstringThe user’s email address
user.namestringDisplay name
user.groupsstring[]IdP-synced group names. For logging and display only; never branch authorization on groups.
user.rolesstring[]Role IDs declared in helix.config.ts, resolved from direct assignments plus group-to-role mappings. The authorization source.
org.idstringHelix organization ID
org.namestringOrganization name

Roles are the app’s authorization vocabulary; groups are an IdP concern. The platform maps IdP groups to roles, and your code should only reason in roles: declarative restrictions via the access named export, or user.roles.includes(...) for branching inside a handler.

Example

// functions/api/v1/dashboard.get.ts
import { defineFunction } from '@trayai/helix-sdk';

export default defineFunction(async (ctx) => {
  if (!ctx.identity) {
    // Reachable only on paths excluded from authentication
    throw ctx.error(401, 'Sign-in required');
  }

  const { user, org } = ctx.identity;
  ctx.log.info('Dashboard accessed', { userId: user.id, org: org.name });

  const isAdmin = user.roles.includes('admin');
  return isAdmin ? getFullDashboard(ctx) : getLimitedDashboard(ctx);
});

Identity in local development

helix dev has no SSO flow in front of it. The dev server injects a static dev identity into every request, so ctx.identity is always populated locally. The default dev user (dev-user, dev@localhost) holds every role declared in config, so no route is blocked out of the box. To test as a specific persona, pass an identity JSON file with helix dev --identity <path>, or override a single request with the X-Helix-Dev-Roles and X-Helix-Dev-User headers (dev only; the Helix edge strips them in production). A file containing just null simulates an unauthenticated request.

Role definitions, the access export, enablement, and excluded paths are covered in the identity and roles guide.

Loading search…

Jump to a section

tab to move · esc to close