Verifying Tokens

How to verify API tokens, enforce privilege scopes, apply IP restrictions, and handle rate-limited verification.

API token verification is the public M2M entry point for validating a token against the IAM and checking if it is allowed to perform a given privilege.

If using the service a caller must send the raw API key in the x-api-key header and request a privilege scope through the privilege query parameter. The service then validates the token structure.

The endpoint is not behind any authentication flow, MFA's or bot detections, it verify the given token and gets out of the way.

If using the library, you call verifyApiKey with the appropriate parameters, depending on your needs and use case.

The token will be verified against the attributes you defined in the creation step

Verifying a token

Using the route

The verification route is available at:

GET /api/public/verify?privilege=<privilege> HTTP/1.1

Send the token in the x-api-key header with the privilege you defined at the creation step of the token.

On success the response will be:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "ok": true,
  "date": "current date",
  "data": {
    "name": "mytoken",
    "tokenId": 12,
    "userId": 42,
    "createdAt": "2026-04-30T10:00:00.000Z",
    "expiresAt": "2026-04-30T11:00:00.000Z",
    "lastUsed": "2026-04-30T10:12:00.000Z",
    "usageCount": 7,
    "providedPrivilege": "verified privilege"
  }
}

Failed responses

If the x-api-key header is missing or malformed, the route returns:

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8

{
  "ok": false,
  "date": "current date",
  "reason": "No api key provided"
}

If the server cannot resolve the caller IP, the route returns:

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

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

If the query parameter does not match the allowed privilege schema, the route returns:

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

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

The input is detected as an XSS attempt, the service bans the request:

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

{
  "banned": true
}

The token is malformed, expired, revoked, missing, or has the wrong privilege, the route returns:

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8

{
  "ok": false,
  "date": "current date",
  "reason": "Invalid key"
}

Rate limits:

HTTP/1.1 429
Content-Type: application/json; charset=utf-8
Retry-After: number
{
  "error": "Too many requests",
  "retry": "number",
}
This endpoint is designed for machine-to-machine use. Do not expose raw API keys in client-side code unless the client is fully trusted and isolated from users.

Rate limits

The route is not behind any authentication or middlewares. its protected solely by rate limiters that differentiate between "probing" (failures) and "usage" (success):

  • rate_limiters.apiTokensLimiters.consumptionRateLimiter - The main limiter for the failure mode. It will consume a point on Every failed attempt of verification. On success it will be restarted, which means the client, whose not been rate limited by it, could continue send valid verified requests. The limiter enforces 10 requests in a 1 minute window, that will ban for 1 hour if this threshold is reached. No consecutive attempts is provided, which means violating this rule will result in a permanent ban.
  • 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. It is disabled by default in this endpoint and if enabled, it will run on every request and will not be restarted on successful verification.
    • burstLimiter - Will block the client for 15 minutes.
    • slowLimiter - Will block for 1 hour.

Using the library

When using the service as a library, you verify tokens with verifyApiKey:

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

const result = await verifyApiKey(
  rawKey,
  false,
  'restricted',
  false,
  false,
  ipAddress
)

if (result.ok) {
  console.log(result.data.name)
}

The parameters you pass to it, controls the security and configuration of the verification process:

Parameters

FieldTypeDescription
rawKeystringThe Raw, un hashed API token to verify
skipCountUpdatesbooleanIf true, the token is verified without incrementing usage counters usageCount and lastUsed
providedPrivilegedemo | restricted | protected | full | customThe privilege level that must match the stored token privilege
byPassIpCheckbooleanIf true, the IP restriction check is skipped
isInternalHashbooleanIf true, the input is already treated as an internal hash skipping checksum and signature verification. Defaults to false
ipAddressstring | undefinedThe caller IP address used for restriction checks

Signature

async function verifyApiKey(
  rawKey: string,
  skipCountUpdates: boolean,
  providedPrivilege: 'demo' | 'restricted' | 'protected' | 'full' | 'custom',
  byPassIpCheck: boolean,
  isInternalHash?: boolean,
  ipAddress?: string
): Promise<Results<VerifySuccessResponse>>

If the verification succeeded, you get back the following object:

{
  ok: true,
  date: new Date().toISOString(),
  data: {
      name: 'mytoken',
      tokenId: 12,
      userId: 1234,
      createdAt: "2026-04-30T10:00:00.000Z",
      expiresAt: "2026-04-30T11:00:00.000Z",
      lastUsed: "2026-04-30T10:12:00.000Z",
      usageCount: 1234,
      providedPrivilege // Validated privilege
  }
}

In any case the token is invalid, the checksum is invalid, or if the token haves insufficient privileges, you will get the following response:

{
 ok: false,
 date: new Date().toISOString(),
 reason: 'Invalid key'
}

If ip restrictions is configured the reason will be 'Invalid Host'.

When the token have an expiration date and it expired, the token will be marked as invalid, and can't be used again. In such cases the reason will be "Token expired". In any case of database errors the reason will result in 'Server error validating token.'.

When debugging or to get more information why may a verification is failing use the Logs with a level of info under the branch of branch: 'api_tokens', and type of type: 'verify'.


Verification Process

When a request is made to verify a token, either via the public API route or internally using the verifyApiKey function, the system executes a multi-step validation sequence. This process is designed to reject invalid payloads as fast as possible to save database resources, while ensuring secure, usage tracking for valid keys.

Fast Validation

Before touching the database, the system validates the structural integrity of the raw API key.

  • Unless the token is explicitly flagged as an internal hash (isInternalHash), it splits the key into its three components: the prefix, the random payload, and the checksum.
  • If any component is missing, it is immediately rejected.
  • The system recalculates the expected checksum from the random payload and performs a timing comparison against the provided checksum. If they do not match, the token is counterfeit or corrupted, and the request is aborted.

Cryptographic Hashing

Once the structure is validated, the raw token is hashed with sha-256. The database never compares raw tokens; it interacts with these hashed representations to prevent exposure in the event of a database leak.

Database Lookup & Privilege Check

The system opens a database transaction and queries the api_tokens table for the hashed key.

  • It explicitly looks for a token that is actively valid (valid = 1) and matches the exact providedPrivilege.
  • If the token does not exist, was previously revoked, or lacks the requested privilege, the transaction is rolled back and the request is rejected with an Invalid key error.
Unless skipCountUpdates is enabled, this query uses a FOR UPDATE lock. This prevents race conditions, ensuring that concurrent requests using the same token safely queue to update the usage counters accurately.

IP Restriction

If the caller requires IP validation (byPassIpCheck is false) and the token owner configured an IP whitelist (restricted_to_ip_address):

  • The system verifies that an IP address was actually provided in the request.
  • It checks if the provided IP exists within the token's configured whitelist array.
  • If either check fails, the transaction is rolled back, and the system logs an unauthorized access attempt, returning an Invalid Host error.

Expiration

The system checks if the token has an expires_at timestamp.

  • If an expiration date exists and the current time has surpassed it, the token is permanently invalidated in the database (valid = 0).
  • The system commits this invalidation state, logs the expiration, and returns a Token expired error to the caller.

Usage Tracking & Metadata

If the token successfully passes all security, network, and lifecycle checks:

  • Unless skipCountUpdates is active, the system increments the token's usage_count by 1 and updates the last_used timestamp to the current UTC time.
  • The transaction is committed.
  • A success payload described above, is returned to the caller, containing rich metadata about the token, including its configured name, internal IDs, and current usage metrics.

References

Configuration

All verification behavior is controlled under apiTokens and rate_limiters.apiTokensLimiters in the configuration passed to configuration().

apiTokens

OptionTypeDefaultDescription
rateLimitOnSuccessfulRequestbooleanfalseWhen enabled, successful verification requests consume the general limiter

Rate limiters

LimiterDescription
rate_limiters.apiTokensLimiters.consumptionRateLimiterThe main rate limiter for token verification, that limits only failed attempts
rate_limiters.apiTokensLimiters.generalUnionLimiterGeneral burst limiter, that if enabled, will run on every request

Metadata returned on success

FieldDescription
nameFriendly token name defined in the creation step
tokenIdDatabase identifier
userIdOwner of the token
createdAtCreation timestamp
expiresAtExpiration timestamp, if set
lastUsedLast successful usage time
usageCountNumber of successful uses
providedPrivilegePrivilege level that was verified

Logo