Skip to content

Auth Custom

Custom authorization

To get the value of request header x-custom-authorization, use g.req.auth.authCustom.

const custom_auth = g.req.auth.authCustom;

Whatever your Token Validator function returns is what lands in g.req.auth.authCustom. Return the decoded JWT payload and you get the decoded token there — return your own object and you get that instead.

Why a custom provider?

AM, database, AWS, Azure and Google cover most cases. But sometimes the token is yours — your own claims, your own signing key, your own expiry rules. That is what a Custom Token Generator is for: you write the code that mints the token and the code that checks it, and API Maker handles the rest of the request pipeline.

Below is a complete working example using jose.


1. Install the jose package

Go to Utility → Sandbox Settings → Sandbox Dependencies and add jose.

Once added, it is available in every custom code of API Maker, including the two functions below.


2. Create the auth provider

Go to API Security → Auth Providers, click + and pick Custom Token Generator.

You will get three tabs: Basic Info, Token Generator and Token Validator.

Basic Info

1
2
3
4
5
6
7
import * as T from 'types';

let customTokenBasicInfo: Partial<T.IAuthTokenAMDB> = {
    name: 'jose_example',
    runOnNativeProcess: false,
};
module.exports = customTokenBasicInfo;
  • name is how you will refer to this provider everywhere else — in the get-token request and in the authProviders array of your settings files.
  • Keep runOnNativeProcess as false so the code runs inside the sandbox, where your npm packages live.

Token Generator

Runs when someone asks for a token. Whatever you return is sent back to the caller as-is.

import * as T from 'types';
import * as db from 'db-interfaces';
import { SignJWT } from "jose";

// Produce and return a token string from the request context.
// Runs at token-generation time.
async function main(g: T.IAMGlobal) {
    const body: { role: string, userId: string } = g.req.body;
    const secretFETransfer = await g.sys.system.getSecret('common.secretFETransfer');
    const secret = new TextEncoder().encode(secretFETransfer);

    const accessToken = await new SignJWT({
        // set your required data
        role: body.role,
        email: "[email protected]",
        companyId: "cmp_123",
    }).setProtectedHeader({
        alg: "HS256",
        typ: "JWT",
    }).setSubject(body.userId)
        .setIssuedAt()
        .setExpirationTime("15m")
        .setJti(crypto.randomUUID())
        .sign(secret);

    return {
        access_token: accessToken,
        refresh_token: 'refresh_token_test',
        validity: 19200
    };
};
module.exports = main;

The whole request body reaches this function as g.req.body, so you decide what your login payload looks like.

Token Validator

Runs on every request that carries the x-custom-authorization header. The token is in g.req.body.token.

import * as T from 'types';
import * as db from 'db-interfaces';
import { jwtVerify } from "jose";

// Validate an incoming token. Return a truthy object (usually the decoded payload) to mark valid.
// Throw or return null/undefined to mark invalid.
async function main(g: T.IAMGlobal) {
    const accessToken = g.req.body.token;
    const secretFETransfer = await g.sys.system.getSecret('common.secretFETransfer');
    const secret = new TextEncoder().encode(secretFETransfer);

    const { payload } = await jwtVerify(accessToken, secret, {
        algorithms: ["HS256"],
    });

    return payload;

    // return true; // if truthy value returned, means token is valid.
    // return false; // if token is not valid
    // return {}; // You can return some object also and it means token is valid.
};
module.exports = main;

The rule is simple — truthy means valid. Anything falsy, or a thrown error, means the request is rejected.


3. Generate a token

Request Method: POST

URL

/api/system-api/user-path/token

1
2
3
4
5
{
    "name": "jose_example",
    "userId": "user_123",
    "role": "admin"
}
  • name tells API Maker which auth provider to run. Everything else in the body is yours and is passed straight to your Token Generator as g.req.body.
  • For more information about this API click here.

Response — exactly what your generator returned:

1
2
3
4
5
{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refresh_token": "refresh_token_test",
    "validity": 19200
}

4. Send the token

Put the access_token in the x-custom-authorization header on every call:

x-custom-authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

To let an API accept this provider, add its name to authProviders in your settings file:

authProviders: <string[]>["jose_example"],

5. Read it in your custom code

import * as T from 'types';

async function main(g: T.IAMGlobal) {
    const auth = g.req.auth.authCustom;

    g.logger.log(auth.sub);        // user_123
    g.logger.log(auth.role);       // admin
    g.logger.log(auth.companyId);  // cmp_123

    return { ok: true };
};
module.exports = main;

By the time your code runs, the token is already verified — API Maker rejects bad ones before they reach you. So g.req.auth.authCustom is safe to trust.