module:api/users
- Description:
Users table management and DB operations
This is a wrapper around users table to support multiple databases with the same interface, makes it easy to find users by id or login.
Supports TOTP and email MFA code verification.
User cookie sessions are stored in the
sessionscolumns as a list of expiration time in ms. Every login or logout removes expired sessions from the list. To login a session must be present in the list.To enable it just add a table name of your choice to the bkjs.conf
api-users-table = usersUsers can be created via shell commands:
# bksh -user-add login admin name admin secret XXXXX roles admin '136ea1b9fb1778bd2d37096d'Caching
Caching will significantly speed up user verification and will avoid DB hits, using internal caching support is just a configuration:
db-cache-tables = bk_user db-cache-ttl-bk_user = 3600000 db-cache-name-bk_user = redis
- Source:
Members
(static) args :Array.<object>
- Description:
Module specific config parameters
- Source:
(static) maxLength :number
- Description:
Max length for user name, login
- Source:
- Default Value:
- 140
(static) mfaAge :number
- Description:
Age of a MFA code in ms
- Source:
- Default Value:
- 600000
(static) schema
- Description:
Default users table schema, each column is represented by a
DbTableColumntype.
- Source:
Properties:
| Name | Type | Description | |||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
schema |
object |
Properties
|
(static) table :string
- Description:
Table to create and use for users, it is an instance of
DbTableand uses the defaultmodule:api/users.schemaproperty.
- Source:
Methods
(async, static) aadd(query, optionsopt) → {Object}
- Description:
Async version of the
module:api/users.addmethod
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
query |
object | string | ||
options |
object |
<optional> |
Returns:
| Type | Description |
|---|---|
| {err:object, data:object} |
Example
const { err, secret } = await lib.aprepareSecret("secret....");
const { err, data } = await api.users.aadd({ login: "john@mail.com", name: "John", secret });
(async, static) acheckSecret(query) → {Object}
- Description:
Async version of
module:api/users.checkSecret, verify user by login and secret
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
query |
object | an object with { login|id, secret } properties for login |
Returns:
| Type | Description |
|---|---|
| {err:object, data:object} |
Example
const { err, data } = api.users.acheckSecret({ id: "...", secret : "..." });
(static) add(query, optionsopt, callback)
- Description:
Registers a new user, returns new record in the callback,
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
query |
object | user record |
|
options |
object |
<optional> |
|
callback |
function(err:object, user:object) | returning new user record |
Example
lib.prepareSecret("secret....", (err, secret) => {
api.users.add({ login: "john@mail.com", name: "John", secret }, (err) => {
});
});
(async, static) adel(query, optionsopt) → {Object}
- Description:
Async version of the
module:api/users.delmethod
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
query |
object | string | ||
options |
object |
<optional> |
Returns:
| Type | Description |
|---|---|
| {err:object, data:object} |
Example
const { err, data } = await api.users.adel("john@mail.com");
const { err, data } = await api.users.adel({ login: "john@mail.com" });
(async, static) aget(query, optionsopt) → {Object}
- Description:
Async version of the
module:api/users.getmethod
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
query |
object | string | ||
options |
object |
<optional> |
Returns:
| Type | Description |
|---|---|
| {err:object, data:object} |
Example
const { err, data } = await api.users.aget("john@mail.com");
(async, static) alogin(context) → {Object}
- Description:
Async version of the
module:api/users.loginmethod
- Source:
Parameters:
| Name | Type | Description | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
context |
RequestContext |
Properties
|
Returns:
| Type | Description |
|---|---|
| {err:object, user:object} |
Example
api.app.post("/login", async (context) => {
const { err, user } = await api.users.alogin(context);
context.reply(err);
});
(static) alogout(context) → {Promise}
- Description:
Async version of the
module:api/users.logoutmethod
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
context |
RequestContext |
Returns:
| Type | Description |
|---|---|
| Promise |
(async, static) aupdate(query, optionsopt) → {Object}
- Description:
Async version of the
module:api/users.updatemethod
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
query |
object | ||
options |
object |
<optional> |
Returns:
| Type | Description |
|---|---|
| {err:object, data:object} |
Example
const { err, data } = await api.users.aupdate({ login: "john@mail.com", name: "John" });
(static) checkSecret(query, callback)
- Description:
Verify user by login and secret
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
query |
object | an object with { login|id, secret } properties for login |
callback |
function(err:object, user:object) |
Example
api.users.checkSecret({ login: "john", secret : "..." }, (err, user) => {
if (err) return
})
(static) clearup(user) → {object}
- Description:
Cleanup user record to be returned by API endpoint, remove all internal and priv properties
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
user |
object |
Returns:
| Type | Description |
|---|---|
| object |
(static) del(query, optionsopt, callback)
- Description:
Deletes an existing user by login or id, no admin checks, returns the old record in the callback
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
query |
object | string | user id or login or { id, login } |
|
options |
object |
<optional> |
|
callback |
function(err:object, user:object) |
Example
api.users.del("john@mail.com", lib.log);
api.users.del({ login: "john@mail.com" }, lib.log);
(static) get(query, optionsopt, callback)
- Description:
Returns a user record by login or id
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
query |
object | string | user id or login or { id, login } |
|
options |
object |
<optional> |
|
callback |
function(err:object, user:object) | returning found record |
(static) isUuid(id) → {boolean}
- Description:
Returns true of the given id is a valid user uuid, it is
module:lib.isUuidwith optional user's table id column prefix
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
id |
string |
Returns:
| Type | Description |
|---|---|
| boolean |
(static) login(context, callback)
- Description:
Login with just the secret, set the user in the context, creates a cookie session and store .exp in the
sessionscolumn, cleanup expired sessions.Verification code will be enforced if a user has TOPT secret or mfa code saved.
In case MFA is wrong or missing the error object will contain a property
code: "MFA"to signal the UI to deal with it by asking for code or using a separate endpoint to send it.The input is always checked for max size by
module.api.validate, login is case-sensitive by default and validation only checks for required, up to the application to perform different validations and conversions before calling this.The user record is returned in the callback because it was verified already.
NOTE: The login middleware is supposed to perform rate limiting for concurrent logins and against brute force enumerations
- Source:
Parameters:
| Name | Type | Description | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
context |
RequestContext |
Properties
|
||||||||||||||||||||||
callback |
function(err:object, user:object) |
Example
api.app.post("/login", (context) => {
api.users.login(context, async (err, user) => {
if (err?.code === "MFA" && user.mfa_code) {
const code = api.users.prepareMFA(user);
await api.users.aupdate({ login: user.login, mfa_code: user.mfa_code });
await sendmail.asend({ to: user.login, subject: "Verification code", text: `Your verification code ${code}`);
err.message += "\nPlease provide the code that was sent to your email";
} else {
err.message += "\nPlease provide code from your auth app";
}
context.reply(err, api.users.cleanup(user));
})
})
(static) logout(context, callback)
- Description:
Clear session, delete expired sessions Passing
&force=1in the query url will clear all sessions for a user
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
context |
RequestContext | |
callback |
function(err:object) |
(static) prepareMFA(user, optionsopt) → {number}
- Description:
Prepare a MFA code to be sent and saved in
mfa_codeproperty
- Source:
Parameters:
| Name | Type | Attributes | Description | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
user |
object | user record |
|||||||||||
options |
object |
<optional> |
Properties
|
Returns:
| Type | Description |
|---|---|
| number |
code - generated random code |
Example
const { sendmail, api } = required("backendjs");
const user = await api.users.get('user@email');
const code = api.users.prepareMFA(user);
await api.users.aupdate({ login: user.login, mfa_code: user.mfa_code });
sendmail.send({ to: user.login, subject: "Verification code", text: `Your verification code ${code}`, lib.log)
(static) prepareSession(context, user)
- Description:
Creates a cookie session and store .exp in the
user.sessionsproperty, cleanup expired sessions
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
context |
RequestContext | |
user |
object | validated user record |
(static) prepareTOTP(user) → {object}
- Description:
Prepare a new TOTP secret to be saved in the user record
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
user |
object | user record |
Returns:
| Type | Description |
|---|---|
| object |
|
Example
const user = await api.users.get('user@email')
const code = api.users.prepareTOTP(user);
await api.users.aupdate({ login: user.login, totp_secret: user.totp_secret });
(static) update(query, optionsopt, callback)
- Description:
Updates an existing user by login
- Source:
Parameters:
| Name | Type | Attributes | Description |
|---|---|---|---|
query |
object | ||
options |
object |
<optional> |
|
callback |
function(err:object, user:object) | returning updated user record |
Example
api.users.update({ login: "john@mail.com", name: "John" }, lib.log);
(static) verifySession(context, callback)
- Description:
Verify current cookie session and set the user in the context on success, this can be used standalone or as middleware
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
context |
RequestContext | |
callback |
function(err:object, user:object) |
(static) verifyToken(context, callback)
- Description:
Verify current token session and set the user in the context on success, this can be used standalone or as middleware
- Source:
Parameters:
| Name | Type | Description |
|---|---|---|
context |
RequestContext | |
callback |
function(err:object, user:object) |