Multica / Self-deployment and reference
Login and registration configuration
Equipped with Email verification code login, Google OAuth, registration whitelist and local testing verification code.

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:
Option A: Resend (recommended for public network/cloud deployment)
-
Create an account at Resend and verify your domain name
-
Create API key
-
Set environment variables:
RESEND_API_KEY=re_xxxxxxxxxxxxxxxx RESEND_FROM_EMAIL=noreply@yourdomain.com # Must be a Resend verified domain name -
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
888888by default" has been removed. Unless you configure it explicitly, entering888888will 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 setMULTICA_DEV_VERIFICATION_CODEto a 6-digit number, such as888888, and keepAPP_ENVto 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.
-
Go to Google Cloud Console to create an OAuth 2.0 client
-
Authorized redirect URIs (Authorized redirect URIs) fill in your Multica front-end address plus
/auth/callback, for example:https://multica.yourdomain.com/auth/callback -
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 -
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 (httpvshttps), trailing slash, and port. Inconsistency Google will reject the entire OAuth flow and the error the user will see isredirect_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, defaulttrue. Setfalseto 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=trueto 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, andALLOW_SIGNUP=truecannot block them. To truly "allow everyone", leave all three environment variables blank (orALLOW_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