Organisations, roles, and invites for B2B applications
A B2B route needs two verified facts: the signed-in user and that user's active membership in the organisation being accessed. A valid session establishes the first fact only. An organisation ID from a route parameter or request body does not establish membership, so every tenant-scoped read and mutation must resolve the membership on the server and constrain the application query to the same organisation.
Make membership the tenant boundary
Own Auth represents the user-to-organisation relationship as a membership with a status and role. One user can hold different roles in several organisations, and removing one membership does not delete the account or affect other tenants. Protected routes should load the current membership before application data and reject inactive or missing memberships.
Keep the organisation constraint in the query that reads or changes the resource. Fetching a project by global ID and checking only that the caller belongs to some organisation leaves an object-level authorization gap. OWASP classifies this pattern as broken object-level authorization because changing one identifier can expose another tenant's record. Reusable repository or service methods that require organisationId make the constraint harder to omit from a new endpoint.
Client route guards and hidden controls can reflect the result, but the server operation remains the enforcement point. Resolve the actor from the verified session instead of accepting a user ID, membership, or role from the client. Apply the same tenant condition to reads, writes, exports, background jobs, and API-key requests.
Roles and assignment rules
Own Auth includes fixed owner, admin, and member roles with permissions for organisation management, invitations, members, audit events, sessions, and API keys. Its organisation methods enforce the documented permissions for their own operations. The application still owns authorization for product resources such as projects, invoices, environments, and customer data.
Role assignment needs a rule separate from permission to send invitations. Own Auth 0.3.6 prevents a non-owner from assigning the built-in owner role through auth.inviteMember. An application that introduces its own role vocabulary must validate which roles the actor may assign before calling the method. Treating invite_members as unrestricted assignment authority can turn an administrative convenience into privilege escalation.
Protect the last-owner invariant. Removing or demoting the final owner can leave an organisation without a principal able to restore administrative control. Ownership transfer, member removal, and role changes should use server-side permission checks and create audit events containing the actor, target member, previous role, and new role. A changed role must affect the next protected operation rather than waiting for a long-lived client claim to expire.
Treat an invitation as a temporary credential
auth.inviteMember records the organisation, normalized email address, role, inviter, status, and expiry. The invitation token carries the ability to create that membership, so the complete URL and token must stay out of request logs, analytics, support tools, and persistent browser storage. Client-controlled query parameters must not override the organisation or role stored with the invitation.
- Resolve the inviter from a verified session and validate that the actor may assign the requested role.
- Call
auth.inviteMemberso the configured email provider sends the generated link without exposing it to application logs. - Require the recipient to sign in or create an account.
- Pass the token and verified user ID to
auth.acceptInvite. - Clear any short-lived server-managed pending-invite state after success or terminal failure.
auth.acceptInvite checks that the signed-in user's email matches the invited address, atomically consumes the token, and creates or reactivates the membership. Concurrent acceptance attempts admit one successful consumer. Expired, revoked, accepted, and consumed invitations remain unusable. auth.revokeInvitation invalidates a pending invitation before acceptance.
Removal and derived credentials
Member removal must stop tenant access on the next protected request. Do not rely on an organisation role copied into a long-lived client token. Load current membership for sensitive operations, or use a cache whose invalidation is tied to membership changes and meets the product's revocation requirement.
Offboarding must also address credentials whose authority came from the removed membership. Revoke or constrain user-owned API keys, integration grants, active tenant sessions, and pending invitations according to that ownership model. Organisation-owned keys should survive an individual departure only when they were deliberately issued as shared infrastructure. Keep the account intact when the user still belongs to other organisations.
Audit evidence
Retain events for organisation creation and deletion, invitation creation and revocation, invitation acceptance, role changes, member removal, and ownership transfer. Events should identify the actor, target, organisation, and resulting role without storing invitation tokens or auth URLs. This record lets an investigator distinguish a valid membership change from an object-level authorization failure.
Release tests should exercise the same resource with two users in two organisations, each built-in role, a removed member, an expired invite, sequential token replay, and concurrent invite acceptance. These cases verify the tenant query constraint, role enforcement, immediate membership removal, and atomic token consumption rather than only confirming that the happy-path invitation screen works.