Table of Contents |
---|
Apiary | |||||||
| mithril/api/tokens | ||||||
Scope | no 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 | ||
---|---|---|
| ||
- grant_type = "password" - email - password - client_id - scope = "app:authorize" |
OR
Code Block | ||
---|---|---|
| ||
- grant_type = "authorize_2fa_access_token" - token = 2fa_access_token - otp |
OR
Code Block | ||
---|---|---|
| ||
- 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
2FA | Return token_type | scopes in token.details |
---|---|---|
Disable | access_token | "scope": "app:authorize" |
Enable | 2fa_access_token | "scope": "" |
Change `2fa_access_token` → `access_token` | access_token | "scope": "app:authorize" |
Anchor | ||||
---|---|---|---|---|
|
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 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 4xx 401 - "User blocked" (!!! TBD)
- 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
- Check ability login (see section on page below)
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.
- 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
- If exist 2FA active item with empty factor.
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 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 = 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
- 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 = "Passed invalid OTP verify attempts more then than 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
Internal logic for `verify_OTP()`
- Find 1 active OTP (status = NEW) for $.key
- If not found - return 409 401 error "Not found active Invalid 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 > > USER_OTP_ERROR_MAX MAX )
- Update OTP item
- status ( NEW → UNVERIFIED)
- updated_at = now()
- Update OTP item
- If OTP.code = $.code - update OTP item:
Response
- 201 if OTP successful verifiyng, create & send
- 4xx in other case
Anchor | ||||
---|---|---|---|---|
|
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 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
- Termionate 2fa_access_token
- Create & return new 2fa_access_token
- Invoke 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
- for 2FA.type = SMS - via SMS gateway API
- 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