What this is about
The endpoints under /cias/registration/** can be reached without signing in. Anyone on the internet can call them, including someone who wants to find out which addresses have an account with you, or who wants to create tenants in bulk. That is why these endpoints reveal as little as possible and are throttled.
What an attacker sees
- Response 202
accepted - Email: “Please verify your address”
- Response 202
accepted - Email: invitation to join, or “You already have an account”
- Response 429
- no email
An attacker only sees the responses, not the emails. To them, new and known addresses look the same.
The responses of the public endpoints
| Endpoint | Response | When |
|---|---|---|
POST /self | 202 accepted | accepted, no matter whether the address is known |
| 400 | address invalid or request broken | |
| 422 | required field missing, unknown field, or a hook rejects | |
| 429 | throttled | |
POST /verify, POST /invitations/accept | 202 accepted | redeemed, or already redeemed earlier |
| 404 | link unknown, or expired while the registration is still waiting | |
GET /invitations/{token}/form | 200 or 404 | open fields, or link unknown |
GET /flows/{flow}/form | 200 | field description of a flow |
All errors have the same format: { "error": "cias.registration.…", "message": "…" }.
Why does a second click return 202 and not an error? Many email programs open links in advance to scan them for malware. This redeems the token before the person clicks. An error on the real click would confuse them. See Verify the email.
Throttling
Self-registration counts attempts in a sliding time window. When the limit is reached, CIAS responds with 429 before it checks or creates anything.
When: always
At most rate-limit attempts per address in rate-limit-window. Default and cias-runtime: 10 in 10 minutes. The hub sets 3 per hour.
Result: Protects a person's mailbox from being flooded with emails.
When: The installation sets client-ip-source to REMOTE_ADDR or HEADER.
A second counter per sender IP. The IP is never stored, but hashed with a salt. With HEADER, CIAS takes the first entry of the configured header, for example X-Forwarded-For. The hub sets HEADER, with 20 attempts in 10 minutes. In cias-runtime this counter is off.
Result: Protects against someone trying many different addresses from one machine.
POST /cias/registration/self (11th attempt in 10 minutes)429
{ "error": "cias.registration.rate-limited", "message": "too many attempts" }The response deliberately does not say when it works again. There is no Retry-After header. Whoever waits gets through again; whoever tries automatically gets no pace to follow.
The administrative endpoints (creation by administrators, invitations) are not throttled. There the caller is signed in and known.