Creating Tokens

How to generate API tokens, configure prefixes, privileges, expiration, and IP restrictions.

Before creating tokens, you need to decide on the privilege type associated with that token, prefix, name, how long it will be valid, and if you want to restrict the usage of it for a specific host. A mismatch in any of these values will cause the token to fail verification, and to perform sensitive actions associated with it.

The IAM scopes a privilege for a token and fails verification if the privilege doesn't match the one associated with a token.

That means your app(s) need to know what each privilege means to it, and how it affects its functionality. The IAM role is to create, store, verify it securely, and send to your app/client the metadata about that token. Above that it doesn't have any understanding of what privilege means to your client.

Do not allow un trusted client to control the privilege value

Creating a token

Two ways to create a token:

  • If you use the service as a library you use createApiToken
  • If you run the service directly you call POST /api/manage/new-token with a full authenticated session and the associated body.

Using the library

example.ts
import { createApiToken } from '@riavzon/auth'

const expires = 1000 * 60 * 60; //ms
const ips = ["1.1.1.1", "2.2.2.2"]
const newCreatedToken = await createApiKey(userId, 'demo', 'mytoken', 'app', expires, ips);

The above code will generate a token with a privilege of demo a name of mytoken with a prefix of app, that will be restricted to "1.1.1.1", "2.2.2.2" ip addresses and will expire in 1 hour.

If succeeded the above will return:

{
   ok: true,
   date: new Date().toISOString(),
   data: {
       rawApiKey,
       rawPublicId,
       expiresAt
   }
}
  • rawApiKey - The raw un hashed api key
  • rawPublicId - The public id
  • expiresAt - When the token will be expired
This is the only time the server and you would see the token, you should return it to the authenticate client immediately. After this point if the client looses it, it would need to generate a new one, or rotate it.
The prefix should not include a "_" character in its value, or the function will throw.

The function will return:

{
 ok: false,
 date: new Date().toISOString(),
 reason: string
}

If the creation is failed, and throw only if the database failed to create a new token.

Possible reasons are:

  • The user have reached the maximum allowed valid tokens he can have at a time (configured under apiTokens.limitTokensPerUser)
  • The character "_" is being used in the prefix
  • Database connection issues
Authentication is not checked inside this function. You should fully authenticate the session before allowing access to it in a route.

Running the service

To create a token with the service, your client needs to be fully authenticated, with a valid refresh token a valid access token and a canary_id cookie. The session also should not have any active MFA or anomaly associated with it.

Send a POST request to POST /api/manage/new-token with the following body:

{
    "privilege": "demo",
    "name": "mytoken",
    "prefix": "app",
    "ipv4": ["1.1.1.1", "2.2.2.2"],
    "expires": 3600000
}

This will generate a token with the same conventions as above, and return the following response:

HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8

{
  "ok": true,
  "date": "current date",
  "data": {
        "rawApiKey": "token",
        "rawPublicId": "pubid",
         "expiresAt": "expiry"
    }
}
Same applies as above, this is the only time the server and you would see the token, you should return it to the authenticate client immediately. After this point if the client looses it, it would need to generate a new one, or rotate it.

Possible failure

If the body is not valid or missing required values 400 will be returned:

HTTP/1.1 400 Bad request
Content-Type: application/json; charset=utf-8

{
  "ok": false,
  "date": "current date",
  "reason": "Bad Request"
}

If the body contains HTML:

HTTP/1.1 403 Bad request
Content-Type: application/json; charset=utf-8

{
 "banned": true
}

For issues related to createApiToken:

HTTP/1.1 400 OR 500
Content-Type: application/json; charset=utf-8

{
  "ok": false,
  "date": "current date",
  "reason": "reason from `createApiToken`"
}

Rate Limits:

HTTP/1.1 429
Content-Type: application/json; charset=utf-8
Retry-After: number
{
  "error": "Too many requests",
  "retry": "number",
}

The route is behind a full authentication flow, each needs to succeeded before the token is created:

  • requireAccessToken Requires the access token header to be present
  • requireRefreshToken Requires the refresh token to be present
  • getFingerPrint Gets the finger prints for MFA
  • checkForActiveMfa MFA cache
  • protectRoute Enforces authentication, and perform the 9 mfa checks,
  • contentType('application/json') Enforces application/json,
  • express.json To limit the body size to 1kb
  • apiTokensController The creations happens here

If the client is not authenticate, cannot be found, have an active MFA the creation will fail, with the appropriate response.

Rate Limits

The endpoint also enforce rate limits controlled under the following configuration options:

  • rate_limiters.apiTokensLimiters.operationRateLimits.newTokenCreationLimiter - The main limiter for the new tokens creation endpoint. The default allows 5 creations in a window of 10 minutes and will trigger a block for 1 hour if this limit is met. Consecutive triggers in this period will block the client permanently.
  • rate_limiters.apiTokensLimiters.generalUnionLimiter - A burstLimiter and a slowLimiter union limiter. Enforces no more than one request per second, and only 50 per minute. No Consecutive triggers in this limiter, triggering it again will result in a permanent ban.
    • burstLimiter - Will block the client for 15 minutes.
    • slowLimiter - Will block for 1 hour.

Ip restrictions & Expiry

Both ip restrictions and expiry are optional. To make a token live forever omit expiresAt or provide null, to allow a token to be used from any host omit ipv4.

However prefix are required, and api will be used as a default if not provided.

Configuration Reference

All options live under apiTokens in the object passed to configuration().

OptionTypeDefaultDescription
limitTokensPerUsernumber20Allowed valid tokens the user can have at a time

Rate limiters

LimiterDescription
rate_limiters.apiTokensLimiters.operationRateLimits.newTokenCreationLimiterThe main rate limiter for token creations
rate_limiters.apiTokensLimiters.generalUnionLimiterGeneral burst limiter

createApiToken Definition

async function createApiKey(
    userId: number, 
    privilegeType: 'demo' | 'restricted' | 'protected' | 'full' | 'custom',
    name: string,
    prefix: string = 'api',
    expires?: number,
    ipAddresses?: string[]
): Promise<Results<CreationSuccess>>
Logo