---
id: CAP-PFM-SEC-005
title: Join through an invitation
feature: FEAT-PFM-SEC-02
product_area: platform
module: security
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes: []
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-platform:docs/security/security-administration.md#14 Acceptance checkpoints (2026-10-11
    Final acceptance)
  - >-
    services-platform:docs/security/console-authentication.md#Acceptance checkpoints (2026-10-10 Final
    acceptance)
  - 'services-platform:docs/security/security-administration.md#2. Capability checklist (I1 to I10)'
purpose: >-
  An invited person establishes sign-in credentials and activates the offered access through the
  invitation sent to their address.
outcomes:
  - The invited person identifies the organization and account before joining.
  - Acceptance activates the membership and offered role assignments together.
  - Unusable links give recovery guidance without disclosing private account information.
concepts:
  - CON-PFM-SEC-003
actors:
  - ACT-PFM-002
  - ACT-PFM-006
surfaces:
  - console
  - api
preconditions: []
postconditions: []
permissions: []
rules:
  - id: BR-CAP-PFM-SEC-005-01
    proved_by: [ATS-PFM-SEC-005-01, ATS-PFM-SEC-005-16, TST-PFM-SEC-083, TST-PFM-SEC-210]
    statement: >-
      Invitation tokens are random, retained as hashes rather than raw values, expire after seven days
      and permit one activation.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/unit/services/invitations/invite-user.spec.js#expires the
      invitation 7 days after the execution's clock, on the invitation and on the user
    origin: >-
      services-platform:docs/security/security-administration.md#14 Acceptance checkpoints (2026-10-11
      Final acceptance)
    examples:
      positive: A valid invitation remains usable six days after issue.
      negative: An invitation used eight days after issue has passed its expiry.
  - id: BR-CAP-PFM-SEC-005-02
    proved_by: [ATS-PFM-SEC-005-02, TST-PFM-SEC-083, TST-PFM-SEC-209]
    statement: >-
      An unauthenticated invitation summary exposes only first name, masked e-mail, expiry, organization
      name and account kind.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/unit/services/invitations/invitation-summary.spec.js#answers
      the first name, a masked e-mail, the expiry, the organization and the kind of account, and nothing
      else
    origin: >-
      services-platform:docs/security/console-authentication.md#Acceptance checkpoints (2026-10-10 Final
      acceptance)
  - id: BR-CAP-PFM-SEC-005-03
    proved_by: [ATS-PFM-SEC-005-03, ATS-PFM-SEC-008-20, TST-PFM-SEC-037, TST-PFM-SEC-060, TST-PFM-SEC-205, TST-PFM-SEC-209]
    statement: >-
      Invalid, unknown, expired, revoked and superseded links use the same backend refusal message with
      reason codes and no organization details; an accepted link reports success again only while its
      membership remains active.
    kind: policy
    enforced_at:
      - service
      - console
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/unit/services/invitations/accept-invitation.spec.js#answers
      an expired, a revoked and a superseded token with the same sentence, and records which it was
    origin: >-
      services-platform:docs/security/console-authentication.md#Acceptance checkpoints (2026-10-10 Final
      acceptance)
  - id: BR-CAP-PFM-SEC-005-04
    proved_by: [ATS-PFM-SEC-005-04, ATS-PFM-SEC-005-14, ATS-PFM-SEC-005-15, ATS-PFM-SEC-005-17, ATS-PFM-SEC-005-19, ATS-PFM-SEC-005-20, ATS-PFM-SEC-006-11, TST-PFM-SEC-060, TST-PFM-SEC-205]
    statement: >-
      Password acceptance sets the invited account’s password before atomically activating membership and
      pending roles; password-policy refusal does not spend the invitation.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/unit/services/invitations/accept-invitation.spec.js#sets
      the chosen password in Cognito, then accepts, activates and makes the pending roles real in one
      transaction
    origin: >-
      services-platform:docs/security/security-administration.md#14 Acceptance checkpoints (2026-10-11
      Final acceptance)
  - id: BR-CAP-PFM-SEC-005-05
    proved_by: [ATS-PFM-SEC-005-05, TST-PFM-SEC-031, TST-PFM-SEC-083, TST-PFM-SEC-205]
    statement: >-
      Replaying a successfully accepted link for an active membership returns the prior success without
      another credential or activation write.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/unit/services/invitations/accept-invitation.spec.js#answers
      a reused token as the success it already was, without touching Cognito again
    origin: >-
      services-platform:docs/security/security-administration.md#14 Acceptance checkpoints (2026-10-11
      Final acceptance)
  - id: BR-CAP-PFM-SEC-005-06
    proved_by: [ATS-PFM-SEC-005-06, TST-PFM-SEC-118]
    statement: >-
      Acceptance rechecks the inviter’s present grant authority; lost authority leaves membership and
      roles inactive and the token unspent, while the applicant receives the neutral unusable-link
      refusal.
    kind: permission
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/integration/services/invitations/inviter-authority.integration.spec.js#refuses
      the acceptance once the administrator has lost the right, in the one sentence every bad link gets
    origin: >-
      services-platform:docs/security/security-administration.md#14 Acceptance checkpoints (2026-10-11
      Final acceptance)
  - id: BR-CAP-PFM-SEC-005-07
    proved_by: [ATS-PFM-SEC-005-07, TST-PFM-SEC-060, TST-PFM-SEC-207]
    statement: >-
      Google acceptance requires a live session for the correct pool, a verified matching e-mail and an
      unclaimed Google identity; expired or replayed sessions fail.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/unit/services/invitations/google-acceptance.spec.js#refuses
      a bearer token of another Cognito user, or one the pool does not verify (the nonce alone proves
      nothing)
    origin: >-
      services-platform:docs/security/console-authentication.md#Acceptance checkpoints (2026-10-10 Final
      acceptance)
  - id: BR-CAP-PFM-SEC-005-08
    proved_by: [ATS-PFM-SEC-005-08]
    statement: >-
      Google sign-in cannot create an unlinked console account without a live invitation-acceptance
      session, except for eligible active founding staff; refusals disclose no account details.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: documented
    source: >-
      services-platform:docs/security/console-authentication.md#(c) A person with a password account and
      no Google link signs in with Google — refused
    origin: >-
      services-platform:docs/security/console-authentication.md#Acceptance checkpoints (2026-10-10 Final
      acceptance)
  - id: BR-CAP-PFM-SEC-005-09
    proved_by: [ATS-PFM-SEC-005-09, TST-PFM-SEC-018]
    statement: >-
      The console provides no public registration, retired sign-up pages return not found and its pool
      denies self-registration.
    kind: policy
    enforced_at:
      - console
      - service
    must_prove: true
    provenance: test
    source: 'react-console:e2e/dev/sign-in.dev.spec.ts#offers no sign-up link, and the retired routes answer 404'
    origin: >-
      services-platform:docs/security/console-authentication.md#Acceptance checkpoints (2026-10-10 Final
      acceptance)
  - id: BR-CAP-PFM-SEC-005-10
    proved_by: [ATS-PFM-SEC-005-10, TST-PFM-SEC-013, TST-PFM-SEC-036]
    statement: >-
      The invitation screen blocks acceptance when a different console user is signed in and offers
      sign-out.
    kind: policy
    enforced_at:
      - console
    must_prove: true
    provenance: test
    source: >-
      react-console:e2e/dev/invitations.dev.spec.ts#refuses to accept an invitation while somebody else's
      console is on the screen
    origin: >-
      services-platform:docs/security/console-authentication.md#Acceptance checkpoints (2026-10-10 Final
      acceptance)
  - id: BR-CAP-PFM-SEC-005-11
    proved_by: [ATS-PFM-SEC-005-11, TST-PFM-SEC-083, TST-PFM-SEC-205]
    statement: >-
      Acceptance and identified invitation refusals produce audit events, successful acceptance names the
      joining person as actor, and tokens, hashes and passwords are excluded from the inspected audit and
      logging paths.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/unit/services/invitations/accept-invitation.spec.js#answers
      a malformed and an unknown token identically, without auditing a user nobody has identified
    origin: >-
      services-platform:docs/security/security-administration.md#14 Acceptance checkpoints (2026-10-11
      Final acceptance)
  - id: BR-CAP-PFM-SEC-005-12
    proved_by: [ATS-PFM-SEC-005-12, TST-PFM-SEC-083]
    statement: >-
      Reinvited revoked members accept onto the existing membership and pool account, preserving identity
      and enabling sign-in again.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#enables
      the same pool account at acceptance
    origin: >-
      services-platform:docs/security/security-administration.md#14 Acceptance checkpoints (2026-10-11
      Final acceptance)
scenarios:
  - id: SC-CAP-PFM-SEC-005-01
    given: An administrator has sent Dana a current invitation.
    when: 'Dana opens the link, checks its masked address and sets a password.'
    then: Dana can sign in with the access offered by the invitation.
    provenance: test
    source: >-
      react-console:e2e/dev/invitations.dev.spec.ts#shows who invited them, then sets their password and
      opens the console
  - id: SC-CAP-PFM-SEC-005-02
    given: Dana’s invitation has been replaced by a resend.
    when: Dana follows the earlier e-mail link.
    then: The screen directs Dana to the newest invitation rather than accept the superseded link.
    provenance: test
    source: >-
      react-console:e2e/dev/invitations.dev.spec.ts#sends somebody to their newest e-mail when a resend
      superseded the link they opened
edge_cases: []
constraints: []
depends_on:
  - CAP-PFM-SEC-008
related:
  - CAP-PFM-SEC-006
  - CAP-PFM-SEC-010
journeys:
  - JNY-PFM-SEC-002
open_questions: []
evidence:
  - >-
    Pinned services-platform references for the structured rows: docs/security/console-authentication.md;
    docs/security/security-administration.md;
    modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js;
    modules/security/tests/integration/services/invitations/inviter-authority.integration.spec.js;
    modules/security/tests/unit/services/invitations/accept-invitation.spec.js;
    modules/security/tests/unit/services/invitations/google-acceptance.spec.js;
    modules/security/tests/unit/services/invitations/invitation-summary.spec.js;
    modules/security/tests/unit/services/invitations/invite-user.spec.js.
  - >-
    Pinned react-console references for the structured rows: e2e/dev/invitations.dev.spec.ts;
    e2e/dev/sign-in.dev.spec.ts.
controls: [TC-AVL-001, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-006, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-003, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-SEC-001, TC-SEC-002, TC-SEC-003, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-SEC-02-01, FAS-PFM-SEC-02-02, FAS-PFM-SEC-02-05]
suites: [TST-PFM-SEC-013, TST-PFM-SEC-018, TST-PFM-SEC-031, TST-PFM-SEC-036, TST-PFM-SEC-037, TST-PFM-SEC-060, TST-PFM-SEC-083, TST-PFM-SEC-118, TST-PFM-SEC-205, TST-PFM-SEC-207, TST-PFM-SEC-209, TST-PFM-SEC-210]
---
# Join through an invitation

## Purpose

An invitation connects an administrator’s offer of access to the person who will use it. Acceptance helps the person recognize that offer and establish the credentials needed to join.

## How It Works

The recipient begins with the e-mail link and reviews the limited invitation summary. The page offers password setup and supported social linking. Someone sharing a browser with another console user first follows the sign-out guidance. An unsuccessful acceptance leaves the person on a screen explaining the available next step. Backend refusal wording stays neutral while reason codes let the console distinguish expired and replaced invitations. Successful setup leads into sign-in and workspace selection. Opening an accepted link can confirm that setup is complete, provided the membership remains active. Administrators use invitation and audit views to investigate problems the joining person cannot diagnose. A removed person can return through reinvitation without acquiring an unrelated second identity.

## Relationships

- Invitation administration creates the account and access offer.
- Sign-in establishes the subsequent console session.
- Scoped role assignment controls which pending grants acceptance activates.
