Multica Docs

Конфигурация входа и регистрации

Email + verification code, Google OAuth, signup allowlists и локальные test codes.

Multica поддерживает два способа входа: email + verification code (по умолчанию) и Google OAuth (optional). После успешного входа server выдаёт JWT cookie на 30 дней. На этой странице — настройка каждого способа, ограничение signup и главная ловушка self-hosted deployments.

Список env vars — Environment variables; токены и lifecycle — Authentication and tokens.

Как работает вход по email + verification code

Пользователь вводит email → server отправляет 6-digit code → пользователь вводит code → server проверяет → выдаётся JWT cookie. Стандартный поток. Два backend доставки — выберите под deployment:

Option A: Resend (рекомендуется для cloud / public internet)

  1. Создайте аккаунт Resend и verify domain

  2. Создайте API key

  3. Задайте переменные окружения:

    RESEND_API_KEY=re_xxxxxxxxxxxxxxxx
    RESEND_FROM_EMAIL=noreply@yourdomain.com  # must be a domain verified in Resend
  4. Restart server

Option B: SMTP relay (self-hosted / on-premise)

Используйте, когда deployment не достигает api.resend.com или уже есть internal mail relay (Microsoft Exchange, Postfix, on-prem SendGrid и т. д.). SMTP_HOST имеет приоритет над RESEND_API_KEY, если оба заданы — при non-empty SMTP_HOST server всегда идёт через SMTP, даже если RESEND_API_KEY тоже configured, verification и invite mail не покидают internal network.

SMTP path поддерживает три режима relay, типичные для on-premise (в т. ч. Exchange receive connectors):

ModePortAuthTLS
Anonymous internal relay25none — submission trusted by IP / subnetnone on the wire (internal segment only)
Authenticated submission587SMTP_USERNAME + SMTP_PASSWORDSTARTTLS, upgraded automatically
Implicit TLS (SMTPS)465optional (SMTP_USERNAME + SMTP_PASSWORD)TLS handshake on connect — auto-enabled on port 465, or force on a non-standard port with SMTP_TLS=implicit

Anonymous Exchange relay на port 25 — типичный internal SMTP relay / Exchange anonymous receive connector из trusted subnet без credentials:

SMTP_HOST=exchange.internal.example.com
SMTP_PORT=25
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_TLS_INSECURE=false
RESEND_FROM_EMAIL=noreply@yourdomain.com  # reused as the From: header

Authenticated submission на port 587 — relay требует service account; STARTTLS автоматически при advertise:

SMTP_HOST=smtp.internal.example.com
SMTP_PORT=587
SMTP_USERNAME=multica
SMTP_PASSWORD=...
SMTP_TLS_INSECURE=false        # set true only for self-signed / private CA
RESEND_FROM_EMAIL=noreply@yourdomain.com

Implicit TLS (SMTPS) на port 465 — провайдеры только с SMTPS без STARTTLS (Aliyun / Tencent enterprise mail). Port 465 auto-enables implicit TLS; SMTP_TLS=implicit (aliases: smtps, ssl) на non-standard SMTPS port:

SMTP_HOST=smtp.qiye.aliyun.com
SMTP_PORT=465                  # implicit TLS auto-enabled on 465
SMTP_USERNAME=multica@yourdomain.com
SMTP_PASSWORD=...
SMTP_TLS=implicit              # optional on 465; required on a non-standard SMTPS port
RESEND_FROM_EMAIL=noreply@yourdomain.com

Strict public relays (Google Workspace smtp-relay.gmail.com) требуют valid EHLO name. Default localhost с public IP отклоняется — opaque EOF на later command (smtp auth: EOF). Задайте SMTP_EHLO_NAME — FQDN, который принимает relay; default — machine hostname, в container обычно не FQDN:

SMTP_HOST=smtp-relay.gmail.com
SMTP_PORT=587
SMTP_EHLO_NAME=mail.yourdomain.com   # FQDN the relay accepts; defaults to the (non-FQDN) container hostname
RESEND_FROM_EMAIL=noreply@yourdomain.com

При старте server печатает выбранный provider и negotiated TLS — например EmailService: SMTP relay exchange.internal.example.com:25 (starttls) from=noreply@example.com или … smtp.qiye.aliyun.com:465 (implicit-tls) from=… (или Resend API / DEV mode). Password не логируется. Если после restart нет SMTP line — SMTP_HOST не дошёл до process (docker compose -f docker-compose.selfhost.yml exec backend env | grep SMTP).

Если не настроено ни одно: server не падает, но каждое письмо пишется только в stdout. Удобно локально; в production — чёрная дыра.

Fixed local testing codes

Не включайте fixed verification code на publicly reachable instance.

Старое поведение, когда non-production принимал 888888 по умолчанию, удалено. Без явной настройки 888888 — как любой неверный code.

Локальная разработка без email backend — generated code из server logs. Для deterministic local/private automation задайте MULTICA_DEV_VERIFICATION_CODE шестизначным значением вроде 888888 и держите APP_ENV non-production:

APP_ENV=development
MULTICA_DEV_VERIFICATION_CODE=888888

Shortcut игнорируется при APP_ENV=production.

Production: пустой MULTICA_DEV_VERIFICATION_CODE и APP_ENV=production. При deploy через make selfhost / docker-compose.selfhost.yml APP_ENV default — production.

Google OAuth configuration

Optional. Без него — только email + verification code; с ним — «Sign in with Google» на странице входа.

  1. OAuth 2.0 client в Google Cloud Console

  2. Authorized redirect URIs — frontend Multica + /auth/callback, например:

    https://multica.yourdomain.com/auth/callback
  3. Client ID и secret — три переменные:

    GOOGLE_CLIENT_ID=xxxxx.apps.googleusercontent.com
    GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxx
    GOOGLE_REDIRECT_URI=https://multica.yourdomain.com/auth/callback
  4. Restart server.

Runtime: frontend читает через /api/config — после смены restart server, без rebuild.

Redirect URI должен совпадать точно в Google Console и GOOGLE_REDIRECT_URI — protocol (http vs https), trailing slash, port. Любое расхождение — Google отклоняет OAuth; пользователь видит redirect_uri_mismatch.

Restricting who can sign up

Три env vars по приоритету:

Rendering diagram…

Existing users всегда могут войти снова — signup allowlist только для first-time signup, не для returning users.

  • ALLOWED_EMAILS (highest priority) — explicit email allowlist, comma-separated. When non-empty, only listed emails can sign up.
  • ALLOWED_EMAIL_DOMAINS — domain allowlist, comma-separated (for example company.io,partner.com).
  • ALLOW_SIGNUP — master switch, default true. Set false to disable signup entirely.

Три слоя — AND semantics, не OR. Частая ошибка: ALLOWED_EMAIL_DOMAINS=company.io + ALLOW_SIGNUP=true «company.io плюс все». Нет. Non-empty layer — emails вне списка отклоняютсяALLOW_SIGNUP=true не перебивает.

Чтобы «allow everyone» — все три empty (или только ALLOW_SIGNUP=true).

Typical configurations:

GoalConfiguration
Internal only, employees of company.ioALLOWED_EMAIL_DOMAINS=company.io
Internal + a few external collaboratorsALLOWED_EMAIL_DOMAINS=company.io + collaborator addresses added to ALLOWED_EMAILS
Disable self-serve signup entirely, invite-onlyALLOW_SIGNUP=false
Open signup (not recommended for production)All three empty

Can you still invite people when signup is disabled?

Только у кого уже есть Multica account. Accept invite не проверяет signup allowlist — если invitee уже signed up (другой workspace), invite link + sign in → accept.

Never signed up — invite не спасёт. Перед accept нужен sign-in; первый шаг (verification code) проходит allowlist. При ALLOW_SIGNUP=false или email вне ALLOWED_EMAILS / ALLOWED_EMAIL_DOMAINS signup не завершится, invite не примет.

External collaborator без аккаунта: временно добавьте email в ALLOWED_EMAILS, дождитесь signup и accept, затем уберите entry.

Invites — Members and roles.

Далее