api/users

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 sessions columns 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 = users
    

    Users 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 DbTableColumn type.

Source:
Properties:
Name Type Description
schema object
Properties
Name Type Description
login string

primary key, user email or user name or other unique identifier to be used in the login method

id string

unique auto-generated UUIDv4, this is a unique index

name string

full user name

roles Array.<string>

list of roles for authorization

flags Array.<string>

tags, feature flags...

secret string

hashed user password

topt_secret string

generated secret for TOTP MFA encoded as base32, the present of the sedret will require TOTP code on every login

mfa_code string

used to enforce MFA verification and keep the last code sent, to enable set any non-empty value, when sent it is saved in the format: code,expires,type, a code, expiration in ms, sent method(email,text..)

pushkey string

can be used for push notifications, tokens: [service://]token[@appname]

passkey string

can be used for passkey verifications

sessions Array.<number>

list of sessions, expiration time

create_time number

create time in milliseconds

update_time number

last modified time, auto saved

access_time number

last access time

login_time number

last time user logged in

expire_time number

deny access if this value is before current date, ms

(static) table :string

Description:
Source:

Methods

(async, static) aadd(query, optionsopt) → {Object}

Description:
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:
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:
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:
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:
Source:
Parameters:
Name Type Description
context RequestContext
Properties
Name Type Description
body object

login credentials

Properties
Name Type Attributes Description
login string

username or email

secret string

clear text password

code string <optional>

optional MFA/TOTP code if enabled

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:
Source:
Parameters:
Name Type Description
context RequestContext
Returns:
Type Description
Promise

(async, static) aupdate(query, optionsopt) → {Object}

Description:
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.isUuid with 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 sessions column, 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
Name Type Description
body object

login credentials

Properties
Name Type Attributes Description
login string

username or email

secret string

clear text password

code string <optional>

optional MFA/TOTP code if enabled

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=1 in 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_code property

Source:
Parameters:
Name Type Attributes Description
user object

user record

options object <optional>
Properties
Name Type Attributes Default Description
method string <optional>
email
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.sessions property, 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
  • same user record with updated topt_secret property
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)