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.
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, insidemanager.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 alastSignInAtor without a document).applicationis always the constantcna-nexus;documentis 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):
- Normalises and validates
documentas a CPF. Anything else is rejected, which is how a placement invite ends:documentis missing, the normalisation throws, the error is logged and sent to Sentry, and the message is dropped. - Looks the
Personup by document. Not found: builds a new person fromname(split into name and surname) andemail. - If the person already has an
Employee, writes nothing more. Otherwise, in one transaction, upserts the person and creates an employee with the inviteemail, active, with no franchise and no staff link. NoEmployeeCreatedis emitted for this employee: the access grant below is what provisions the login. - 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,
applicationfrom the payload andorigintaken from the envelope of this message: today frommetadata.owner, which the publishers hard-code to their own name (NEXUS,PLACEMENT, fallbackONE). Once the envelope carriesproducedBy,ownerwill beAUTHand cna-one must read the origin fromproducedBy(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 owner | AUTH |
metadata.event | InviteUser |
| Consumer group | common-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
| Copy | owner | routingKey | Payload | Note |
|---|---|---|---|---|
| cna-auth (canonical) | AUTH | document | { email, name, application, document } | Defined but never produced nor consumed here. |
| cna-nexus | AUTH | document | identical | Publisher. |
| cna-one | ONE | document | identical | Consumer. Wrong owner on the class; stored under one/user/ instead of auth/user/. |
| cna-placement | AUTH | email | { 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
| Property | Value |
|---|---|
| Contract Ownerx-contract-owner | AUTH |
| Kafka Topicx-kafka-topic | User |
| Message Keyx-message-key | document |
| Sourcex-source | cna-auth api/app/events/auth/user/InviteUser.ts |
| Driftx-drift | Four 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. |
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.
Full name. cna-one splits it into name (first word) and surname (the rest).
Identifier of the application the person is invited to, as registered in cna-auth (applications.identifier). cna-nexus always sends cna-nexus.
Person document (CPF), 11 digits without mask. Kafka message key and the field cna-one correlates the person by.