Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents
Specification


Apiary
Status
titlepost
mithril/api/tokens
Scope

Purpose

Modification  existing WS: Create a user:

  • Add handling logic for return different type of tokens by 2FA_Statuses (2fa_access_token, access_token)
  • Add logic for process authorization 2 factor via OTP
  • Add logic for process resend OTP via create new token (grant_type = resend_otp)

Request parameters

Add handling logic for combinations of request parameters:

ONE OF:  

Code Block
titleCREATE_2FA_TOKEN
- grant_type = "password"
- email
- password
- client_id
- scope = "app:authorize"

OR

Code Block
titleAUTHORIZE_OTP
- grant_type = "authorize_2fa_access_token"
- token = 2fa_access_token
- otp 

OR

Code Block
titleRESEND_OTP
- grant_type = "refresh_2fa_access_token" 
- token = 2fa_access_token


Сase `CREATE_2FA_TOKEN` (grant_type = "password")

Purpose

Add handling logic for return different type of tokens by user 2FA Status.

  • if 2FA = disable, return access_token
  • if 2FA = enable, return 2fa_access_token , create & send OTP 

Logic WS

  • Check login exist
    • If login exist
      • Get user by $.email
      • Validate user.password = $.password
      • If invalid -  Update user (set values) by $.user_id
        • Increment `users.priv_settings.login_error_counter` (+1)
        • If `users.priv_settings.login_error_counter` > USER_LOGIN_ERROR_MAX
          • Blocked user - update user (set values) by $.user_id
            • is_blocked = TRUE
            • block_reason = "OTP verify attempts more then USER_LOGIN_ERROR_MAX"
            • updated_at = now()
  • Validate user `is_blocked` flag
    • if is_blocked = TRUE
    • return 4xx - "User blocked"  (!!! TBD)
  • Get active 2FA item for non-blocked user by $.user_id

    Code Block
    languagesql
    SELECT *
    FROM authentication_factors AS 2FA
    	INNER JOIN user AS U
    		ON 2FA.user_id = U.id
    WHERE 
    	U.id = $.user_id
    		AND U.is_active = TRUE
    		AND U.is_blocked = FALSE
    		AND 2FA.is_active = TRUE
    • If exist 2FA active item with empty factor.  
      • !!! TBD - Need get factor from user (WS: Set2FA)
      • ....
    • If  exist 2FA active item for user with non-empty factor - go to new process authentication with 2FA
      • invoke proc `create_OTP()`
      • return 2fa_access_token + code 201
    • If not exist 2FA active item for user  go to standart process without 2FA
      • return access_token+ code 201

Response

  • 201 if token successful create & return
  • 4xx in other case

Сase `AUTHORIZE_OTP` (grant_type = "authorize_2fa_access_token")

Purpose

Verify OTP & prolongation authentication process (return access_token for getting approvals) .

Logic WS

  • Validate token (2fa_access_token) for grant+_type "authorize_2fa_access_token" 
    • If invalid - return error 4xx 
  • Validate user id & user status
  • Validate user `is_blocked` flag
    • if is_blocked = TRUE
    • return 4xx - "User blocked"  (!!! TBD)
  • Get active 2FA item for non-blocked user by $.user_id

    Code Block
    languagesql
    SELECT *
    FROM authentication_factors AS 2FA
    WHERE 
    	2FA.user_id = $.user_id
    		AND 2FA.is_active = TRUE
    • If not found - return 409 error "Not found 2FA data for user"
  • Extract type & factor from 2FA item for user
  • Invoke internal function `verify_OTP (key key, code)`, for 2FA.type = SMS, with params:
    • key = token + 2FA.faсtor
    • code = $.otp
  • Get result of call `verify_OTP()`  
  • If result = VERIFIED
    • Update user (set values) by $.user_id
      • users.priv_settings.otp_error_counter = 0

    • Update 2fa_access_token (set `tokens.details.used`=true)
    • Create & return new access_token (as a existing standart process without 2FA)
    • Return 201
  • If result = UNVERIFIED
    • Update user (set values) by $.user_id
      • Increment `users.priv_settings.otp_error_counter` (+1)
    • If `users.priv_settings.otp_error_counter` > USER_OTP_ERROR_MAX
      • Blocked user - update user (set values) by $.user_id
        • is_blocked = TRUE
        • block_reason = "OTP verify attempts more then USER_OTP_ERROR_MAX"
        • updated_at = now()
    • return 401 error

Internal logic for `verify_OTP()`

  • Find 1 active OTP (status = NEW) for $.key
    • If not found - return 409 error "Not found active OTP" - ??? or 401 ?
    • If found -  increment  `attempts_count` (+1) for this OTP
      • If OTP.code = $.code - update OTP item:
        • OTP status  ( NEW → VERIFIED)
        • updated_at = now()
      • If (OTP.code <> $.code ) AND (OTP.attempts_countOTP_ERROR_MAX 
        • Update OTP item
          • status ( NEW → UNVERIFIED)
          • updated_at = now()

Response

  • 201 if OTP successful verifiyng, create & send 
  • 4xx in other case

Сase ` RESEND_OTP` (grant_type = "refresh_2fa_access_token")

Purpose

Refresh (create new) 2fa_access_token, create &send new OTP item for user via current 2FA factor.

Logic WS

  • Validate token (2fa_access_token) for grant_type = "refresh_2fa_access_token"
    • If invalid - return error 4xx 
  • Validate user id & user status
  • Validate user `is_blocked` flag
    • if is_blocked = TRUE
    • return 4xx - "User blocked"  (!!! TBD)
  • Get active 2FA item for non-blocked user by $.user_id

    Code Block
    languagesql
    SELECT *
    FROM authentication_factors AS 2FA	
    WHERE 
    	2FA.user_id = $.user_id
    		AND 2FA.is_active = TRUE
    • If not found - return 409 error "Not found 2FA data for user"
  • Extract type & factor from 2FA item for user
  • Update 2fa_access_token (set `tokens.details.used`=true)
  • Create & return new 2fa_access_token 
  • Invoke internal function `create OTP (key)`, for 2FA.type = SMS, with params:
    • key = token + 2FA.faсtor
    • Get result of call `create OTP()` as `OTP_value` 
  • Sending (delivery) OTP via channel communication 
    • for 2FA.type = SMS - via SMS gateway API
      • mobile phone = 2FA.factor
      • SMS text = OTP_value 
  • Return 201

Internal logic for `create OTP()`

  • Processed OTP lists for $.key
    • Deactivate all active OTP items (NEW → CANCELED)
  • Create new OTP item
    • status = NEW
    • expired_at = now() + OTP_LIFETIME
    • value = generate number according OTP_LENGTH
    • updated_at = now()

Response

  • 201 if OTP successful create & send 
  • 4xx in other case