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  ```