In some cases it is necessary to act on behalf of a user. While the Auth service doesn’t allow that it is not difficult to implement such functionality as a serverless function. Below you can find an example of a function that can generate a valid access token for your application with customized values. For details read the docstring in the function itself.

Feel free to adapt to your needs.


npm install jsonwebtoken


Create a file under functions (for instance /functions/custom-jwts.ts), with the following contents:

import { Request, Response } from 'express'
import process from 'process'
import jwt from 'jsonwebtoken'

// function to extract jwt from the request
const getJwt = (req: Request): string | null => {
    const authHeader = req.headers.authorization
    if (!authHeader) {
        return null

    const parts = authHeader.split(' ')
    if (parts.length !== 2) {
        return null

    const [scheme, token] = parts
    if (!/^Bearer$/i.test(scheme)) {
        return null

    return token

// validate jwt token is valid and caller is either an admin or an operator
const jwtIsAuthorized = (req: Request, key: string): string => {
    const token = getJwt(req)
    if (!token) {
        return ""

    try {
        const decoded = jwt.verify(token, key)

        const claims = decoded['']
        if ( !claims || !claims['x-hasura-allowed-roles'] ) {
            return ""

        if (
            claims['x-hasura-allowed-roles'].includes('admin') ||
        ) {
            return decoded.sub

        return ""
    } catch (e) {
        return ""

This is a sample function that generates a JWT token to impersonate users.


- send a valid JWT token with the request. The token should have the `admin` or `operator` role.
- send the `x-hasura-admin-secret` header with the request


A json object with the following keys:

- `userId` (string): the user id for which the token is generated
- `defaultRole` (string): the default role for the userId
- `allowedRoles` (array of strings): the roles that the userId can assume


A json object with the following keys:

- `accessToken` (string) - The generated access token

In addition to the typical JWT claims generated by Nhost, the token generated by this function will have the following claims:

- `x-hasura-on-behalf-of`: the user id of the caller or `admin` if the caller used the `x-hasura-admin-secret` header

You are free to modify this function to suit your needs.
export default (req: Request, res: Response) => {
    let authorizedCaller = ""
    if (req.headers['x-hasura-admin-secret'] === process.env.HASURA_GRAPHQL_ADMIN_SECRET) {
        authorizedCaller = "admin"

    const jwtSecret = JSON.parse(process.env.NHOST_JWT_SECRET)
    if (!authorizedCaller) {
        authorizedCaller = jwtIsAuthorized(req, jwtSecret.key)

    if (!authorizedCaller) {
        return res.status(401).json({ message: 'Unauthorized' })

    // extract from json in the body
    const {userId, defaultRole, allowedRoles} = req.body
    if (!userId || !defaultRole || !allowedRoles) {
        return res.status(400).json({ message: 'Bad request' })

    let token = jwt.sign({
      "exp": Math.floor( / 1000) + 60 * 60, // 1 hour
      "": {
        "x-hasura-allowed-roles": allowedRoles,
        "x-hasura-default-role": defaultRole,
        "x-hasura-user-id": userId,
        "x-hasura-user-is-anonymous": "false",
        "x-hasura-on-behalf-of": authorizedCaller
      "iat": Math.floor( / 1000),
      "iss": "custom-lambda",
      "sub": userId,
    }, jwtSecret.key);

            accessToken: token,

Now you can call it like:

curl -X POST \
    -H "Content-Type: application/json" \
    -H "x-hasura-admin-secret: nhost-admin-secret" \
    -d '{"userId": "FFAB5354-C5EB-42C1-8BC3-AD21D2297883", "defaultRole": "user", "allowedRoles": ["user", "me"]}' \

The new token should be a valid token for your application with the custom values requested:

  "exp": 1717141288,
  "": {
    "x-hasura-allowed-roles": [
    "x-hasura-default-role": "user",
    "x-hasura-user-id": "FFAB5354-C5EB-42C1-8BC3-AD21D2297883",
    "x-hasura-user-is-anonymous": "false",
    "x-hasura-on-behalf-of": "admin"
  "iat": 1717137688,
  "iss": "custom-lambda",
  "sub": "FFAB5354-C5EB-42C1-8BC3-AD21D2297883"