CodamAIDocs
Topicdone

Verify the email

CIAS creates the account disabled, sends its own email with a one-time link, and enables the account only after the click. How the link is protected.

Variants
clicksecond click (same result)link unknown or expired → 404admin verifies manually

What this is about

Before someone may use an account, it must be clear that the address belongs to them. For this, CIAS sends an email with a link. Whoever clicks it proves that they can read the mailbox.

CIAS does this itself, not Keycloak. This way, the email, the link, and the flow are the same for all variants and traceable in CIAS.

The flow

sequenceDiagram
    participant C as CIAS
    participant K as Keycloak
    participant M as Email
    participant B as User
    C->>K: Create account: enabled=false, emailVerified=false
    C->>C: Generate token, store only the hash
    C->>M: Email with link …/registration/verify?token=…
    M-->>B: Email
    B->>C: POST /cias/registration/verify { token }
    C->>C: Compute hash, find registration, valid?
    C->>C: Token used, registration → VERIFIED
    Note over C: then approval or provisioning
    C->>K: emailVerified=true, enabled=true

Properties of the token

PropertyValueWhy
Length32 random bytes (256 bits), as 43 characters in the linkcannot be guessed
Storedonly the SHA-256 hashSomeone who reads the database cannot build a link from it
Validas long as the flow defines (token-ttl, default 24 hours)Links in old emails should not work forever
UseonceAfter that, the token is used up
Uniqueeach hash belongs to exactly one registration

A hash is a fingerprint: you can easily compute the hash from the token, but not the token from the hash.

The variants

What happens when you redeem the link

When: Token known, not expired, registration waiting for the click.

The token is marked as used, and the registration moves to VERIFIED. After that, it continues without further action: to approval, if the flow requires one, otherwise straight to provisioning.

Result: 202 { "status": "accepted" }

When: The token was already redeemed. Email programs often open links in advance to check them, and so redeem the token before the person does.

CIAS sees that the registration has already moved on and does nothing. The response is the same as for the first click.

Result: 202, no change.

When: The token does not exist, or it expired while the registration is still waiting for the click.

Both cases get the same response. Someone who guesses links learns nothing.

Result: 404 cias.registration.not-found. The person registers again and gets a new email.

When: The email does not arrive, and a platform administrator has checked the address in another way.

  1. 1
    Admin→CIAS
    POST /cias/admin/registrations/{id}/activate
  2. 2
    CIAS
    Is the registration waiting for the click?
  3. 3
    CIAS
    marks the token as used, even if it has expired, registration → VERIFIED
  4. 4
    CIAS
    continues as after a click

Result: Only for platform administrators.

Next

Sources in the code and the knowledge base
  • CIAS/cias-registration – TokenFactory, RegistrationToken, Registration (verify, confirmManually), RegistrationService (register, verify, activate), RegistrationLinks
  • CIAS/cias-iam-keycloak – KeycloakIdentityAdapter (createDisabledIdentity, markEmailVerified, enable)
  • CIAS/cias-registration – RegistrationServiceTest (second click), RegistrationEntity (token_hash)
  • CIAS/cias-registration/docs/adr – ADR-012
Search