ЕСОЗ - публічна документація

[deprecated] 2-factor authentication - draft 01

Requirements

High-level tasks:

  • mithril model and API changes - ave phone number, get services. Changes in concept user, party employee, party_users.
  • Change Create employee process - add 2nd factor phone number, add phone number verification step
  • Change approve employee process - two-step approval, resend sms
  • Change login process - step, resend sms, otp counter, block user after n logins failed
  • Existing users migration - move the mobile phone number to the 2nd factor
  • Mithril admin changes - show 2nd factor, block/unblock user
  • Introduce user endpoints to MIS

Assumtions:

  • We keep using oauth 2.0 flow.
  • 2factor auth will be introduced for all users at the same time
  • mobile phone number for all the existing users should be automatically used as the 2nd factor
  • 2nd factor can be changed only via create employee request
  • 2nd factor - OTP delivered via sms
  • 2-factor auth can be applied to all the admin consoles: mithrill, uaddresses, nhs.admin - configurable

Configs

ParamsTypePurpose
OTP_ERROR_MAXinteger

Maximum for entered in-error trying for OTP verification process.  After excess of quantity entered in-error trying, status for 2FA automatically sets to BLOCED

OTP_LIFETIMEinteger Life-time for OTP item
OTP_LENGTHintegerLength of OTP value

Data structure

Store 2FA structure for User

  • Store parameters for 2FA in `mithri.users.priv_settings` as structure 

    {
    "2FA": {
    	"status": "VERIFIED",
    	"type": "PHONE",
    	"value": "+380937777777",
    	"otp_error_counter": 0
    	}
    }

    where:

    ValuePurpose
    statusStatus for 2FA
    typeFactor type for 2FA
    valueFactor value for 2FA
    otp_error_counterCount of errors for trying OTP authorization process
  • Dictionary `2FA_STATUSES`

    ValuePurpose
    INITInitial status for migration. May be deleted in place of `RESET`
    RESETReset authentication factor by admin action.
    VERIFIEDUser successful verify authentication factor via verify process.
    UNVERIFIEDUser don't verify authentication factor via verify process.
    DISABLEDisable 2FA for this user.
    BLOCKEDBlocked after N-unsuccessful attempts via authentication process.
  • Status chart for 2FA
  • Dictionary `2FA_TYPES`
    • PHONE

Store OTP structure for 2FA

  • Store OTP items for 2FA process in table `mithril.otp`.  Structure:

    ColumnTypePurpose
    id uuid
    user_iduuidUser
    valuevarcherUser successful verify authentication factor via verify process.
    expired_atintegerTimestamp which top expired (inserted_at + param from config)
    statusvarcharDictionary: OTP_STATUS
    inserted_attimestamp
    updated_attimestamp
  • Dictionary: `OTP_STATUS`

    ValuePurpose
    ACTIVEInitial status for newest OTP item. Ready to use in OTP verification process.
    USEDStatus for succesful OTP verification process
    EXPIREDExpired after OTP lifetime.
    INACTIVE

    Status for manual admin action. Need for generate new OTP at now.

  • Status Chart for OTP
  • ....

Process 

Login user  

Purpose

Exist user login process with 2FA with verified 2FA factor (2FA status = VERIFIED).

Steps 

  1. Create new token with user info (login, password).
  2. Get  2FA_status by user
    1. In case 2FA_status = DISABLE - go to standart process without 2FA (return access_token)
    2. In case 2FA_status = (INIT, RESET, UNVERIFIED) - go to verification 2FA process (return error TBD!!!)
    3. In case 2FA_status = BLOCKED - go to unblock 2FA process (return error TBD!!!)
  3. In case 2FA_status = VERIFIED - create & return temporary_access_token  (new token type !!!)
  4. With temporary_access_token we don't create Approvals (return error TBD !!!)
  5. When return temporary_access_token - automatically invoke WS:SendOTP .
  6. When call WS:SendOTP (temporary_access_token) which create new OTP item in status = ACTIVE, and send OTP via factor type & value
  7. FE/user call WS:AuthorizeOTP (temporary_access_token, OTP) which validate OTP in payload with OTPs for this token
    1. In case unsuccessful validation- return error 
    2. In case successful validation- used temporary_access_token & return new generated access_token.
  8. With access_token from response, FE/user will call WS:CreateApprovals


Services specifications

WS: SendOTP (new EP)

Purpose

Create new OTP item & sending OTP value via OTP factor.

Request parameters

  • token

Logic WS

  • Validate token
  • Extract user from token
  • Validate 2FA_Status
    • If 2FA_Status = DISABLE return error 409 
    • if 2FA_Status = BLOCKED return error 401 
  • Processed OTP lists for user
    • Deactivate all active OTP items (ACTIVE → INACTIVE)
  • Create new OTP item
    • status = ACTIVE
    • expired_at = now() + OTP_LIFETIME
    • value = invoke system procedure Generate_new_otp()
    • inserted_at = updated_at = now()
  • Sending OTP
    • via SMS for `2FA.type`=PHONE
    • for phone number  from 2FA.value
    • text SMS from`OTP.value`
  • ...

Response

  • 201 if OTP creating & sending successful
  • 4xx in other case


WS: AuthorizeOTP (new EP)

Purpose

Authorize with OTP (after Authorize with login/password and getting temporary_access_token ).

Request parameters

  • token
  • checking_otp

Logic WS

  • Validate token
  • If token_type <> temporary_access_token - return error 401
  • Extract user from token
  • Validate 2FA_Status
    • If 2FA_Status = DISABLE return error 409 
    • if 2FA_Status = BLOCKED return error 401 
    • if 2FA_Status = INIT, RESET, UNVERIFIED return error 401
  • If  2FA_Status = VERIFIED:
    • Processed OTP lists for user:
      • If not found any ACTIVE OTP - return 409/401 (!!! TBD)
      • If found ACTIVE OTP - verify OTP:
        • If checking_otp=`otp.value` - processing:
          • update OTP (set `otp.status= USED)
          • update temporary_access_token (set `tokens.details.used`=true)
          • create & return new access_token (as a existing standart process without 2FA) & return 201
        • If checking_otp<>`otp.value` - processing:
          • `2FA.otp_error_counter` + 1
          • If `2FA.otp_error_counter` >= `configs.OTP_ERROR_MAX`
            • Set `2FA.Status=BLOCKED`
            • return error 401
  • ...

Response

  • 201 if OTP creating & sending successful
  • 4xx in other case


PROC: [AUTO] Terminate OTP (new PROC)

Purpose

AUTO-Terminating expired  OTP.

Logic

  • Fetch records from `top`, where:

    • status = 'ACTIVE'
    • expired_at < NOW() 
  • Update (Set values) OTP items 
    1. status  =  EXPIRED
    2. updated_at = now()


WS: ResetUser2FA (new EP)

Purpose

Reset 2FA for user (for Admin).

Request parameters

  • user_id

Logic WS

  • Validate token & scope
  • Validate user
  • Get 2FA.Status from user
  • Validate 2FA_Status 
    • If 2FA_Status = INIT return error 409 
    • if any case :
      • Update 2FA (set values)
        • 2FA.Status = RESET
        • 2FA.value = 'NULL`
        • 2FA.otp_error_counter=0
      • Return 200
  • ...

Response

  • 200 if 2FA successful reset
  • 4xx in other case


WS: DisableUser2FA (new EP)

Purpose

Disable 2FA for user (for Admin).

Request parameters

  • user_id

Logic WS

  • Validate token & scope
  • Validate user
  • Get 2FA.Status from user
  • Update 2FA (set values)
    • 2FA.Status = DISABLE
    • 2FA.value = 'NULL`
    • 2FA.otp_error_counter=0
  • Return 200
  • ...

Response

  • 200 if 2FA successful reset
  • 4xx in other case


Modification WS: Approvals /Authorize an Approval ( existing EP)

Purpose

Add handling logic for type of tokens

Logic WS

  • If token_type = access_token & user.2FA.status = DISABLE - go to standard process create Approvals (existing logic)
  • If token_type = access_token & user.2FA.status <> DISABLE - return 401 error
  • If token_type = temporary_access_token - return 401 error



PROC: GenerateNewOTP (new EP)

Purpose

Generate new value for OTP

Logic

Generate rendom digital value with length =  OTP_LENGTH

Modification WS: Approvals /Authorize an Approval ( existing EP)

Purpose

Add handling logic for type of tokens

Logic WS

  • If token_type = access_token & user.2FA.status = DISABLE - go to standard process create Approvals (existing logic)
  • If token_type = access_token & user.2FA.status <> DISABLE - return 401 error
  • If token_type = temporary_access_token - return 401 error

Modification WS: Create a Token ( existing EP)

Purpose

Add handling logic for return different type of tokens by 2FA_Statuses

Logic WS

  • Get  2FA_status by user
  • If 2FA_status = DISABLE - go to standart process without 2FA (return access_token)
  • In case 2FA_status = (INIT, RESET, UNVERIFIED) - go to verification 2FA process (TBD!!!)
  • If 2FA_status = BLOCKED -  return 401 error (TBD!!!)
  • If 2FA_status = VERIFIED - return temporary_access_token.



ЕСОЗ - публічна документація