Table of Contents |
---|
...
Column | Type | Purpose |
---|---|---|
id | uuid | |
key | varchar | Value of factor (1 active 2FA for user) |
code | varchar | Value of OTP |
status | varchar | Status (Dictionary: OTP_STATUS) |
code_expired_at | timestamp | Timestamp which OTP expired (now() + param from config) |
attempts_count | integer | Count of trying OTP authorization process |
updateed_at | timestamp |
OTP states
Dictionary: `OTP_STATUS`
Value Purpose NEW Initial status for newest OTP item. Ready to use in OTP verification process. VERIFIED Status for successful OTP verification process UNVERIFIED Status for unsuccessful OTP verification process EXPIRED Expired after OTP lifetime. CANCELED Status for manual admin action.
- Status Chart for OTP
- TransitionsFromTransitionResult
Created new OTP status = NEW status = NEW Succseful OTP verify status = VERIFIED status = NEW Unsuccseful OTP verify afterexcess of count [param: OTP_ERROR_MAX] status = UNVERIFIED status = NEW [AUTO] Termination process after end of life-time OTP [param: OTP_LIFETIME] status = EXPIRED status = NEW All OTP by `key` in status=`NEW` before creating new OTP status = CANCELED
Services specifications
1- WS.Create User (Modification)
Status | ||||
---|---|---|---|---|
|
Purpose
- Add `is_blocked`, `block_reason` column in user entity
- Add logic for create 2FA item for new user
Request parameters
Add new parameters:
- 2fa_enable (boolean, optional)
Logic WS
Create user with this structure in `users.priv_settings`:
Code Block { "login_error_counter": 0, "otp_error_counter": 0 }
and:
- is_blocked = FALSE
- block_reason = NULL
- .....
- After insert new user in `users`
- Analyze $.2fa_enable
- If $.2fa_enable = FALSE - break
- If $.2fa_enable = TRUE
- Insert new record in `authentication_factors` with logical status = RESET
- user_id = $.user_id
- type = `SMS`
- factor = NULL
- is_active = TRUE
- update_at = now()
- Insert new record in `authentication_factors` with logical status = RESET
- If $.2fa_enable = NULL or empty
- Get config-param `USER_2FA_ENABLED`
- If `USER_2FA_ENABLED` = TRUE
- Insert new record in `authentication_factors` with logical status = RESET
- user_id = $.user_id
- type = `SMS`
- factor = NULL
- is_active = TRUE
- update_at = now()
- Insert new record in `authentication_factors` with logical status = RESET
2- WS: UpdateUser2FA (new EP)
Status | ||||
---|---|---|---|---|
|
APIary
PUT mithril/api/users/{user_id}/2fa/{2fa_id}
Scope: `user2fa:disable2fawrite`
Purpose
Disable/Enable 2FA for user (via Admin-console).
Request parameters
- user_id
- 2fa_id
- is_active
Logic WS
- Validate token & scope
- Validate user_id FK
- Validate 2fa_id FK
Get 2FA item by 2fa_id for non-blocked user by $.user_id
Code Block language sql SELECT * FROM authentication_factors AS 2FA INNER JOIN user AS U ON 2FA.user_id = U.id WHERE U.id = $.user_id AND 2FA.id = $.2fa_id AND U.is_active = TRUE AND U.is_blocked = FALSE
- If exist 2FA item for user → update 2FA item (set values):
- 2FA.is_active = FALSE
- 2FA.update_at = now()
- If exist 2FA item for user → update 2FA item (set values):
- Return 200
- ...
Response
- 200 if 2FA successful disable + 2FA_object_view
- 4xx in other case
...
PATCH mithril/api/users/{user_id}/2fa{2fa_id}/actions/reset2fa
Scope: `user2fa:reset2fareset`
Purpose
Reset factor value by 2fa_id for user (via Admin-console).
...
- Validate token & scope
- 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()
- Blocked user - update user (set values) by $.user_id
- If login exist
- Validate user status
Get active 2FA item for non-blocked user by $.user_id
Code Block language sql 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 with 2FA (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)
- If exist 2FA active item with empty factor
...
- 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
- 200 if OTP successful create & send
- 4xx in other case
...
- Validate token (2fa_access_token) - ????
- If invalid - return error 4xx
- Validate user id & user status
Get active 2FA item for non-blocked user by $.user_id
Code Block language sql 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 = 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 200
- Update user (set values) by $.user_id
- 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()
- Blocked user - update user (set values) by $.user_id
- return 401 error
- Update user (set values) by $.user_id
...
- 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_count > OTP_ERROR_MAX )
- Update OTP item
- status ( NEW → UNVERIFIED)
- updated_at = now()
- Update OTP item
- If OTP.code = $.code - update OTP item:
Response
- 200 if OTP successful create & send
- 4xx in other case
10- WS:
...
CreateUser2FA (new EP)
Status | ||||||
---|---|---|---|---|---|---|
|
APIary
POST mithril/api/users/{user_id}/2fa
Scope: `2fa:write`
Purpose
Create factor item for user (after RESET of 2fa)
Request parameters
- token
- user_id
- 2fa_id
- type
- factor
Logic WS
- Validate token & scope
- Validate user_id FK
- Validate user status
- Insert new record in `authentication_factors`
- user_id = $.user_id
- type = $.type
- factor = $.factor
- is_active = TRUE
- update_at = now()
- Return 201
Response
- 201 if 2FA successful reset + 2FA_object_view
- 4xx in other case
xx- WS: Set factor user 2FA (New EP)
Status | ||||||
---|---|---|---|---|---|---|
|
APIary
PATCH mithril/api/users/{user_id}/2fa{2fa_id}/actions/set_factor
Scope: `2fa:set_factor`
Purpose
Update value of factor (after RESET of 2fa)
Request parameters
- user_id
- 2fa_id
- new_factor
- token
...
- Validate token & scope
- Validate user_id FK
- Validate 2fa_id FK
Get 2FA item by 2fa_id for non-blocked user by $.user_id
Code Block language sql SELECT * FROM authentication_factors AS 2FA INNER JOIN user AS U ON 2FA.user_id = U.id WHERE U.id = $.user_id AND 2FA.id = $.2fa_id AND U.is_active = TRUE AND U.is_blocked = FALSE
- If (exist 2FA item for user) AND (token_type = access_token_type) AND (2FA.factor <> "" AND 2FA.factor <> NULL) - update 2FA item (set values) to logical status = ACTIVE :
- 2FA.factor = new_factor
- 2FA.update_at = now()
- If (exist 2FA item for user) AND (token_type = 2fa_access_token_type) AND (2FA.factor = "" OR 2FA.factor = NULL) - update 2FA item (set values) to logical status = ACTIVE :
- 2FA.factor = new_factor
- 2FA.update_at = now()
- Else return 401 error
- If (exist 2FA item for user) AND (token_type = access_token_type) AND (2FA.factor <> "" AND 2FA.factor <> NULL) - update 2FA item (set values) to logical status = ACTIVE :
- Return 200
- ...
Response
- 200 if 2FA successful reset + 2FA_object_view
- 4xx in other case
...
11- PROC: [AUTO] Terminate OTP (new PROC)
Status | ||||||
---|---|---|---|---|---|---|
|
Purpose
AUTO-Terminating expired OTP.
...
12- WS: Get user 2FA List (new EP)
Status | ||||
---|---|---|---|---|
|
APIary
GET mithril/api/users/{id}/2fa/
Scope: `2fa:read`
Purpose
Get 2FA items list for user (via Admin-console).
Request parameters
- user_id
- type
Logic WS
- Validate token & scope
- Validate user id & user status
Get data by filters
Code Block language sql SELECT * FROM authentication_factors AS 2FA WHERE (2FA.user_id = $.user_id OR $.user_id IS NULL) AND (2FA.type = $.type OR $.type IS NULL)
- Return 200
- ...
Response
- 200 if get list successful + 2FA_list_view
- 4xx in other case
...
13- WS: Get user 2FA by ID (new EP)
Status | ||||
---|---|---|---|---|
|
APIary
GET mithril/api/users/{user_id}/2fa/{2fa_id}
Scope: `2fa:read`
Purpose
Get 2FA item by id (via Admin-console).
Request parameters
- 2fa_id
Logic WS
- Validate token & scope
- Validate 2fa id
- Validate user_id FK
Get data by id
Code Block language sql SELECT * FROM authentication_factors AS 2FA WHERE 2FA.id = $.id AND 2FA.user_id = $.user_id
- Return 200
- ...
Response
- 200 if get list successful + 2FA_object_view
- 4xx in other case
...