Skip to content

Identity Sync Events

Detailed information about what each event does and how it's handled.

Subject Events

Subject events fire when identity lifecycle changes occur at the IDP level.

subject.created

Fires when a new user registers or is created in AuthVital.

// Incoming event payload
{
  event: 'subject.created',
  data: {
    sub: 'user-abc-123',
    email: 'jane@example.com',
    email_verified: true,
    preferred_username: 'janesmith',
    name: 'Jane Smith',
    given_name: 'Jane',
    family_name: 'Smith',
    middle_name: null,
    nickname: 'Janey',
    picture: 'https://example.com/avatar.jpg',
    website: 'https://janesmith.com',
    gender: 'female',
    birthdate: '1990-05-15',
    zoneinfo: 'America/New_York',
    locale: 'en-US',
    phone_number: '+1-555-123-4567',
    phone_number_verified: false,
    tenant_id: 'tenant-xyz',
    app_role: 'member',
    groups: ['engineering', 'frontend'],
  }
}

// Handler creates identity
await prisma.identity.create({
  data: {
    id: 'user-abc-123',
    email: 'jane@example.com',
    emailVerified: true,
    username: 'janesmith',
    displayName: 'Jane Smith',
    givenName: 'Jane',
    familyName: 'Smith',
    // ... all other OIDC fields
    tenantId: 'tenant-xyz',
    appRole: 'member',
    groups: ['engineering', 'frontend'],
    isActive: true,
    hasAppAccess: true,
  },
});

subject.updated

Fires when user profile data changes. Only updates changed fields!

// Incoming event payload
{
  event: 'subject.updated',
  data: {
    sub: 'user-abc-123',
    email: 'jane.smith@newcompany.com',  // Changed
    given_name: 'Jane',
    family_name: 'Smith-Johnson',         // Changed
    changed_fields: ['email', 'family_name'],  // Tells us what changed
    // ... other fields
  }
}

// Handler updates ONLY changed fields
await prisma.identity.update({
  where: { id: 'user-abc-123' },
  data: {
    email: 'jane.smith@newcompany.com',
    familyName: 'Smith-Johnson',
    syncedAt: new Date(),
  },
});

subject.deleted

Fires when a user is permanently deleted from AuthVital.

// Handler deletes identity (cascades to sessions due to onDelete: Cascade)
await prisma.identity.delete({
  where: { id: 'user-abc-123' },
});

subject.deactivated

Fires when a user's account is deactivated at the IDP level (cannot log into ANY app).

// Handler sets isActive = false
await prisma.identity.update({
  where: { id: 'user-abc-123' },
  data: {
    isActive: false,
    syncedAt: new Date(),
  },
});

Member Events

Member events fire when a user's relationship with a tenant changes.

member.joined

Fires when a user joins a tenant/organization.

// Incoming event payload
{
  event: 'member.joined',
  data: {
    sub: 'user-abc-123',
    tenant_id: 'tenant-xyz',
    role: 'editor',
    groups: ['design-team', 'all-hands'],
  }
}

// Handler updates tenant context
await prisma.identity.update({
  where: { id: 'user-abc-123' },
  data: {
    tenantId: 'tenant-xyz',
    appRole: 'editor',
    groups: ['design-team', 'all-hands'],
    syncedAt: new Date(),
  },
});

member.left

Fires when a user leaves a tenant/organization.

// Handler clears tenant context
await prisma.identity.update({
  where: { id: 'user-abc-123' },
  data: {
    tenantId: null,
    appRole: null,
    groups: [],
    syncedAt: new Date(),
  },
});

member.role_changed

Fires when a user's role within a tenant changes.

// Handler updates role and groups
await prisma.identity.update({
  where: { id: 'user-abc-123' },
  data: {
    appRole: 'admin',  // Promoted!
    groups: ['design-team', 'all-hands', 'leadership'],  // New groups
    syncedAt: new Date(),
  },
});

App Access Events

App access events fire when a user's access to YOUR SPECIFIC APPLICATION changes.

app_access.granted

Fires when a user is granted access to THIS specific application.

// Handler enables app access
await prisma.identity.update({
  where: { id: 'user-abc-123' },
  data: {
    hasAppAccess: true,
    appRole: 'viewer',  // Initial role for this app
    syncedAt: new Date(),
  },
});

app_access.revoked

Fires when a user's access to THIS specific application is revoked.

// Handler disables app access
await prisma.identity.update({
  where: { id: 'user-abc-123' },
  data: {
    hasAppAccess: false,
    appRole: null,  // Clear app role
    syncedAt: new Date(),
  },
});

app_access.role_changed

Fires when a user's role within THIS application changes.

// Handler updates app role
await prisma.identity.update({
  where: { id: 'user-abc-123' },
  data: {
    appRole: 'editor',  // Changed from 'viewer'
    syncedAt: new Date(),
  },
});

Event Summary

Event Affects Key Fields
subject.created New identity All OIDC fields
subject.updated Existing identity Only changed_fields
subject.deleted Identity record Deletes record
subject.deactivated Identity status isActive = false
member.joined Tenant membership tenantId, appRole, groups
member.left Tenant membership Clears tenant fields
member.role_changed Tenant role appRole, groups
app_access.granted App access hasAppAccess = true, appRole
app_access.revoked App access hasAppAccess = false
app_access.role_changed App role appRole