Vibe Coding
Back to Multica

Multica / Self-deployment and reference

Login and registration configuration

Equipped with Email verification code login, Google OAuth, registration whitelist and local testing verification code.

Login and registration configuration key concepts infographic
Login and registration configuration key concepts infographic

Overview

Multica supports two login methods: Email + verification code (default) and Google OAuth (optional). After successful login, the server issues a JWT cookie valid for 30 days. This page explains how to configure it, how to restrict who can register, and how to use local test verification codes safely.

For the list of environment variables used above, see Environment Variables; for how to use tokens and life cycle details, see Authentication and Tokens.

How does email + verification code login work?

The user enters their email address on the login page → the server sends a 6-digit verification code → the user fills it in → the server verifies → issues a JWT cookie. It's standard procedure. Two email sending channels are supported, choose one according to the deployment environment:

  1. Create an account at Resend and verify your domain name

  2. Create API key

  3. Set environment variables:

    RESEND_API_KEY=re_xxxxxxxxxxxxxxxx
    RESEND_FROM_EMAIL=noreply@yourdomain.com # Must be a Resend verified domain name
    
  4. Restart the server

Option B: SMTP relay (intranet/self-deployment)

Suitable for scenarios where the intranet cannot access api.resend.com, or there is already an internal mail relay (Microsoft Exchange, Postfix, self-deployed SendGrid, etc.). When set at the same time, SMTP_HOST has a higher priority than RESEND_API_KEY: as long as SMTP_HOST is not empty, the server will always take the SMTP path. Even if RESEND_API_KEY is also configured, the verification code and invitation email will not be sent out of the intranet through Resend.

The SMTP path covers the three relay modes exposed by most local mail servers (especially Microsoft Exchange's receive connector):

Mode Port Authentication TLS
Anonymous internal relay 25 None - Trust by IP/Subnet No TLS on link (internal segment only)
Authentication submission (submission) 587 SMTP_USERNAME + SMTP_PASSWORD STARTTLS, automatic upgrade
Implicit TLS (SMTPS) 465 Optional (SMTP_USERNAME + SMTP_PASSWORD) TLS handshake upon connection - port 465 is automatically enabled; non-standard ports are forced to open by setting SMTP_TLS=implicit

Anonymous Exchange relay, port 25 - Classic "internal SMTP relay" / Exchange anonymous receive connector, released according to trusted subnet, no credentials required:

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

Authentication submission, port 587 - requires service account relay; the server will automatically upgrade when advertising STARTTLS:

SMTP_HOST=smtp.internal.example.com
SMTP_PORT=587
SMTP_USERNAME=multica
SMTP_PASSWORD=...
SMTP_TLS_INSECURE=false # Only changed to true if private CA / self-signed certificateRESEND_FROM_EMAIL=noreply@yourdomain.com

Implicit TLS (SMTPS), port 465 - Applicable to service providers that only provide SMTPS and do not advertise STARTTLS (such as Alibaba Cloud/Tencent Enterprise Email). Port 465 will automatically enable implicit TLS; non-standard SMTPS port setting SMTP_TLS=implicit (alias: smtps, ssl) can be forcibly enabled:

SMTP_HOST=smtp.qiye.aliyun.com
SMTP_PORT=465 # 465 Automatically enable implicit TLS
SMTP_USERNAME=multica@yourdomain.com
SMTP_PASSWORD=...
SMTP_TLS=implicit #465 can be omitted; required on non-standard SMTPS ports
RESEND_FROM_EMAIL=noreply@yourdomain.com

Strictly public network relays (such as Google Workspace smtp-relay.gmail.com) also require a legal EHLO name. They will reject the default localhost greeting from the public IP, and the relay will immediately disconnect - this will not report an error during the greeting phase, but will appear as an unintelligible EOF (smtp auth: EOF) on a subsequent command. Set SMTP_EHLO_NAME to the FQDN expected by the relay; it defaults to the machine hostname, which is usually not a valid FQDN inside the container:

SMTP_HOST=smtp-relay.gmail.com
SMTP_PORT=587
SMTP_EHLO_NAME=mail.yourdomain.com # FQDN accepted by relay; the default is the (non-FQDN) container host name
RESEND_FROM_EMAIL=noreply@yourdomain.com

When starting, the server will print the currently selected provider and the negotiated TLS mode, such as EmailService: SMTP relay exchange.internal.example.com:25 (starttls) from=noreply@example.com or … smtp.qiye.aliyun.com:465 (implicit-tls) from=… (or Resend API / DEV mode), the password will not appear in the log. If you don’t see the SMTP line after restarting, it means that SMTP_HOST has not entered the process. Confirm the container environment (docker compose -f docker-compose.selfhost.yml exec backend env | grep SMTP).

Both are unsuitable: The server does not report an error, but all emails that should be sent only hit the stdout of the server. Local development is convenient (you copy the verification code from the log), and the production environment is a black hole.

Fixed local test verification code

Production deployments should leave MULTICADEVVERIFICATIONCODE empty and set APPENV=production. If you use make selfhost / docker-compose.selfhost.yml to self-deploy, APPENV defaults to production.

**Do not enable fixed verification codes on publicly accessible instances. ** The old behavior of "non-production accepts 888888 by default" has been removed. Unless you configure it explicitly, entering 888888 will be rejected like a normal error code. Local development without any email backend (neither Resend nor SMTP is set up) should use the random verification code printed in the server log. If you need deterministic local/private automated testing, you can set MULTICA_DEV_VERIFICATION_CODE to a 6-digit number, such as 888888, and keep APP_ENV to non-production:

APP_ENV=development
MULTICA_DEV_VERIFICATION_CODE=888888
```
This shortcut code will be ignored when `APP_ENV=production`.

How to configure Google OAuth

Optional. If it is not matched, you can only log in via email + verification code; if it is matched, there will be an extra "Log in with Google" button on the login page.

  1. Go to Google Cloud Console to create an OAuth 2.0 client

  2. Authorized redirect URIs (Authorized redirect URIs) fill in your Multica front-end address plus /auth/callback, for example:

    https://multica.yourdomain.com/auth/callback
    
  3. Get the client ID and client secret and set three environment variables:

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

Hot Validation: The front end reads these configurations when running through /api/config - just restart the server after making changes, and the front end does not need to rebuild the image or redeploy.

The callback URI must be exactly the same in Google Console and GOOGLE_REDIRECT_URI, including protocol (http vs https), trailing slash, and port. Inconsistency Google will reject the entire OAuth flow and the error the user will see is redirect_uri_mismatch.

How to restrict who can register

Three levels of environment variables are combined according to priority:

graph TD
    Start[New user's first login] --> A{email is in<br/>ALLOWED_EMAILS?}
    A -- Yes --> Allow[Allow registration]
    A -- No --> B{domain is in<br/>ALLOWED_EMAIL_DOMAINS?}
    B -- Yes --> Allow
    B -- No --> C{Any whitelist<br/>is not empty?}
    C -- Yes --> Block [Reject]
    C -- No --> D{ALLOW_SIGNUP<br/>= true?}
    D -- Yes --> Allow
    D -- No --> Block

Old users who have logged in can always log in again - the signup whitelist only takes effect for first time registration and will not block old users.

  • ALLOWED_EMAILS (highest priority) - Explicit email whitelist, comma separated. If it is not empty, only the emails in the list can be registered.
  • ALLOWED_EMAIL_DOMAINS—— Domain name whitelist, comma separated (e.g. company.io,partner.com).
  • ALLOW_SIGNUP - master switch, default true. Set false to turn off registration completely.

**Three-layer whitelist has AND semantics, not OR. ** Many people's first instinct is to "set ALLOWED_EMAIL_DOMAINS=company.io + ALLOW_SIGNUP=true to allow company.io and everyone else" - not. As long as a non-null value is set in any layer of the whitelist, unmatched mailboxes will be directly rejected, and ALLOW_SIGNUP=true cannot block them. To truly "allow everyone", leave all three environment variables blank (or ALLOW_SIGNUP=true).

Typical configuration:

Requirements Configuration
Company intranet, only company.io employees are allowed ALLOWED_EMAIL_DOMAINS=company.io
Company intranet + several external collaborators ALLOWED_EMAIL_DOMAINS=company.io + collaborators’ personal emails added to ALLOWED_EMAILS
Completely close self-service registration, only invites ALLOW_SIGNUP=false
Open registration (not recommended for production use) Leave all three blank

Can I still invite people in after registering is closed?

Only available to people who already have a Multica account. The signup whitelist is not checked in the step of accepting the invitation - if the other party has already registered (for example, in another workspace), they can click the link to log in and accept it directly.

But for those who have not registered yet, invitations cannot save them. They must log in before accepting the invitation. The first step of logging in (sending a verification code) will pass the signup whitelist check. If you set ALLOW_SIGNUP=false, or their email address is not in ALLOWED_EMAILS / ALLOWED_EMAIL_DOMAINS, they will not be able to complete the registration and will not be able to accept the invitation.

To invite an external collaborator who has not registered yet: temporarily add their email address to ALLOWED_EMAILS and then remove it after they register + accept the invitation.

See Members and Permissions for the creation and use of invitations.

Next step

  • Environment Variables - Complete definition of environment variables used on this page
  • Authentication and Tokens - Classification and use of JWT / PAT / Daemon Token
  • Troubleshooting - Common troubleshooting issues such as failure to receive verification code, OAuth reporting redirect_uri_mismatch, and registration rejection