This is a facility to handle user authentivcaton that extends from bfx-facs-base to provide authentication management with support for generating and validating tokens, managing users, and handling permissions. It uses SQLite for storing user and token information and LRU for caching.
This facility requires a config file in the following structure:
{
"a0": {
"superAdmin": "superadmin@localhost", // Superadmin email address
"ttl": 5000, // Default token time-to-live in seconds
"saltRounds": 10, // Number of salt rounds for password hashing
"trustProxy": false, // See "Client IP resolution" below
"roles": { // Roles with associated permissions
"admin": [
"miner:rw",
"container:rw",
"user:rw"
],
"site_manager": [
"miner:rw",
"container:rw",
"user:r"
],
"user": ["jobs:rw"]
}
}
}Tokens are bound to the IPs observed at issue time. By default the facility
trusts only the direct socket peer (req.socket.remoteAddress) and ignores
the spoofable X-Forwarded-For header — so callers behind a proxy will see
their token bound to the proxy's address.
If your service runs behind a trusted reverse proxy, set trustProxy: true
and configure your HTTP framework to validate the proxy chain itself
(e.g. Express's app.set('trust proxy', …)). When enabled, the facility:
- Checks for Cloudflare's
CF-Connecting-IPheader (if present and valid) - Trusts the framework-derived
req.ip/req.ips(fromX-Forwarded-For) - Always includes
req.socket.remoteAddressas a fallback
The raw X-Forwarded-For header is never consumed directly for security reasons.
createUser and updateUser only accept role names declared as keys in
conf.roles. The super-admin marker (*) cannot be assigned via these
APIs — it is reserved for the bootstrap super-admin written by _initDb.
updateUser additionally requires the caller's token to hold user:rw
whenever the requested roles differ from the user's current roles.
Creates a new user with specified roles and permissions.
Parameters:
req<object>: Object with user creation details.email<string>: Email address of the user.roles<string[]>: Array of roles for the user.password<string>: Password for the user.
const result = await auth.createUser({
email: 'user@example.com',
roles: ['admin']
})Updates an existing user with new roles and permissions.
Parameters:
req<object>: Object with user update details.token<string>: Authentication token for the user.email<string>: Email address of the user.roles<string[]>: Array of roles for the user.password<string>: New password for the user.
const result = await auth.updateUser({
token: 'some-token',
email: 'new@example.com',
roles: ['admin']
})Compares user details (email, password, roles) with the stored user information.
Parameters:
req<object>: Object containing user details for comparison.token<string>: Authentication token for the user.email<string> (optional): Email address to compare.roles<string[]> (optional): Array of roles to compare.password<string> (optional): Password to compare.
const isMatching = await auth.compareUser({
token: 'user-token',
email: 'test@example.com',
roles: ['user'],
password: 'securepassword'
})
console.log(isMatching) // true or falseGenerates a new authentication token based on the provided parameters. It validates the input, allocates resources, and stores the token data.
Parameters:
req<object>: Object containing token generation details.ips<string[]>: List of IP addresses associated with the token.userId<number>: User ID for whom the token is generated.ttl<number>: Time-to-live for the token in seconds (default: 300).metadata<object>: Optional metadata associated with the token.pfx<string>: Prefix for the token (default: 'pub').scope<string>: Scope for the token (default: 'api').roles<string[]>: Array of roles for the token.
const token = await auth.genToken({
ips: ['192.168.1.1'],
userId: 1,
ttl: 3600,
metadata: { key: 'value' },
pfx: 'pub',
scope: 'api',
roles: ['admin']
})Regenerates an existing authentication token. It validates the old token, checks permissions, and creates a new token.
Parameters:
req<object>: Object with token regeneration details.oldToken<string>: Existing token to be regenerated.ips<string[]>: New IP addresses associated with the token (optional).ttl<number>: Time-to-live for the new token in seconds (default: 300).pfx<string>: Prefix for the new token (default: 'pub').scope<string>: Scope for the new token (default: 'api').roles<string[]>: Array of roles for the new token.
const newToken = await auth.regenerateToken({
oldToken: 'existing-token',
ips: ['192.168.1.1'],
ttl: 3600,
pfx: 'pub',
scope: 'api',
roles: ['admin']
})Retrieves permissions associated with a token.
Parameters:
token<string>: The token to get permissions for.
Returns:
{ superadmin: <boolean>, perms: <string[]> }with token permissions.
const perms = auth.getTokenPerms('some-token')
console.log('Token permissions:', perms)Validates a token and checks if it is associated with the given IP addresses.
Parameters:
token<string>: The token to resolve.ips<string[]>: List of IP addresses to validate.
const user = await auth.resolveToken('some-token', ['192.168.1.1'])Note: User data (user) is coming from the auth_token table.
Checks if a token has the required permissions.
Parameters:
token<string>: The token to check.perm<string>: Permission to check.
Returns:
trueif the token has the required permissions.falseotherwise.
const hasPerms = auth.tokenHasPerms('some-token', 'miner:r')
console.log('Token has required permissions:', hasPerms)Cleans up expired tokens from the database.
await auth.cleanupTokens()Adds authentication handlers to the service.
Parameters:
handlers<object>: Object containing authentication handlers, each key is a handler name and value is a handler function.
auth.addHandlers({
'handler-name': async (ctx, req) => {
// Handler logic
}
})Handles authentication callbacks by resolving tokens and returning authentication results.
Parameters:
type<string>: Type of authentication callback.req<object>: Request object containing callback details.
const token = await auth.authCallbackHandler('callback-type', request)Return the user with the given id
Parameters:
id<string>: id of the user.
const user = await auth.getUserById('3')Return the user with the given email
Parameters:
id<string>: email of the user.
const user = await auth.getUserByEmail('new@example.com')Returns a list of users present
const users = await auth.listUsers('callback-type', request)Deletes the user with the provided id.
Parameters:
id<string>: id of the user.
await auth.deleteUser('23')Handles multi-factor authentication (MFA) by invoking the specified MFA handler.
Parameters:
type<string>: The type/name of the MFA handler to invoke (e.g.,'totp').req<object>: The request object containing necessary authentication details.
Throws:
ERR_HANDLER_INVALIDif the specified handler does not exist or is not a function.
const result = await auth.mfaHandler('totp', { totp: '123456' , ...<Object> })Handles authentication callbacks with MFA support. If MFA is required, returns a CSRF token and the list of required MFA methods; otherwise, returns the authentication token.
Parameters:
type<string>: The type of authentication callback.req<object>: The request object containing callback details.getUserMfaMethods<function>: A function that returns the list of enabled MFA methods for the user.
Returns:
- If MFA is required:
An object containing
csrf_token,mfa_required: true, andmfa_methods(array of enabled MFA methods). - If MFA is not required:
An object containing the authentication
token.
Throws:
ERR_MFA_METHOD_HANDLER_INVALIDif getUserMfaMethods is not a function.