Account Recovery Guide
Description
For an end user, who has Online Banking with NetTeller or Episys, they may recover their username and password via this feature.
Rate Limiting
Banno Enrollment rate limits users via its own database, as well as via NetTeller.
| Feature | Core Type | Max by IP | Max by Tax Identification Number(TIN) |
|---|---|---|---|
| Account Recovery | NetTeller | 50 | 5 |
| Account Recovery | SymXchange | 50 | 5 |
Once a user has met the “Max by IP” or “Max by TIN” rate limits, that user is then not allowed to re-try for ~24 hours. Note that either rate limit is independent of one another.
Technical Details
The following serves as a technical overview of Enrollment.


And here is a diagram of the flow (editable) for banks (including the updates to unlock during recovery as necessary):

And for credit unions (editable):

POST /recovery/lookup
NetTeller Bank
The purpose of this endpoint is to determine if the user, whose details are presented in the HTTP Request, is eligible to recover their account.
The following steps happen, in order:
From the request, look up in
banno_alldatabase whether theinstitutionIdis a SymXchange or NetTeller InstitutionLook up the user in core, i.e. SymXchange or BSL
Get a list of Customer IDs (CIFs) via
jxchange-apifor the user’s request-provided TINFor each CIF, look up its details via `jxchange-api. The details include:
- name
- optional email
- list of phone numbers
- set of NetTeller Ids
- optional demographic data
For each candidate, namely the 4-tuple, (CustomerId, NetTellerId, optional email, and list of phone numbers), get the accounts for the given NetTeller Id via a
jxchange-apicallExclude any candidate, namely (CustomerId, NetTellerId, optional email, list of phone numbers, list of accounts) if any account’s number does not match the one supplied in POST /recovery/lookup’s payload
At this point, we have a List[Candidates], i.e. 0 or more with a matching Account Number
For each candidate, query
banno_all.login_accountsto determine if the candidate’s details match an existing Banno User. Only count as a ‘banno user candidate’ if the query results one or more results.SELECT login_id, institution_id, user_id, aggregation_type, customer_id, netteller_id, username, member_number FROM login_accounts WHERE institution_id = $institutionId AND customer_id = $customerId AND netteller_id = $netTellerId AND aggregation_type = 2 -- NetTeller AND active = true AND primary_login = trueGiven a List[Candidate] and List[MatchingBannoLoginAccounts], check whether the user can be recovered. See https://github.com/Banno/consumer-enrollment/blob/1.378.0/core/src/main/scala/com/banno/consumerenrollment/backend/modules/recovery/RecoveryInitiationForBanks.scala#L133-L206
If an eligible match has been found, check the status of the user via
netteller-bsl-service.- If the user is Locked or Dormant, then update the sesssion state, which gets stored in the JWT, to indicate that the user cannot be recovered since he/she is locked.
- If the user is Normal or PasswordNeedsReset, then update the sesssion state to include the phone number, etc.
Check the user’s 2FA Enrollment Status via
oob2faIf the user is enrolled, send a 2FA code and respond w/ HTTP-200; otherwise, respond with an HTTP-202 so that the user gets prompted (by the UI) to enroll in 2FA by supplying Phone Number and Email.
Note that the purpose of the JWT for Enrollment is to achieve authentication, as well as to keep state to maintain, effectively, a session. It is valid for 10 minutes and includes JSON state, e.g. whether the user verified their 2FA Code, etc.
PUT /recovery/oob/verify
NetTeller Bank & Credit Unions
- Make HTTP request to
oob2fa, Espresso’s service that handles 2FA (enrollment, verification, sending codes, etc), with the 2FA code that the user supplied in the HTTP Request toPUT /recovery/oob/verify. - Record a history event, i.e. send an HTTP request to Team Curry’s ‘history’ web service with the result of the 2FA verification attempt
- Send HTTP Response per https://github.com/Banno/consumer-api-docs/blob/master/_recovery/details.swagger.json#L136-L167
POST /recovery/password
NetTeller Bank
The following logic takes place in consumer-enrollment:
- Make HTTP request to Espresso’s
netteller-bsl-servicein order to set a new password for the given user’s NetTellerId- Note that this call will fail if the user is locked or dormant per the contract
- Check if recovering user is already a Banno User
- Note - it’s not necessary to be a Banno User before completing Account Recovery. For example, a NetTeller Online user
could recover their account despite having not being a Banno User.
- If the user is already a Banno User
- update banno_all.login_accounts via a JDBC connection if a particular column is in a bad state
- make HTTP requests to
device-servicevia HTTP in order to register the user’s device
- Else, if the user is not yet a Banno User, then
- insert new rows via JDBC to
banno_allto create a new Banno User - make HTTP requests to
device-servicevia HTTP in order to register the user’s device - persist the user’s encrypted password via an HTTP Request to
consumer-login-secretsover HTTP
- insert new rows via JDBC to
- If the user is already a Banno User
- Note - it’s not necessary to be a Banno User before completing Account Recovery. For example, a NetTeller Online user
could recover their account despite having not being a Banno User.
- Record a history event, i.e. send an HTTP request to Team Curry’s ‘history’ web service, indicating that the user saved his/her password.
- Invalidate the Recovering User’s session, i.e. prevents subsequent HTTP Requests to
POST /recovery/passwordfrom succeeding. - Send HTTP Response to
mobile-data-services’sapiwith the recovered user’s details.
The following logic takes place in mobile-data-services’s api:
- Use shared code in
apito create a fully auth’d JWT - Send HTTP Response w/ fully auth’d JWT