Для разработчиков

Подключить Mars ID
к своему проекту

Вход через Telegram вместо своей формы регистрации. Работает и на нашем хостинге, и на твоём — Vercel, Render, VPS, что угодно. Бесплатно, ключи выдаём за час.

OpenID Connect JWT · HS256 cookie на .marshub.uz Telegram Mini App
Путь 1

Замок на готовый сайт

Сайт уже задеплоен, кода трогать не хочется, но видеть его должны не все. Проверку делает наш прокси до того, как запрос дойдёт до твоих файлов.

Целый сайт имя.marshub.uz

Напиши нам имя поддомена и список Telegram ID, кого пускать. Мы включаем замок в базе — рестарт и правка кода не нужны.

  • пустой список = пустим любого, кто вошёл в Mars ID
  • список ID = пустим только их, остальным 403
  • без замка сайт публичный, как и был

Гостя без входа само перекинет на id.marshub.uz/login и вернёт обратно на твою страницу.

Одна страница pages.marshub.uz

Тут всё сам, без нас. У каждой опубликованной страницы есть настройка доступа:

  • public — открыта всем
  • password — общий пароль, аккаунт не нужен
  • marsid — вход через Telegram, можно со списком ID

Через MCP-инструмент marshub_set_page_access это делает и твой AI-агент.

Что это не умеет: замок отвечает на вопрос «пускать или нет». Если внутри нужны роли, личный кабинет или «этот заказ мой» — нужен бэкенд, это путь 2.

Путь 3

Свой хостинг, любой домен

Cookie живёт только на .marshub.uz — снаружи её нет. Поэтому для проекта на Vercel, Render, своём VPS или на localhost работает стандартный OpenID Connect. Mars ID — полноценный OIDC-провайдер, его понимает почти любая библиотека авторизации из коробки.

Шаг 1 — получить ключи

Напиши @marvinaka и пришли три вещи:

Название проекта

Как подписать в списке клиентов

Callback URL

Точный адрес, куда вернуть юзера. Можно несколько: прод, стейдж, localhost

Кому доступ

Всем с Mars ID / только ученикам / только сотрудникам

В ответ придут client_id и client_secret. Callback сверяется побуквенно — никаких масок и «начинается с»: лишний слэш в конце уже другой адрес.

Шаг 2 — настройки для библиотеки

Если твой фреймворк умеет OIDC (NextAuth/Auth.js, Passport, Authlib, Spring Security, Keycloak-adapter, Grafana, Forgejo, GlitchTip…) — просто вставь эти значения, кода почти не будет.

ПараметрЗначение
Issuerhttps://id.marshub.uz
Discovery / well-knownhttps://id.marshub.uz/.well-known/openid-configuration
Authorization URLhttps://id.marshub.uz/oauth/authorize
Token URLhttps://id.marshub.uz/oauth/token
UserInfo URLhttps://id.marshub.uz/oauth/userinfo
Scopesopenid profile email
Response typecode
id_token алгоритмHS256 (подпись — твоим client_secret)
Аутентификация клиентаclient_secret_post или client_secret_basic

Чего у нас нет: PKCE, refresh-токенов и RS256/JWKS. Токены живут час, код обменивается один раз за 5 минут. Для SPA без бэкенда это значит: обменивать код должен сервер, класть client_secret в браузер нельзя.

Шаг 3 — если пишешь руками

// 1. Отправляем юзера на Mars ID
app.get('/login', (req, res) => {
  const state = crypto.randomBytes(16).toString('hex');
  req.session.state = state;                     // проверим на возврате

  const u = new URL('https://id.marshub.uz/oauth/authorize');
  u.searchParams.set('client_id', process.env.MARS_CLIENT_ID);
  u.searchParams.set('redirect_uri', 'https://myapp.uz/callback');
  u.searchParams.set('response_type', 'code');
  u.searchParams.set('scope', 'openid profile email');
  u.searchParams.set('state', state);
  res.redirect(u.toString());
});

// 2. Юзер вернулся с кодом — меняем код на токены
app.get('/callback', async (req, res) => {
  if (req.query.state !== req.session.state) return res.status(400).send('bad state');

  const tokens = await fetch('https://id.marshub.uz/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_type:    'authorization_code',
      code:          req.query.code,
      redirect_uri:  'https://myapp.uz/callback',   // тот же, что на шаге 1
      client_id:     process.env.MARS_CLIENT_ID,
      client_secret: process.env.MARS_CLIENT_SECRET,
    }),
  }).then(r => r.json());

  // 3. Кто это — из id_token (подписан твоим client_secret) или запросом:
  const user = await fetch('https://id.marshub.uz/oauth/userinfo', {
    headers: { Authorization: `Bearer ${tokens.access_token}` },
  }).then(r => r.json());

  // user = { sub, name, preferred_username, handle, email,
  //          role, tg, is_staff, is_student, core_id }
  req.session.user = user;
  res.redirect('/');
});
Путь 4

Telegram Mini App

Внутри Telegram человека уже опознали — показывать кнопку входа глупо. Telegram отдаёт подписанную строку initData, ты пересылаешь её нам, мы проверяем подпись и ставим ту же cookie __mars_id.

// в Mini App, сразу на старте
const tg = window.Telegram.WebApp;

const me = await fetch('https://id.marshub.uz/api/auth/telegram-webapp', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  credentials: 'include',                // иначе cookie не сохранится
  body: JSON.stringify({ initData: tg.initData }),
}).then(r => r.json());

// me = { id, name, handle, role, tg, needs_handle }
// needs_handle: true → человек ещё не выбрал себе имя

Запрос идёт с чужого домена, поэтому origin твоего Mini App нужно внести в белый список — напиши нам адрес. Подпись initData действительна сутки.

Справочник

Что где лежит

Адреса

EndpointМетодЧто делает
/login?next=URLGETСтраница входа, после успеха вернёт на URL
/logoutGETУдалить cookie
/api/verifyGETПроверить токен из Authorization: Bearer или cookie → { id, name, role, tg }
/api/meGETТо же, но только по cookie. Из браузера с другого домена — нужен белый список origin'ов
/api/auth/telegram-webappPOSTinitData Mini App → cookie
/.well-known/openid-configurationGETOIDC Discovery
/oauth/authorizeGETНачало OAuth-флоу
/oauth/tokenPOSTcode → access_token + id_token
/oauth/userinfoGETДанные по Bearer access_token
/healthGETЖивой ли сервис

Всё относительно https://id.marshub.uz

Что внутри токена

ПолеЧто это
subUUID пользователя, не меняется
nameИмя из Telegram
handleНик (aziz), может быть null до онбординга
rolemember · student · intern · tutor · mentor · team · admin
tgTelegram ID, число
emailhandle@marshub.uz
is_staffСотрудник школы (из HR-базы)
is_studentДействующий ученик (из Core)
core_idID в основной базе школы, если связан
expЖивёт 30 дней

role — это «кто человек», а не «что ему можно». Права держи у себя: is_staff/is_student для грубого разделения, свой список — для точного.

Грабли, на которые все наступают

  • 01Cookie httpOnly. Из JavaScript её не прочитать — и не надо. Проверка только на сервере.
  • 02На localhost cookie не придёт. Она привязана к .marshub.uz. Для локальной разработки бери OIDC с callback на localhost — мы такой зарегистрируем.
  • 03Проверяй алгоритм. Если верифицируешь подпись сам — всегда явно указывай HS256, иначе библиотека может принять токен без подписи.
  • 04Не верь фронтенду. Роль, id и «я админ» приходят из токена на сервере. Всё, что прислал браузер в теле запроса, — не доказательство.
  • 05Права меняются не мгновенно. Токен живёт 30 дней: сняли роль — в старом токене она ещё есть. Если критично, дёргай /api/verify или храни отзыв у себя.
  • 06Кодируй next. Адрес возврата — через encodeURIComponent, иначе потеряются параметры после ?.
Пишешь с AI

Отдай задачу агенту

Claude Code, Cursor, Mars Code — любому агенту хватит одной страницы фактов. Скинь ему ссылку и задачу, дальше он сам.

Промпт, который можно скопировать как есть:

Подключи вход через Mars ID к моему проекту.

Факты и эндпоинты возьми здесь: https://marshub.uz/id/llms.txt
Мой проект: <стек, фреймворк>
Живёт на: <имя.marshub.uz | внешний хостинг | Telegram Mini App>

Требования:
- если проект на *.marshub.uz — читать cookie __mars_id и проверять
  её запросом на https://id.marshub.uz/api/verify (Authorization: Bearer),
  общий AUTH_SECRET не использовать;
- если проект на внешнем домене — OAuth2 authorization code
  (issuer https://id.marshub.uz), client_id/secret из переменных окружения,
  без PKCE и refresh-токенов — их у провайдера нет;
- неавторизованного редиректить на
  https://id.marshub.uz/login?next=<url-encoded адрес возврата>;
- роль и права проверять только на сервере, фронтенду не верить;
- добавить ссылку выхода на https://id.marshub.uz/logout.

Нужны ключи или маршрут?

Регистрация OIDC-клиента, замок на сайт, свой поддомен, белый список для Mini App — всё это делаем руками и быстро. Напиши, что подключаешь.