github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/design/verifiable-fields/README.md (about) 1 ## Story 2 - As a Heeus app developer I want to declare fields like Email and phone numbers that must be verified 3 - As a Heeus app developer I want to verification be limited by tries amount or whatever to eliminate security holes 4 5 ## Solution principles 6 - verifiable fields are verified by 6-digit code got by crypto-safe randomize algorhythm 7 - case with a link sent via email instead of code is bad because it could cause e.g. multiple payments after multiple opening the link 8 - deny Token usage in a wrong WSID 9 - Rate Limiter API is used to limit rates: 10 - `q.sys.InitiateEmailVerification` calls - not often than 100 times per hour per workspace (profile) 11 - `q.sys.IssueVerifiedValueToken` calls - not often than 3 times per hour per workspace (profile) 12 - code verification passed -> counter is reset to zero 13 - `q.sys.InitiateEmailVerification` and `q.sys.IssuerVerifiedValueToken` are called at targetApp/profileWSID - to protect against unauthenticated users 14 - to e.g. reset password these funcs should be called with sys auth via helper funcs like `q.sys.InitiateResetPasswordByEmail` 15 16 ## Rates 17 18 ```mermaid 19 flowchart LR 20 21 Token:::H 22 IBucket:::S 23 24 25 Profile[(Profile)]:::H 26 Profile --- InitiateEmailVerification["q.sys.InitiateEmailVerification()"]:::S 27 InitiateEmailVerification --- bucketsIEV 28 subgraph bucketsIEV["InitiateEmail buckets"] 29 direction LR 30 Bucket["Bucket[targetApp, 'InitiateEmailVerification', profileWSID]"]:::H 31 end 32 Profile --- IssueVerifiedValueToken["q.sys.IssueVerifiedValueToken(targetID)"]:::S 33 IssueVerifiedValueToken --- bucketsIVVT 34 35 subgraph bucketsIVVT["IssueVerifiedValueToken buckets"] 36 direction LR 37 Bucket3["Bucket[targetApp, 'IssueVerifiedValueToken', profileWSID]"]:::H 38 end 39 40 41 IssueVerifiedValueToken -.-> Token[VerifiedValueToken] 42 43 44 TargetWS[(TargetWS)]:::H 45 UsingToken:::B 46 47 bucketsIVVT --- IBucket 48 bucketsIEV --- IBucket 49 50 Token -.-> UsingToken[Using VerifiedValueToken] 51 52 UsingToken --- TargetWS 53 54 classDef B fill:#FFFFB5 55 classDef S fill:#B5FFFF 56 classDef H fill:#C9E7B7 57 classDef G fill:#FFFFFF,stroke:#000000, stroke-width:1px, stroke-dasharray: 5 5 58 ``` 59 60 ## Limitations 61 - it is unclear how to control the rate per ID when a doc is created 62 - once obtained Verified Value could be used an ulimited number of times during the token validity time (10 minutes). 63 - not a problem, ok to reset password for the login many times during 10 minutes 64 65 ## Functional design 66 Declare a schema with a verified field: 67 ```go 68 AppConfigType.AppDef.Add(QName, e.g. appdef.TypeKind_CDoc). 69 AddVerifiedField(name, kind, false, e.g. appdef.VerificationKind_EMail) 70 ``` 71 72 Issue verification token and code: 73 ```go 74 token, code, err := verifier.NewVerificationToken(entity, field, email, e.g. appdef.VerificationKind_EMail, targetWSID, ITokens, IAppTokens) 75 ``` 76 77 Issue verified value token: 78 ```go 79 verifiedValueToken, err := verifier.IssueVerifiedValueToken(token, code) 80 ``` 81 82 ## Technical design 83 84 ```mermaid 85 sequenceDiagram 86 participant c as Client 87 participant p as targetApp/profileWSID 88 participant tp as targetApp/targetWSID 89 c->>p: q.sys.InitiateEmailVerification(entity, field, email, targetWSID) (auth profile user) 90 activate p 91 opt if IAppStructs.IsFunctionRateLimitExceeded 92 p->>c: 429 to many requests 93 end 94 p->>p: generate token for targetWSID and code 95 p->>p: c.sys.SendEmailVerificationCode() (sys auth) 96 p->>c: token 97 deactivate p 98 99 note over p: Projector<A, SendEmailVerificationCodeProjector> 100 p->>p: send email 101 102 c->>p: q.sys.IssueVerifiedValueToken(token, code) (auth null) 103 activate p 104 opt if IAppStructs.IsFunctionRateLimitExceeded 105 p->>c: 429 too many requests 106 end 107 p->>p: decrypt token 108 p->>p: check code 109 alt code is ok 110 p->>p: reset rate limit 111 p->>c: 200 ok + verifiedValueToken 112 else code is wrong 113 p->>c: 400 bad request 114 end 115 deactivate p 116 117 c->>tp: use VerifiedValueToken (e.g. c.sys.ResetPassword(email: verifiedValueToken)) 118 activate tp 119 tp->>tp: decrypt token 120 opt wrong wsid 121 tp->>c: error 122 end 123 tp->>tp: set the value from the token to the target field 124 deactivate tp 125 ``` 126 ### Verifiable fields in application schema 127 ```mermaid 128 erDiagram 129 130 appdef_Schema ||..|| ResetPasswordByEmailUnloggedParams: "describes e.g." 131 appdef_Schema ||..|| CDocAirUserProfile: "describes e.g." 132 133 ResetPasswordByEmailUnloggedParams ||--|| authnz_Email: "has field" 134 air_Email ||..|| VerifiableField: is 135 authnz_Email ||..|| VerifiableField: is 136 CDocAirUserProfile ||--|| air_Email: "has field" 137 ResetPasswordByEmailUnloggedParams ||..|| cmd_ResetPasswordByEmail: "is an arg of" 138 ``` 139 140 ### Using Verified Value Token to set the value of the Verifiable field 141 ```mermaid 142 flowchart LR 143 144 verifiedValueTokenInCUD_request -->|decrypt to| VerifiedValuePayload 145 VerifiedValuePayload --> Entity_QName 146 VerifiedValuePayload --> Field 147 VerifiedValuePayload --> Value 148 Entity_QName -->|check| VerifiableField 149 Field -->|check| VerifiableField 150 Value -->|assign| VerifiableField 151 ``` 152 153 ```mermaid 154 erDiagram 155 156 VerifiedValuePayload ||--|| VerificationKind: has 157 VerifiedValuePayload ||--|| Entity: has 158 VerifiedValuePayload ||--|| Field: has 159 VerifiedValuePayload ||--|| Value: has 160 161 VerificationKind ||..|| kind: "checked for equality to" 162 Value ||..|| value: "assigned to" 163 Entity ||..|| QName: "checked for equality to" 164 Field ||..|| name: "checked for equality to" 165 166 167 value ||--|| VerifiedField: "is part of" 168 name ||--|| VerifiedField: "is part of" 169 kind ||--|| VerifiedField: "is part of" 170 171 VerifiedField }|--|| appdef_Schema: "belongs to" 172 QName ||..|| appdef_Schema: "refs" 173 174 appdef_Schema ||..|| ResetPasswordByEmailUnloggedParams: "e.g." 175 appdef_Schema ||..|| CDocAirUserProfile: "e.g." 176 177 ResetPasswordByEmailUnloggedParams ||--|| authnz_Email: "has field" 178 VerifiedField ||..|| air_Email: "can be" 179 VerifiedField ||..|| authnz_Email: "can be" 180 CDocAirUserProfile ||--|| air_Email: "has field" 181 ResetPasswordByEmailUnloggedParams ||..|| cmd_ResetPasswordByEmail: "is an arg of" 182 ```