You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(spec): define verification code lifetime per method (#55)
Adds section 9.5. Core computes expires_at from the method and the tenant's
configured TTL, within bounds core enforces. Defaults are 10 minutes for
phone OTP, 24 hours for domain email, and 21 days for postcard, which was
previously the only stated value.
Closes#38
Signed-off-by: Matthew Wren <info@origindev.com>
Copy file name to clipboardExpand all lines: docs/spec/events-and-adapters.md
+36-2Lines changed: 36 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -915,7 +915,7 @@ A code is printed on a postcard and mailed to the listed address. Strong evidenc
915
915
916
916
Implemented as a `postal.send` capability adapter. Print-and-mail APIs exist as a commercial service category; verify current pricing and availability independently. Expect roughly one to two dollars per piece and several days of delivery time.
917
917
918
-
- Code is 6 digits, valid **21 days**, single use.
918
+
- Code is 6 digits, single use. Lifetime is configurable per tenant within the bounds in §9.5; the default is 21 days.
919
919
- One postcard per listing per 30 days, rate-limited so an attacker cannot generate mail volume at the operator's expense.
920
920
- Emits `claim.verification_started` with `method: "postcard"`, then `postal.dispatched` on adapter confirmation.
921
921
-**Cost falls on the operator**, so the admin UI must show per-piece cost at the point of enabling it.
@@ -950,6 +950,40 @@ A competitor or ex-employee claiming a listing takes control of that business's
950
950
951
951
**Disputes go to moderation, never auto-transfer.** A second claim on an already-claimed listing emits `claim.disputed` and opens a `moderation.queued` item. Verification alone never moves ownership away from a sitting owner. Rate-limit claim attempts per IP and per account.
952
952
953
+
### 9.5 Verification code lifetime
954
+
955
+
**Core computes `expires_at`, never the caller.** A lifetime is a rule, and rules belong to core. `claim.verification_started` carries the computed value; an adapter that needs to tell a user when their code dies reads it from the event.
956
+
957
+
Lifetime is tenant-configurable within bounds core enforces. A tenant may tune it to its own audience; it may not configure a lifetime that makes the method meaningless.
`gbp_oauth` has no OSDS-side code; Google owns that session. `manual` has no code at all — an admin decides when they have seen enough.
968
+
969
+
```jsonc
970
+
// tenant configuration
971
+
"claim_verification": {
972
+
"enabled_methods": ["manual", "phone_otp"],
973
+
"ttl": {
974
+
"phone_otp_minutes":10,
975
+
"domain_email_minutes":1440,
976
+
"postcard_days":21
977
+
}
978
+
}
979
+
```
980
+
981
+
A value outside the bounds is rejected at configuration time, not silently clamped at use. The admin UI shows the bounds at the point of entry, and says why the ceiling exists — a code that outlives the session it was sent for is a code someone else can find later.
982
+
983
+
**Bounds are core's, not the tenant's.** They exist because a 48-hour SMS code and a 10-minute one are different security properties, and an operator tuning a form field is not making a security decision knowingly.
984
+
985
+
Attempt limits are a separate concern and are not specified here — see §15.6. `claim.verification_failed` carries `attempt`, which currently has no defined ceiling.
6.**Rate limiting and abuse** — public API limits, claim attempt limits, review submission limits.
1099
+
6.**Rate limiting and abuse** — public API limits, claim attempt limits, review submission limits. Includes a ceiling for the attempt counter on claim.verification_failed (§9.5).
0 commit comments