Документация

Документация

Авторизация через мессенджеры вместо SMS — без расходов на каждое сообщение. Каждый авторизованный пользователь доступен для уведомлений по номеру телефона через тех же ботов. Система сама выбирает мессенджер, а вы задаёте приоритет.

JWT-токен

Поле token в ответе подтверждённой сессии — подписанный JWT (RS256). Данные профиля (имя, username) берутся из мессенджера в момент авторизации и не хранятся на сервере. Для проверки токена используйте публичный ключ, доступный по JWKS-эндпоинту.

Содержимое токена (payload)

FieldDescription
_idID пользователя (24 hex-символа)
user_idID пользователя в мессенджере (числовой ID Telegram / MAX / WhatsApp)
typeТип мессенджера: "telegram", "max" или "whatsapp"
phoneНомер телефона (например "+79001234567")
first_nameИмя из профиля мессенджера (не хранится, берётся в момент авторизации)
last_nameФамилия (может быть null, не хранится)
usernameUsername в мессенджере (может быть null, не хранится)
app_idID приложения, для которого выпущен токен
issИздатель (URL сервера Antirius Auth Cloud)
iatВремя выпуска (Unix timestamp)
expВремя истечения (Unix timestamp)

Публичный ключ (JWKS)

Публичный ключ для проверки JWT доступен по стандартному JWKS-эндпоинту:

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

Как проверить токен

Проверьте подпись токена публичным ключом из JWKS (RS256), затем убедитесь, что app_id в токене совпадает с 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' });
  }
}

Альтернатива: проверка через API сессии

Также можно проверить авторизацию напрямую через эндпоинт статуса сессии:

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

Запрос с type=full. Если в ответе "status": "confirmed" и поле "token" — пользователь авторизован. type=status — только проверка прогресса без деактивации сессии.