Sign-in and signup
Configure email verification codes, Google sign-in, and who can sign up.
Multica signs users in with email verification codes by default; Google OAuth can be added on top. Existing users can always sign in again — signup restrictions only decide whether new accounts can be created.
Email verification codes
After the user enters an email address, Multica sends a 6-digit verification code. The code is valid for 10 minutes; once it verifies, the browser receives a sign-in cookie.
Email can be delivered through Resend or SMTP. When both are configured, SMTP_HOST takes priority.
Using Resend
-
Verify a sending domain and create an API key at Resend.
-
Set:
RESEND_API_KEY=re_xxxxxxxxxxxxxxxx RESEND_FROM_EMAIL=[email protected] -
Restart the API service.
RESEND_FROM_EMAIL must belong to a domain already verified in Resend.
Using SMTP
At minimum, set the host and sender address:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=multica
SMTP_PASSWORD=<password>
SMTP_FROM_EMAIL=[email protected]Common connection modes:
| Scenario | Configuration |
|---|---|
| Internal anonymous relay | SMTP_PORT=25, leave username and password empty |
| STARTTLS | SMTP_PORT=587; upgrades to TLS by default when the server supports it |
| Implicit TLS | SMTP_PORT=465, or set SMTP_TLS=implicit explicitly |
If SMTP_FROM_EMAIL is unset, it falls back to RESEND_FROM_EMAIL. Private CAs or self-signed certificates require adding the CA to the container trust store; SMTP_TLS_INSECURE=true skips certificate verification and should only be used temporarily on a trusted internal network.
Some strict relays also require a valid EHLO name:
SMTP_EHLO_NAME=mail.example.comBehavior without an email service
The server still starts, but verification codes and invitation links are only written to the log; no email is sent. This suits local development, not production.
The startup log states whether the current mode is Resend API, SMTP relay, or DEV mode.
Fixed local verification code
Local automated tests can set a fixed verification code:
APP_ENV=development
MULTICA_DEV_VERIFICATION_CODE=888888The code must be 6 digits. The fixed code is ignored when APP_ENV=production.
Do not enable a fixed code on a publicly reachable instance. The production combination is APP_ENV=production with an empty MULTICA_DEV_VERIFICATION_CODE.
Google sign-in
-
Create an OAuth 2.0 client in the Google Cloud Console.
-
Add the Multica frontend's callback URL to Authorized redirect URIs:
https://multica.example.com/auth/callback -
Set:
GOOGLE_CLIENT_ID=xxxxx.apps.googleusercontent.com GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxx GOOGLE_REDIRECT_URI=https://multica.example.com/auth/callback -
Restart the API service.
The URLs in the Google Console and in GOOGLE_REDIRECT_URI must match exactly, including protocol, port, and trailing slash. Once configured, the sign-in page shows a Google sign-in button; the frontend image needs no rebuild.
Signup restrictions
Three variables together decide whether a new account can be created:
| Variable | Effect |
|---|---|
ALLOWED_EMAILS | Full email addresses allowed to sign up, comma-separated |
ALLOWED_EMAIL_DOMAINS | Email domains allowed to sign up, comma-separated |
ALLOW_SIGNUP | Whether signup is allowed when no allowlist is configured; defaults to true |
The evaluation order is:
- The email matches
ALLOWED_EMAILS— allow. - Or its domain matches
ALLOWED_EMAIL_DOMAINS— allow. - With no allowlist configured and
ALLOW_SIGNUP=true— allow. - Otherwise, if the email has a pending, unexpired workspace invitation — allow.
- Otherwise — reject.
Common configurations:
# Company domain and invited users
ALLOW_SIGNUP=false
ALLOWED_EMAIL_DOMAINS=company.com
# Also admit one external collaborator
ALLOWED_EMAILS=[email protected]Both allowlists also work as an explicit exception list when ALLOW_SIGNUP=false.
Invitations and signup restrictions
A pending, unexpired workspace invitation allows its email address to create an account even when ALLOW_SIGNUP=false or the address does not match ALLOWED_EMAILS or ALLOWED_EMAIL_DOMAINS. This exception also applies when ALLOW_SIGNUP=true with an allowlist configured: allowlists are not a hard boundary against invited users.
Existing users can continue to sign in. New users without a matching allowlist entry or open signup need a pending, unexpired invitation; missing, expired, accepted, declined, and revoked invitations do not grant signup permission. The invitation is checked when requesting a login code and again when creating the account, including Google sign-in.
You do not need to add ordinary invitees to ALLOWED_EMAILS or restart the server. The invitation must still be accepted through the normal invitation flow to join the workspace.
Revoking an invitation does not delete an account already created using it or prevent that existing account from signing in.
Session lifetime
Sessions slide. The lifetime below is an idle bound, not a countdown from sign-in: once a session drops below half of it, the next request re-issues it with a full lifetime again, so an account in continuous use is never signed out on a schedule. There is no absolute cap.
The bound is one-sided. A session is only re-issued once it is past the halfway point, so a session last used while it still had more than half its lifetime left is not extended — the idle gap that is always survivable is half the value below, not all of it.
Adjust with AUTH_TOKEN_TTL, which accepts a Go duration or a positive integer of seconds:
AUTH_TOKEN_TTL=720hThe minimum is 60s; anything shorter is clamped up to it and logs a warning at startup. Below that, the renewal cadence the server derives would fall under the floor clients apply to it, and clients would check for renewal less often than the session can survive.
Restart the API service after changing it. The value applies to sessions issued or re-issued afterwards — because sessions slide, an existing session picks up the new lifetime the next time it is re-issued.
Next steps
- Environment variables — the full variable reference.
- Authentication and tokens — sign-in sessions and token types.
- Members and roles — invitations and roles.