ЕСОЗ - публічна документація
[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
Params | Type | Purpose |
---|---|---|
OTP_ERROR_MAX | integer | 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_LIFETIME | integer | Life-time for OTP item |
OTP_LENGTH | integer | Length 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:
Value Purpose status Status for 2FA type Factor type for 2FA value Factor value for 2FA otp_error_counter Count of errors for trying OTP authorization process Dictionary `2FA_STATUSES`
Value Purpose INIT Initial status for migration. May be deleted in place of `RESET` RESET Reset authentication factor by admin action. VERIFIED User successful verify authentication factor via verify process. UNVERIFIED User don't verify authentication factor via verify process. DISABLE Disable 2FA for this user. BLOCKED Blocked 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:
Column Type Purpose id uuid user_id uuid User value varcher User successful verify authentication factor via verify process. expired_at integer Timestamp which top expired (inserted_at + param from config) status varchar Dictionary: OTP_STATUS inserted_at timestamp updated_at timestamp Dictionary: `OTP_STATUS`
Value Purpose ACTIVE Initial status for newest OTP item. Ready to use in OTP verification process. USED Status for succesful OTP verification process EXPIRED Expired 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
- Create new token with user info (login, password).
- Get 2FA_status by user
- In case 2FA_status = DISABLE - go to standart process without 2FA (return access_token)
- In case 2FA_status = (INIT, RESET, UNVERIFIED) - go to verification 2FA process (return error TBD!!!)
- In case 2FA_status = BLOCKED - go to unblock 2FA process (return error TBD!!!)
- In case 2FA_status = VERIFIED - create & return temporary_access_token (new token type !!!)
- With temporary_access_token we don't create Approvals (return error TBD !!!)
- When return temporary_access_token - automatically invoke WS:SendOTP .
- When call WS:SendOTP (temporary_access_token) which create new OTP item in status = ACTIVE, and send OTP via factor type & value
- FE/user call WS:AuthorizeOTP (temporary_access_token, OTP) which validate OTP in payload with OTPs for this token
- In case unsuccessful validation- return error
- In case successful validation- used temporary_access_token & return new generated access_token.
- 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
- If checking_otp=`otp.value` - processing:
- Processed OTP lists for user:
- ...
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
- status = EXPIRED
- 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
- Update 2FA (set values)
- ...
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.
ЕСОЗ - публічна документація