Documentation

Documentation

Authentication via messengers instead of SMS — no per-message costs. Every authenticated user becomes reachable for notifications by phone number through the same bots. The system picks the right messenger automatically; you control the priority.

JWT Token

The token field in the confirmed session response is a signed JWT (RS256). Profile data (name, username) is taken directly from the messenger at the time of authentication and is not stored on the server. Verify the token using the public key available at the JWKS endpoint.

Token payload

FieldDescription
_idUser ID (24 hex chars)
user_idMessenger user ID (Telegram / MAX / WhatsApp numeric ID)
typeMessenger type: "telegram", "max" or "whatsapp"
phoneUser phone number (e.g. "+79001234567")
first_nameFirst name from messenger profile (not stored, taken at auth time)
last_nameLast name (may be null, not stored)
usernameMessenger username (may be null, not stored)
app_idApplication ID the token was issued for
issIssuer (Antirius Auth Cloud server URL)
iatIssued at (Unix timestamp)
expExpiration (Unix timestamp)

Public key (JWKS)

The public key for verifying JWT tokens is available at the standard JWKS endpoint:

https://id.antirius.com/.well-known/jwks.json

How to verify the token

Verify the signature using the JWKS public key (RS256), then check that app_id in the token matches your application ID:

Koa (jsonwebtoken + jwks-rsa)

import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const APP_ID = 'YOUR_APP_ID';
const ISSUER = 'https://id.antirius.com';
const JWKS_URI = 'https://id.antirius.com/.well-known/jwks.json';

const jwks = jwksClient({ jwksUri: JWKS_URI });

function verifyAuthToken(token) {
  return new Promise((resolve, reject) => {
    jwt.verify(
      token,
      (header, cb) => {
        jwks.getSigningKey(header.kid, (err, key) => cb(err, key?.getPublicKey()));
      },
      { algorithms: ['RS256'], issuer: ISSUER },
      (err, decoded) => {
        if (err) return reject(err);
        if (decoded.app_id !== APP_ID) return reject(new Error('APP_ID_MISMATCH'));
        resolve(decoded);
      },
    );
  });
}

export async function authMiddleware(ctx, next) {
  const auth = ctx.headers.authorization || '';
  if (!auth.startsWith('Bearer ')) {
    ctx.status = 401;
    ctx.body = { error: 'No token' };
    return;
  }
  try {
    ctx.state.user = await verifyAuthToken(auth.slice(7));
    await next();
  } catch (e) {
    ctx.status = 401;
    ctx.body = { error: e.message || 'Invalid token' };
  }
}

Fastify

import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const APP_ID = 'YOUR_APP_ID';
const ISSUER = 'https://id.antirius.com';
const JWKS_URI = 'https://id.antirius.com/.well-known/jwks.json';

const jwks = jwksClient({ jwksUri: JWKS_URI });

function verifyAuthToken(token) {
  return new Promise((resolve, reject) => {
    jwt.verify(
      token,
      (header, cb) => {
        jwks.getSigningKey(header.kid, (err, key) => cb(err, key?.getPublicKey()));
      },
      { algorithms: ['RS256'], issuer: ISSUER },
      (err, decoded) => {
        if (err) return reject(err);
        if (decoded.app_id !== APP_ID) return reject(new Error('APP_ID_MISMATCH'));
        resolve(decoded);
      },
    );
  });
}

async function authPreHandler(request, reply) {
  const auth = request.headers.authorization || '';
  if (!auth.startsWith('Bearer ')) {
    return reply.code(401).send({ error: 'No token' });
  }
  try {
    request.user = await verifyAuthToken(auth.slice(7));
  } catch (e) {
    return reply.code(401).send({ error: e.message || 'Invalid token' });
  }
}

Alternative: verify via session API

You can also validate the authentication by calling the session status endpoint directly:

GET https://id.antirius.com/api/v1/auth/session/SESSION_ID?type=full

// If confirmed → response includes token + user:
{
  "status": "confirmed",
  "poll_type": "full",
  "token": "eyJhbGciOi...",
  "user": { "_id": "...", "phone": "+79001234567", ... },
  "app_id": "YOUR_APP_ID"
}

// Progress check without JWT (repeatable):
GET https://id.antirius.com/api/v1/auth/session/SESSION_ID?type=status

Call with type=full. If the response contains "status": "confirmed" and a "token" field — the user is authenticated. Use type=status only to check progress without consuming the session.