Versions Compared

Key

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

Table of Contents
Specification


Apiary
Status
titlepost
mithril/api/tokens
Scopeno 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 ofONE OF:  

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

OR

  • grant_type = "password"
  • email
  • password
  • client_id
  • scope =
    Code Block
    title

    "CREATE_2FA_TOKEN"

    AUTHORIZE_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

    Peculiar properties of 2fa_access_token

     An 2FA flow feature implementation, we need restrict ability call WS: Approvals with `2fa_access_token`, and allow call WS: Approvals with `access_token ()`.

    It means the following 

    2FAReturn token_typescopes in token.details
    Disableaccess_token

    "scope": "app:authorize"

    Enable 2fa_access_token

    "scope": ""

    Change `2fa_access_token`  →  `access_token`access_token"scope": "app:authorize"

    OR " AUTHIRIZE_OTP"

    • token = 2fa_access_token
    • otp 

    OR "RESEND_OTP"

    • token = 2fa_access_token
    • grant_type = "resend_otp" 

    Logic WS

  • Validate token & scope
  • Check login existIf login exist
  • Get user by $.email
  • Validate user.password = $.password
  • If invalid -  Update



    Anchor
    CREATE_2FA_TOKEN
    CREATE_2FA_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

    Check ability login with invalid  password per same period.


    Logic WS

    • Check login exist
      • If login exist
      • Get user by $.email
      • Validate user `is_blocked` flag
        • if is_blocked = TRUE
        • return 401 - "User blocked" 
      • Validate user.password = $.password
      • Check ability login (see section on page below) 
    • 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.  
        • Add to response `urgent.next_step` = REQUEST_FACTOR
        • create new & return 2fa_access_token + code 201
      • If  exist 2FA active item for user with non-empty factor - go to new process authentication with 2FA
        • invoke OTP timeout procedure
        • If successful - invoke proc `create_OTP()`
        • If OTP successful sending: add to response `urgent.next_step` = REQUEST_OTP
          • else: add to response `urgent.next_step` RESEND_OTP
        • create new & return 2fa_access_token + code 201
      • If not exist 2FA active item for user  go to standart process without 2FA
        • Add to response `urgent.next_step` = REQUEST_APPS 
        • create new &return access_token+ code 201

    Response

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

    Check ability login

    Purpose 

    We store fail attempts (existing user/login + invalid password) in array at `user.priv_settings.login_hstr` and validate count such logins above limit.

    Logic 

    • Sort array `login_hstr[]` DESC by `time`
    • Get count() items with `type = "password"` from array for period from `now()` to `now() - MAX_FAILED_LOGINS_PERIOD`
    • IF count() >= MAX_FAILED_LOGINS THEN
      • return 401 error + message “You reached login attempts limit. Try again later”
    • ELSE
      • clear otp_hstr[] for `type = "password"`

    С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 401 - "User blocked" 
    • 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, 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
        • `users.priv_settings.
    login
        • otp_error_counter` (+1)
      • If `users.priv_settings.
    login
      • otp_error_
    counter` > USER_LOGIN
      • counter` > USER_OTP_ERROR_MAX
        • Blocked user - update user (set values) by $.user_id
          • is_blocked = TRUE
          • block_reason = "Passed invalid OTP
    verify attempts
          • more
    then 
          • than USER_
    LOGIN
          • 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 401 error "Invalid OTP"
      • 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_count > USER_OTP_ERROR_MAX 
          • Update OTP item
            • status ( NEW → UNVERIFIED)
            • updated_at = now()

    Response

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

    Anchor
    RESEND_OTP
    RESEND_OTP
    С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 401 - "User blocked"  
    • Get active 2FA item for non-blocked user by $.user_id

      Code Block
      languagesql
      SELECT *
      FROM authentication_factors AS 2FA
    • 	
    INNER
    • 
      
    JOIN user
    • WHERE 
    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 with 2FA (return 2fa_access_token + code 201)
  • If not exist 2FA active item for usergo to standart process without 2FA (return access_token+ code 201
      • not found - return 409 error "Not found 2FA data for user"
    • Extract type & factor from 2FA item for user
    • Termionate 2fa_access_token 
    • Create & return new 2fa_access_token 
    • invoke OTP timeout procedure
    • If successful - 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
    token
    • OTP successful create &
    return
    • send 
    • 4xx in other case