# Custom Auth Provider Example

> Build your own token provider in API Maker with jose — sign it, validate it, and read the payload from g.req.auth.authCustom.

Source: https://docs.apimaker.dev/v4/examples/req/auth/authCustom.html

> **The code in every language**
>
> Each example is the inside of `main`, written in every [language](https://docs.apimaker.dev/v4/docs/languages/index.html) API Maker runs : pick yours in the tabs, and every page shows it from then on. The types a call takes (`GetAll`, `DeepItem`…) are those of `am` : `from am import GetAll` in Python, `use am::GetAll;` in Rust, `import am.GetAll;` in Java and Kotlin ; PHP and C# have them without an import, Go names them `am.GetAll`.

### Custom authorization

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

**TypeScript**

```typescript
const customAuth = g.req.auth.authCustom; // what the token validator returned
```

**Python**

```python
custom_auth = g.req.auth.authCustom  # what the token validator returned
```

**PHP**

```php
$customAuth = $g->req->auth->authCustom; // what the token validator returned
```

**C#**

```csharp
var customAuth = g.Req.Auth["authCustom"]; // what the token validator returned
```

**Go**

```go
customAuth := g.Req.Auth().Get("authCustom") // what the token validator returned
```

**Rust**

```rust
let custom_auth = &g.req.auth()["authCustom"]; // what the token validator returned
```

**Java**

```java
Object customAuth = g.req.getAuth().get("authCustom"); // what the token validator returned
```

**Kotlin**

```kotlin
val customAuth = g.req.auth["authCustom"] // what the token validator returned
```

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: a JWT signed with HS256, with the usual JWT package of each language.

---

### 1. Add a JWT package, and a signing key

Go to **Operations → Sandbox Settings**, select the language of the provider and add its package (see [Packages](https://docs.apimaker.dev/v4/docs/settings/sandboxSettings.html#packages)):

| Language | Registry | Package |
|---|---|---|
| TypeScript | npm | `jose` |
| Python | PyPI | `pyjwt` |
| PHP | Packagist | `firebase/php-jwt` |
| C# | NuGet | `Microsoft.IdentityModel.JsonWebTokens` |
| Go | Go modules | `github.com/golang-jwt/jwt/v5` |
| Rust | crates.io | `jsonwebtoken[rust_crypto]` and `uuid[v4]` |
| Java, Kotlin | Maven Central | `com.auth0:java-jwt` |

Once added, it is available in every piece of code of that language, including the two functions below.

Then add a key of your own to the [secret](https://docs.apimaker.dev/v4/docs/secrets/secrets.html#your-own-keys), `jwtSigningKey`, with at least 32 random characters: the packages of PHP and C# refuse a shorter key for HS256. Keep it for the tokens alone. `common.secretFETransfer` is no signing key: your apps hold it, so anyone with a copy of an app could sign tokens with it.

---

### 2. Create the auth provider

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

You will get three tabs: **Basic Info**, **Token Generator** and **Token Validator**. The Basic Info is always TypeScript, which API Maker [runs itself](https://docs.apimaker.dev/v4/docs/languages/index.html#settings-run) when you save ; the generator and the validator are written in the [language](https://docs.apimaker.dev/v4/docs/languages/index.html#basic-info) you pick for the provider.

#### Basic Info

```ts
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 the packages of its language live.

#### Token Generator

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

**TypeScript**

**Token Generator**

```typescript
import * as T from 'types';
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 signingKey = await g.sys.system.getSecret('jwtSigningKey');
    const secret = new TextEncoder().encode(signingKey);

    const accessToken = await new SignJWT({
        // set your required data
        role: body.role,
        email: "ada@example.com",
        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;
```

**Python**

**Token Generator**

```python
import time
import uuid

import jwt  # the package pyjwt

from am import G


# Produce and return a token string from the request context.
# Runs at token-generation time.
async def main(g: G):
    body = g.req.body
    signing_key = await g.sys.system.get_secret('jwtSigningKey')

    now = int(time.time())
    access_token = jwt.encode(
        {
            # set your required data
            'role': body.role,
            'email': 'ada@example.com',
            'companyId': 'cmp_123',
            'sub': body.userId,
            'iat': now,
            'exp': now + 15 * 60,
            'jti': str(uuid.uuid4()),
        },
        signing_key,
        algorithm='HS256',
    )

    return {
        'access_token': access_token,
        'refresh_token': 'refresh_token_test',
        'validity': 19200,
    }
```

**PHP**

**Token Generator**

```php
<?php
use Firebase\JWT\JWT;

// Produce and return a token string from the request context.
// Runs at token-generation time.
function main(AmG $g)
{
    $body = $g->req->body;
    $signingKey = $g->sys->system->getSecret('jwtSigningKey');

    $now = time();
    $accessToken = JWT::encode([
        // set your required data
        'role' => $body->role,
        'email' => 'ada@example.com',
        'companyId' => 'cmp_123',
        'sub' => $body->userId,
        'iat' => $now,
        'exp' => $now + 15 * 60,
        'jti' => bin2hex(random_bytes(16)),
    ], $signingKey, 'HS256');

    return [
        'access_token' => $accessToken,
        'refresh_token' => 'refresh_token_test',
        'validity' => 19200,
    ];
}
```

**C#**

**Token Generator**

```csharp
using System.Text;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

file static class Api
{
    // Produce and return a token string from the request context.
    // Runs at token-generation time.
    public static async Task<object?> Main(G g)
    {
        var body = g.Req.Body;
        var signingKey = (await g.Sys.System.GetSecret("jwtSigningKey"))?.GetValue<string>() ?? "";

        var accessToken = new JsonWebTokenHandler().CreateToken(new SecurityTokenDescriptor
        {
            // set your required data
            Claims = new Dictionary<string, object>
            {
                ["role"] = body.GetString("role") ?? "",
                ["email"] = "ada@example.com",
                ["companyId"] = "cmp_123",
                ["sub"] = body.GetString("userId") ?? "",
                ["jti"] = Guid.NewGuid().ToString(),
            },
            IssuedAt = DateTime.UtcNow,
            Expires = DateTime.UtcNow.AddMinutes(15),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey)), SecurityAlgorithms.HmacSha256),
        });

        return new
        {
            access_token = accessToken,
            refresh_token = "refresh_token_test",
            validity = 19200,
        };
    }
}
```

**Go**

**Token Generator**

```go
package code

import (
	"crypto/rand"
	"encoding/hex"
	"time"

	"apimaker/am"
	"github.com/golang-jwt/jwt/v5"
)

// Produce and return a token string from the request context.
// Runs at token-generation time.
func Main(g *am.G) (any, error) {
	body := g.Req.Body()
	secret, err := g.Sys.System.GetSecret("jwtSigningKey")
	if err != nil {
		return nil, err
	}
	signingKey, _ := secret.(string)

	jti := make([]byte, 16)
	if _, err := rand.Read(jti); err != nil {
		return nil, err
	}
	now := time.Now()
	accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		// set your required data
		"role":      body.String("role"),
		"email":     "ada@example.com",
		"companyId": "cmp_123",
		"sub":       body.String("userId"),
		"iat":       now.Unix(),
		"exp":       now.Add(15 * time.Minute).Unix(),
		"jti":       hex.EncodeToString(jti),
	}).SignedString([]byte(signingKey))
	if err != nil {
		return nil, err
	}

	return am.M{
		"access_token":  accessToken,
		"refresh_token": "refresh_token_test",
		"validity":      19200,
	}, nil
}
```

**Rust**

**Token Generator**

```rust
use std::time::{SystemTime, UNIX_EPOCH};

use am::{Errors, G, Value, json};
use jsonwebtoken::{Algorithm, EncodingKey, Header, encode};
use uuid::Uuid;

// Produce and return a token string from the request context.
// Runs at token-generation time.
pub fn main(g: &mut G) -> Result<Value, Errors> {
    let body = g.req.body().clone();
    let signing_key = g.sys.system.get_secret("jwtSigningKey", None)?;

    let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
    let claims = json!({
        // set your required data
        "role": body["role"],
        "email": "ada@example.com",
        "companyId": "cmp_123",
        "sub": body["userId"],
        "iat": now,
        "exp": now + 15 * 60,
        "jti": Uuid::new_v4().to_string(),
    });
    let key = EncodingKey::from_secret(signing_key.as_str().unwrap_or_default().as_bytes());
    let access_token = encode(&Header::new(Algorithm::HS256), &claims, &key)?;

    Ok(json!({
        "access_token": access_token,
        "refresh_token": "refresh_token_test",
        "validity": 19200,
    }))
}
```

**Java**

**Token Generator**

```java
import am.G;
import am.JsonObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.time.Instant;
import java.util.UUID;

class Api {
    // Produce and return a token string from the request context.
    // Runs at token-generation time.
    static Object main(G g) {
        JsonObject body = g.req.getBody();
        String signingKey = (String) g.sys.system.getSecret("jwtSigningKey");

        Instant now = Instant.now();
        String accessToken = JWT.create()
            // set your required data
            .withClaim("role", body.getString("role"))
            .withClaim("email", "ada@example.com")
            .withClaim("companyId", "cmp_123")
            .withSubject(body.getString("userId"))
            .withIssuedAt(now)
            .withExpiresAt(now.plusSeconds(15 * 60))
            .withJWTId(UUID.randomUUID().toString())
            .sign(Algorithm.HMAC256(signingKey));

        return JsonObject.of(
            "access_token", accessToken,
            "refresh_token", "refresh_token_test",
            "validity", 19200);
    }
}
```

**Kotlin**

**Token Generator**

```kotlin
import am.G
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import java.time.Instant
import java.util.UUID

// Produce and return a token string from the request context.
// Runs at token-generation time.
fun main(g: G): Any? {
    val body = g.req.body
    val signingKey = g.sys.system.getSecret("jwtSigningKey") as String

    val now = Instant.now()
    val accessToken = JWT.create()
        // set your required data
        .withClaim("role", body.getString("role"))
        .withClaim("email", "ada@example.com")
        .withClaim("companyId", "cmp_123")
        .withSubject(body.getString("userId"))
        .withIssuedAt(now)
        .withExpiresAt(now.plusSeconds(15 * 60))
        .withJWTId(UUID.randomUUID().toString())
        .sign(Algorithm.HMAC256(signingKey))

    return mapOf(
        "access_token" to accessToken,
        "refresh_token" to "refresh_token_test",
        "validity" to 19200,
    )
}
```

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`.

**TypeScript**

**Token Validator**

```typescript
import * as T from 'types';
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 signingKey = await g.sys.system.getSecret('jwtSigningKey');
    const secret = new TextEncoder().encode(signingKey);

    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;
```

**Python**

**Token Validator**

```python
import jwt  # the package pyjwt

from am import G


# Validate an incoming token. Return a truthy object (usually the decoded payload) to mark valid.
# Raise or return None to mark invalid.
async def main(g: G):
    access_token = g.req.body.token
    signing_key = await g.sys.system.get_secret('jwtSigningKey')

    # Raises when the signature or the expiry is wrong.
    payload = jwt.decode(access_token, signing_key, algorithms=['HS256'])

    return payload
```

**PHP**

**Token Validator**

```php
<?php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// Validate an incoming token. Return a truthy object (usually the decoded payload) to mark valid.
// Throw or return null to mark invalid.
function main(AmG $g)
{
    $accessToken = $g->req->body->token;
    $signingKey = $g->sys->system->getSecret('jwtSigningKey');

    // Throws when the signature or the expiry is wrong.
    $payload = JWT::decode($accessToken, new Key($signingKey, 'HS256'));

    return $payload;
}
```

**C#**

**Token Validator**

```csharp
using System.Text;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

file static class Api
{
    // Validate an incoming token. Return a truthy object (usually the decoded payload) to mark valid.
    // Throw or return null to mark invalid.
    public static async Task<object?> Main(G g)
    {
        var accessToken = g.Req.Body.GetString("token");
        var signingKey = (await g.Sys.System.GetSecret("jwtSigningKey"))?.GetValue<string>() ?? "";

        var result = await new JsonWebTokenHandler().ValidateTokenAsync(accessToken, new TokenValidationParameters
        {
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey)),
            ValidAlgorithms = [SecurityAlgorithms.HmacSha256],
            ValidateIssuer = false,
            ValidateAudience = false,
        });

        return result.IsValid ? result.Claims : null;
    }
}
```

**Go**

**Token Validator**

```go
package code

import (
	"apimaker/am"
	"github.com/golang-jwt/jwt/v5"
)

// Validate an incoming token. Return a truthy object (usually the decoded payload) to mark valid.
// Return an error or nil to mark invalid.
func Main(g *am.G) (any, error) {
	accessToken := g.Req.Body().String("token")
	secret, err := g.Sys.System.GetSecret("jwtSigningKey")
	if err != nil {
		return nil, err
	}
	signingKey, _ := secret.(string)

	// An error when the signature or the expiry is wrong.
	payload := jwt.MapClaims{}
	_, err = jwt.ParseWithClaims(accessToken, payload, func(*jwt.Token) (any, error) {
		return []byte(signingKey), nil
	}, jwt.WithValidMethods([]string{"HS256"}))
	if err != nil {
		return nil, err
	}

	return payload, nil
}
```

**Rust**

**Token Validator**

```rust
use am::{Errors, G, Value};
use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode};

// Validate an incoming token. Return a truthy object (usually the decoded payload) to mark valid.
// Return an error or null to mark invalid.
pub fn main(g: &mut G) -> Result<Value, Errors> {
    let access_token = g.req.body()["token"].as_str().unwrap_or_default().to_owned();
    let signing_key = g.sys.system.get_secret("jwtSigningKey", None)?;

    // An error when the signature or the expiry is wrong.
    let key = DecodingKey::from_secret(signing_key.as_str().unwrap_or_default().as_bytes());
    let payload = decode::<Value>(&access_token, &key, &Validation::new(Algorithm::HS256))?;

    Ok(payload.claims)
}
```

**Java**

**Token Validator**

```java
import am.G;
import am.Json;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

class Api {
    // Validate an incoming token. Return a truthy object (usually the decoded payload) to mark valid.
    // Throw or return null to mark invalid.
    static Object main(G g) {
        String accessToken = g.req.getBody().getString("token");
        String signingKey = (String) g.sys.system.getSecret("jwtSigningKey");

        // Throws when the signature or the expiry is wrong.
        var verified = JWT.require(Algorithm.HMAC256(signingKey)).build().verify(accessToken);

        return Json.parse(new String(Base64.getUrlDecoder().decode(verified.getPayload()), StandardCharsets.UTF_8));
    }
}
```

**Kotlin**

**Token Validator**

```kotlin
import am.G
import am.Json
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import java.util.Base64

// Validate an incoming token. Return a truthy object (usually the decoded payload) to mark valid.
// Throw or return null to mark invalid.
fun main(g: G): Any? {
    val accessToken = g.req.body.getString("token")
    val signingKey = g.sys.system.getSecret("jwtSigningKey") as String

    // Throws when the signature or the expiry is wrong.
    val verified = JWT.require(Algorithm.HMAC256(signingKey)).build().verify(accessToken)

    return Json.parse(String(Base64.getUrlDecoder().decode(verified.payload)))
}
```

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
```text
/api/system-api/user-path/token
```

```json
{
    "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](https://docs.apimaker.dev/v4/docs/apis-all/system-apis/system-generated-token-api.html).

Response — exactly what your generator returned:

```json
{
    "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:

```text
x-custom-authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

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

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

---

### 5. Read it in your custom code

**TypeScript**

```typescript
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;
```

**Python**

```python
def main(g):
    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}
```

**PHP**

```php
<?php
function main(AmG $g)
{
    $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];
}
```

**C#**

```csharp
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        var auth = g.Req.Auth.GetObject("authCustom");

        g.Logger.Log(auth.GetString("sub"));        // user_123
        g.Logger.Log(auth.GetString("role"));       // admin
        g.Logger.Log(auth.GetString("companyId"));  // cmp_123

        return new { ok = true };
    }
}
```

**Go**

```go
package code

import "apimaker/am"

func Main(g *am.G) (any, error) {
	auth := g.Req.Auth().Map("authCustom")

	g.Logger.Log(auth.String("sub"))       // user_123
	g.Logger.Log(auth.String("role"))      // admin
	g.Logger.Log(auth.String("companyId")) // cmp_123

	return am.M{"ok": true}, nil
}
```

**Rust**

```rust
use am::{Errors, G, Value, json};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    let 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

    Ok(json!({ "ok": true }))
}
```

**Java**

```java
import am.G;
import am.JsonObject;
import java.util.Map;

class Api {
    static Object main(G g) {
        JsonObject auth = g.req.getAuth().getObject("authCustom");

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

        return Map.of("ok", true);
    }
}
```

**Kotlin**

```kotlin
import am.G

fun main(g: G): Any? {
    val auth = g.req.auth.getObject("authCustom")

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

    return mapOf("ok" to true)
}
```

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.

## Related

- [The global object g](https://docs.apimaker.dev/v4/docs/pre-defined-terms/global-object-g.html) · [Custom APIs](https://docs.apimaker.dev/v4/docs/apis-all/custom-apis/user-created-custom-api.html)
