command

Invite User

Invites a person into the CNA platform. Published by cna-nexus and cna-placement when a user is created or re-invited; handled by cna-one, which creates the person and employee and then requests application access from cna-auth.

Command Topic: UserKey: documentKey drift

Overview

Intent to invite a person into the platform so that they can sign in to an application. It is a command owned by AuthAuthDomainv1.0.0Identity and access bounded context. Owns the AUTH message contracts (application access grants, user invitations) and t...Ownercna-platformMapView docs, but the application that handles it is CNA OneCNA OneServicev1.0.0Franchise operating system (franchises and employees, pricing, products and learning books, school operations). Publishe...PublishesGrantApplicationAccess, EmployeeCreated +6SubscribesInviteUser, FranchiseCreated +9Ownercna-platformMapRepoView docs: it mirrors the invited person, gives them an employee record, and then asks CNA AuthCNA AuthServicev1.0.0Authentication and identity provider for the CNA platform (accounts, tokens, OTP/2FA, per-application access grants). Co...SubscribesGrantApplicationAccess, RevokeApplicationAccess +2Ownercna-platformMapRepoView docs for access through Grant Application AccessGrant Application AccessCommandv1.0.0Asks CNA Auth to give a person access to an application, provisioning the account and user when they are new. Published ...Ownercna-platformSchemaMapView docs. cna-auth itself never reads this topic.

When it is published.

  • CNA NexusCNA NexusServicev1.0.0Franchise network back-office (franchises, economic groups, partners, contracts and amendments, document storage, AI doc...PublishesInviteUser, ContractActivationRequested +13SubscribesContractActivationRequested, AmendmentApplyRequested +4Ownercna-platformMapRepoView docs, on two occasions: when a back-office user is created (createUser, inside manager.afterCommit(), so only after the user row and its notification preferences are committed) and when an existing user who never signed in is re-invited (inviteUser, which rejects users with a lastSignInAt or without a document). application is always the constant cna-nexus; document is the user’s CPF, 11 digits, validated before publishing.
  • CNA Placement TestCNA Placement TestServicev1.0.0English placement test application (candidates, tests, attempts, admin users). Publishes InviteUser and consumes nothing...PublishesInviteUserOwnercna-platformMapRepoView docs, when an admin user is created or re-invited (createUser → inviteUser, rejecting users who already signed in or whose invite was revoked or used). Its copy of the class does not follow this contract: see Known drift.

What the consumer does. cna-one (personConsumer, handler syncInviteUserToPerson):

  1. Normalises and validates document as a CPF. Anything else is rejected, which is how a placement invite ends: document is missing, the normalisation throws, the error is logged and sent to Sentry, and the message is dropped.
  2. Looks the Person up by document. Not found: builds a new person from name (split into name and surname) and email.
  3. If the person already has an Employee, writes nothing more. Otherwise, in one transaction, upserts the person and creates an employee with the invite email, active, with no franchise and no staff link. No EmployeeCreated is emitted for this employee: the access grant below is what provisions the login.
  4. On every arrival, new or known person, publishes Grant Application AccessGrant Application AccessCommandv1.0.0Asks CNA Auth to give a person access to an application, provisioning the account and user when they are new. Published ...Ownercna-platformSchemaMapView docs with the person, the employee, application from the payload and origin taken from the envelope of this message: today from metadata.owner, which the publishers hard-code to their own name (NEXUS, PLACEMENT, fallback ONE). Once the envelope carries producedBy, owner will be AUTH and cna-one must read the origin from producedBy (tracked in the drift list).

Failures are logged and reported to Sentry, never rethrown, so the partition never stalls and there is no retry.

Kafka

Topic (aggregateRoot)User
Message key (routingKey)payload.document
Contract ownerAUTH
metadata.eventInviteUser
Consumer groupcommon-person-events

Payload schema

Source of truth

export type InviteUserPayload = {
email: string;
name: string;
application: string;
document: string;
};
class InviteUser extends Event<InviteUserPayload> {
public static owner = "AUTH";
public static aggregateRoot = "User";
public static routingKey = "document";
}

Known drift

CopyownerroutingKeyPayloadNote
cna-auth (canonical)AUTHdocument{ email, name, application, document }Defined but never produced nor consumed here.
cna-nexusAUTHdocumentidenticalPublisher.
cna-oneONEdocumentidenticalConsumer. Wrong owner on the class; stored under one/user/ instead of auth/user/.
cna-placementAUTHemail{ email, name, employeeId? }Publisher. Different partition key; no document and no application, so cna-one drops the message with an error.

The placement divergence is a live contract break: a placement-originated invite never becomes a person or an access grant. Tracked in the drift list for a code fix.

Custom properties

PropertyValue
Contract Ownerx-contract-ownerAUTH
Kafka Topicx-kafka-topicUser
Message Keyx-message-keydocument
Sourcex-sourcecna-auth api/app/events/auth/user/InviteUser.ts
Driftx-driftFour copies disagree. cna-placement keys on email and sends { email, name, employeeId } without document or application; cna-one declares owner ONE; cna-auth defines the class but never produces or consumes it.
4 properties
emailstring
required

E-mail the person is invited on. Becomes the employee e-mail in cna-one and the login e-mail the access grant is provisioned on.

namestring
required

Full name. cna-one splits it into name (first word) and surname (the rest).

applicationstring
required

Identifier of the application the person is invited to, as registered in cna-auth (applications.identifier). cna-nexus always sends cna-nexus.

documentstring
required

Person document (CPF), 11 digits without mask. Kafka message key and the field cna-one correlates the person by.