Skip to content
This page View Markdown Open in ChatGPT Open in Claude

Auth of a database user

Your users are rows of a table of yours. An auth provider of type DB tells API Maker where : the table, the username column, the password column, the groups column. From then on the token API signs those people in, and every call carries their token in x-am-user-authorization.

Two tokens, two gates : the API user (which application) and the person (which rows) 1 · get the tokens POST …/system-api/admin/token { "u": "shop_app", "p": "…" } an API user of API Maker → x-am-authorization POST …/system-api/admin/token { "name": "users_tg", "u": "alice", "p": "…" } a row of your users table → x-am-user-authorization Google, Azure AD, AWS Cognito and custom providers give the person token too : x-google-authorization… 2 · call an API with both GET /api/schema/admin/shop/main/orders x-am-authorization: eyJ… x-am-user-authorization: eyJ… Public APIs (IS_PUBLIC) need no token. Token access needs the API user, and the person only when the settings name an auth provider. 3 · two gates in API Maker API gate · the groups of the API user may this application call this API of this table ? which fields it may read and write · 403 when no group grants it Row gate · your pre hook of the rows this API reaches, which are this person's ? g.req.auth.authAMDB → find = { owner: person.id } only the rows of Alice, only the fields her app may see 401 : token missing or expired · 403 : no group grants the API
Page API Security → Auth Providers : one provider per users table. Types : DB, AWS Cognito, Azure AD, Google, Custom.
Header x-am-user-authorization: <token>
Get it POST /api/system-api/<user path>/token with { "name": "<provider>", "u", "p" }
Require it authProviders: ['<provider>'] in the settings of an API, a table, a database, or in common.authProviders of the secret.
In code g.req.auth.authAMDB : the row of the person, without its password.

1. Declare the provider

Basic Info of the provider
import * as T from 'types';

let dbTokenGenerator: T.IAuthTokenAMDB = {
    name: 'users_tg',
    instance: 'mongodb',
    database: 'shop',
    collection: 'users',                      // table: '…' for SQL
    usernameColumn: 'email',
    passwordColumn: 'password',               // a hashed column works : the password sent is hashed and compared
    passwordChangedAtColumn: 'passwordChangedAt',   // optional : the token carries this value instead of the password fingerprint
    groupsColumn: 'groups',                   // comma separated group names ; '*' = every group of the API user
    expiresInSeconds: 259200,
    runOnNativeProcess: false,                // where the Fields Generator runs
};
module.exports = dbTokenGenerator;
  • select limits the columns of the person put in g.req.auth.authAMDB ; condition adds a filter to the lookup ({ active: true }).
  • passwordChangedAtColumn : update it whenever a password changes, and every token made before stops working, refresh tokens included. A token made while the column of the person is still empty ends with its first value.
  • Without that column the token carries a fingerprint of the stored password (amfp1:…, a keyed hash made with the signing secret of the server), never the password or its hash : a changed password ends the tokens made before, and nothing about the password can be read out of a token.

2. Require it on the APIs

Settings of an API, a table or a database
let settings: T.IInstanceApiSettingsTypes = {
    apiAccessType: T.EAPIAccessType.TOKEN_ACCESS,
    authProviders: ['users_tg'],
};
module.exports = settings;
  • Without authProviders in any settings, the APIs need what common.authProviders of the secret names ; nothing there means the API user token alone.
  • Several DB providers in the list : a token of any of them works. A provider of another type in the list (Google…) adds its own header to what the call must carry.
  • A token works only for the provider which made it, in the account which made it. The token of another provider, of another account or of an API user answers 401 : Invalid token provided in 'x-am-user-authorization' header., even when that table has a person with the same username and password.
  • Your other admin user accounts accept the token too when the root setting Allow API Maker User's Token Across Admin Users is on (Root Settings → Deployment Settings → Security) : the account serving the request needs a provider of the same name, the token must still be valid in the account which made it, and both users tables must keep the same password for that username. The person of the request is the row of the account serving it, with its own groups ; a refresh is asked of the account which made the token.
  • A call without the token answers 401 : Please provide 'x-am-user-authorization' token in request headers. A token whose person is not in the table any more : 401, Token user not found in 'mongodb' -> 'shop' -> 'users'.

3. Sign in

POST /api/system-api/admin/token
{ "name": "users_tg", "u": "[email protected]", "p": "PASSWORD" }
Answer
{ "success": true, "statusCode": 200, "data": { "token": "eyJ…", "refresh_token": "eyJ…", "expires_in": 259200 } }
  • The app sends the token in x-am-user-authorization, next to x-am-authorization of the API user. The sample custom API /default/login of a new account gets both in one call.
  • The person may call an API when a group of theirs grants it and a group of the API user grants it.
  • The token holds the columns of the person the provider reads (a column called name included, as the person has it), the password fingerprint or the password changed at value, and two fields of its own : __amTokenGenerator, the provider which made it, and __amAdminUserId, the account of that provider. A refresh needs the refresh_token of the same provider.

The Fields Generator

  • The Fields Generator tab of a DB provider is a function which returns extra fields to put in the token, from the row of the person (g.req.body) : a display name, a department… They come back in g.req.auth.authAMDB on every call without a lookup.
  • It runs in the sandbox, or on the native process with runOnNativeProcess: true (a Native chip on the list ; use g.logger there, not console.log).

Custom providers

  • A Custom provider is your own logic : a token generator function which answers the token API for { "name": "<provider>", … } with whatever it returns, and a token validator function which receives g.req.body.token and returns a truthy value (or an object) when the token is valid. Calls send that token in x-custom-authorization and your code reads the result in g.req.auth.authCustom. Example.