kislerdm / diagramastext

Application to streamline diagram generation using plain English instructions instead of code
https://diagramastext.dev
Apache License 2.0
26 stars 1 forks source link

As a user, I want to have extended usage plan #64

Open kislerdm opened 1 year ago

kislerdm commented 1 year ago

Problem

All users have a single plan for now. Many users would want to extend the quota, or have case-tailored usage conditions.

Proposed Solution

Implement user's authentication and authorisation using JWT.

References:

End-to-end flow: webclient

sequenceDiagram
     User->>Webclient: Initiates diagram's generation
     Webclient-->>Browser Cookies: Look up JWT tokens

alt "Login/Sign-in flow", the refresh token not found
     Webclient->>User: Login page
alt User opts-in for login
     User->>Webclient: Enter email address 
     Webclient-->Webclient: Login page's state transition: "input secret"
     Webclient->>AuthService: Call "/auth/email"

else User wants to continue without authN
     Webclient-->Webclient: Generate user's "fingerprint"
     Webclient->>AuthService: Call "/auth/anonym"
end

     AuthService-->AuthService: Validate request

alt User exist
     AuthService-->>DB: Lookup the user and status
     alt email_verified==true && active==false
        AuthService->>Webclient: Reply: 401
        Webclient->>Webclient: Error popup
     end
else User does not exist
      AuthService-->>DB: Create user: email_verified is false, active is false
end
     AuthService-->AuthService: Generate JWT ID token and secret
     AuthService-->>Cache: Store secret
     AuthService-->>SMTP: Dispatch the secret
     AuthService->>Webclient: Response: user_id+secret expiration time
     SMTP->>User: Deliver email with the secret     
     User->>Webclient: Input secret
     Webclient-->Webclient: Validate secret's expiration
     Webclient->>AuthService: Call "/auth/confirm"
     AuthService-->>Cache: Lookup secret
     AuthService-->AuthService: Validate secret
     AuthService-->>DB: Update user: email_verified is true, active is true
     AuthService-->AuthService: Generate JWT: Access and Refresh tokens
     AuthService->>Webclient: Response: JWT tokens
     Webclient-->>Browser Cookies: Store JWT tokens

else Referesh token found
     Webclient->>AuthService: Call "/auth/refresh"
     AuthService-->AuthService: Validate token

     alt Token is invalid
       AuthService->>Webclient: Response: 401
       Webclient->>Webclient: Error popup, init the "login/sign-in flow"
    else Token is valid
       AuthService-->>DB: Lookup the user and status
      alt active==true
         AuthService-->AuthService: Generate JWT: Access tokens
         AuthService->>Webclient: Response: Send JWT tokens
         Webclient-->>Browser Cookies: Store JWT tokens
      else active==false
        AuthService->>Webclient: Response: 401
        Webclient->>Webclient: Error popup, init the "login/sign-in flow"          
       end
    end

end

Contract

/auth/signin/init

Request:

Method Schema
POST ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "/auth/email:request", "type": "object", "required": [ "email" ], "additional_fields": false, "properties": { "email": { "title": "User's email", "type": "string", "pattern": "^[a-zA-Z0-9+_.-]+@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$" } } } ```

Responses:

Status Schema Reason
200 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "/auth/email:response-200", "type": "object", "required": [ "user_id", "exp" ], "additional_fields": false, "properties": { "user_id": { "title": "User ID", "type": "string", "format": "uuid" }, "exp": { "title": "Timestamp when the secret expires", "type": "integer", "minimum": 0 } } } ``` Secret sent to user's email
400 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Faulty input
401 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` User is deactivated
422 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Faulty email, the secret cannot be sent
500 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Internal server error

/auth/anonym

Request:

Method Schema
POST ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "/auth/anonym:request", "type": "object", "required": [ "fingerprint" ], "additional_fields": false, "properties": { "fingerprint": { "title": "User's fingerprint", "type": "string", "pattern": "^[a-f0-9]{40}$" } } } ```

Responses:

Status Schema Reason
200 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "/auth/anonym:response-200", "type": "object", "required": [ "token_refresh", "token_access" ], "additional_fields": false, "properties": { "token_refresh": { "title": "JWT refresh token", "type": "object", "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$" }, "token_access": { "title": "JWT access token", "type": "object", "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$" } } } ``` OK
400 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Faulty input
500 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Internal server error

/auth/signin/confirm

Request:

Method Schema
POST ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "/auth/signin/confirm:request", "type": "object", "required": [ "user_id", "secret" ], "additional_fields": false, "properties": { "user_id": { "title": "User ID", "type": "string", "format": "uuid" }, "secret": { "title": "Authentication secret", "type": "string", "pattern": "^[0-9]{6}$" } } } ```

Responses:

Status Schema Reason
200 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "/auth/confirm:response-200", "type": "object", "required": [ "token_refresh", "token_access" ], "additional_fields": false, "properties": { "token_refresh": { "title": "JWT refresh token", "type": "string", "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$" }, "token_access": { "title": "JWT access token", "type": "string", "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$" } } } ``` OK
400 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Faulty input
401 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Unauthorized
500 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Internal server error

/auth/refresh

Request:

Method Schema
POST ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "/auth/refresh:request", "type": "object", "required": [ "token_refresh" ], "additional_fields": false, "properties": { "user_id": { "title": "JWT refresh token", "type": "string", "format": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$" } } } ```

Responses:

Status Schema Reason
200 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "/auth/refresh:response-200", "type": "object", "required": [ "token_access" ], "additional_fields": false, "properties": { "token_access": { "title": "JWT access token", "type": "object", "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$" } } } ``` OK
400 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Faulty input
401 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Unauthorized
500 ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Error", "type": "object", "required": [ "error" ], "additional_fields": false, "properties": { "error": { "title": "Error message", "type": "string" } } } ``` Internal server error