/ Docs
Volcano DocumentationAuthenticationConfiguration

Email Domain Allowlist

Restrict which email domains can own an account in your Volcano project, and choose whether the restriction covers signup only or sign-in as well.

Restrict which email domains can own an account in your project, and decide whether accounts that predate the restriction may keep signing in.

SUPERAGENT plan required. Settings: allowed_email_domains, allowed_email_domains_mode Types: Array of strings, string Defaults: [] (any domain), "signup"

Use it for internal tools, customer-specific deployments, or any app where only people with a company address should be able to join.

Enable

Set both settings from the Dashboard, the API, or volcano-config.yaml. An empty list accepts any domain.

The allowlist is a SUPERAGENT feature, to set and to enforce. A HOBBY project that asks for one gets 403 the email domain allowlist is only available on the PRO plan, and a project that downgrades keeps its domains but stops enforcing them — see Plans.

Dashboard: in Auth Settings → General, under Email Domain Allowlist, enter the domains one per line and choose where the list applies: New signups only (signup), Signups and sign-ins (signup_and_signin), or Not enforced (disabled). Picking Signups and sign-ins asks you to confirm first, because saving it signs out every account outside the list.

API:

curl -X PUT https://api.volcano.dev/projects/PROJECT_ID/auth/config \
  -H "Authorization: Bearer PLATFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "allowed_email_domains": ["domain1.com", "domain2.com"],
    "allowed_email_domains_mode": "signup_and_signin"
  }'

volcano-config.yaml:

auth:
  signup:
    allowed_email_domains:
      - domain1.com
      - domain2.com
    allowed_email_domains_mode: signup_and_signin

Modes

allowed_email_domains_mode decides how far the list reaches. An empty list restricts nothing whatever the mode is.

ModeSignupSign-inExisting sessions
signup (default)Blocked outside the listAllowedKept
signup_and_signinBlocked outside the listBlocked outside the listRevoked for the addresses it blocks
disabledAllowedAllowedKept

Pick signup to stop new accounts while letting everyone who already signed up carry on. Pick signup_and_signin to cut off accounts outside the list too — this is the setting that turns the allowlist into an access control rather than a registration filter. Pick disabled to park the list without deleting it, so you can switch the same domains back on later.

What It Covers

Every path that would give an address an account is checked under signup and signup_and_signin:

PathBehavior with a disallowed domain
POST /auth/signup403, no user created
OAuth/SSO callback creating a new user403, no user created
OAuth/SSO callback claiming an existing unconfirmed account by email403
POST /auth/oauth/{provider}/link403 when the provider address would become a new address on the account
POST /auth/user/convert-anonymous403, the user stays anonymous
POST /auth/user/change-email403, the address is unchanged
POST /auth/user/confirm-email-change403 if the allowlist narrowed after the change was requested
POST /auth/user/methods/{methodId}/promote403, the account keeps its current primary
Anonymous signup (no email)Allowed — nothing to check

Promotion is on that list because it re-derives the account's canonical email from the promoted identity. An account can hold an identity that predates the restriction, and promoting it would move the account onto an address signup would refuse.

Under signup_and_signin, every path that hands out a session is checked as well:

PathBehavior with a disallowed domain
POST /auth/signin403, no session issued
OAuth/SSO callback for an already-linked account403
POST /auth/oauth/exchange403
POST /auth/device/token403 access_denied
POST /auth/refresh403, the session cannot be extended
POST /auth/platform/exchange403, no CLI platform token is minted
Anonymous sign-inAllowed — anonymous accounts carry an internal address

Linking a provider whose address the account already owns keeps working, so a user grandfathered in before the restriction can still add a provider.

Sign-in is judged on the account's canonical email — the address of its primary identity, the one GET /auth/user returns — not on the address the request presented. An account with several identities is therefore either in or out as a whole: it cannot sign in through a secondary address that happens to be on the list while its canonical address is not. That is the same address the session revocation matches, so the two never disagree.

The error message is email domain is not allowed for this project.

Matching Rules

Matching is an exact, case-insensitive comparison of the part after the last @:

AllowlistAddressResult
domain1.comuser@domain1.comAllowed
domain1.comUser@DOMAIN1.COMAllowed
domain1.comuser+tag@domain1.comAllowed
domain1.comuser@mail.domain1.comRejected
domain1.comuser@domain1.com.evil.comRejected

Subdomains are not implied. List mail.domain1.com explicitly if you want to accept it.

Entry Format

Entries are normalized before they are stored, so the forms people paste all converge:

You writeStored as
@Domain1.comdomain1.com
DOMAIN1.com domain1.com
domain1.com.domain1.com

Duplicates are dropped and the configured order is kept. An entry must be a bare domain with a top-level domain of at least two letters, so localhost, user@domain1.com, and domain 1.com are rejected with a 400 when you save. The list holds at most 100 domains.

Existing Users

Under signup and disabled, adding a restriction never locks anyone out. Users whose domain is no longer on the list keep signing in normally; the allowlist only governs new accounts and email changes.

Under signup_and_signin, saving the policy signs out every account outside the list right away — their sessions are deleted, so their access tokens stop working on the next request rather than at expiry. The accounts themselves stay, so putting the domain back restores access; delete the users if you need them gone for good.

Nobody is signed out while the list is parked on a HOBBY project: sign-in admits the same accounts, so ending their sessions would only interrupt users the project can no longer keep out.

Saving and signing out are one operation: if the sign-out fails, the save fails with it and the previous policy stays in force, so a project never advertises a lock it has not applied. A sign-in that was already in flight when you saved can still land a session — it read the old policy — but it cannot be refreshed or exchanged for a CLI token, so it ends when its access token expires.

One exception to "right away": a Realtime connection that is already open is not closed, because its session was checked when it connected. It stops working as soon as it reconnects. Close those connections from your app if you need the lock to reach live subscribers immediately.

Anonymous accounts are never affected: whether they may exist at all is governed by anonymous users instead. The exemption follows the account being anonymous, not the address it carries — anonymous.volcano.internal is reserved for accounts the platform mints, and a signup asking for an address inside it is refused with the same 403 whatever the mode, including disabled.

Builder debug users (*@volcano.local, created by the project owner from the dashboard) are provisioned directly and never pass through the signup check. Saving a signup_and_signin policy does end their session; the next debug run mints a new one.

Remove the Restriction

Two ways, depending on whether you want to keep the list:

# Park the list — the domains stay, nothing is enforced.
curl -X PUT https://api.volcano.dev/projects/PROJECT_ID/auth/config \
  -H "Authorization: Bearer PLATFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"allowed_email_domains_mode": "disabled"}'

# Clear the list — the domains are gone.
curl -X PUT https://api.volcano.dev/projects/PROJECT_ID/auth/config \
  -H "Authorization: Bearer PLATFORM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"allowed_email_domains": []}'

In volcano-config.yaml, allowed_email_domains: [] clears the list; omitting a key leaves the stored value untouched. From the Dashboard, Not enforced parks the list and emptying the field clears it.

Both work on any plan — see below.

Plans

The allowlist is a SUPERAGENT feature, to configure and to enforce.

A project that drops to HOBBY keeps its domains — nothing is deleted, and GET /auth/config still returns them — but they stop deciding anything: signups and sign-ins from any domain go through again, and the sessions the policy would have ended are left alone. Upgrading to SUPERAGENT puts the stored list straight back to work, without anyone re-saving it. Plan changes reach the API within a few seconds.

Downgrading reopens signups you closed. If the list is the only thing keeping strangers out of your app, disable signup (enable_signup: false) before you downgrade.

What a HOBBY project can do to the list it configured while on SUPERAGENT:

ChangeHOBBY
Save the policy it already has (a volcano-config.yaml apply, for example)Allowed — nothing changes
Park the list (disabled), leaving the domains as they areAllowed
Clear the list ([])Allowed
Add or replace a domain, parked or not403, nothing is saved
Switch to signup_and_signin, or turn a parked list back on403

The rule behind the table: a HOBBY project may move toward no restriction, never away from it. That keeps a downgrade from stranding a project behind a list it cannot clear, while still requiring SUPERAGENT to configure one. A parked list counts as the allowlist for this — nothing but a mode change and an upgrade stand between it and enforcement — so the only edit HOBBY can make to the domains is dropping them.

Troubleshooting

Signup returns 403 email domain is not allowed for this project The address is outside the list. Add its domain, or clear the list.

Sign-in returns 403 email domain is not allowed for this project The project is in signup_and_signin and the account's domain is not listed. Check the account's canonical email (GET /auth/user) rather than the address you signed in with — they differ when another identity is primary. Add the domain, or switch the mode back to signup to let existing accounts in.

Signup returns 403 for an @anonymous.volcano.internal address That domain is reserved for anonymous accounts the platform creates. No project can accept it, and adding it to the allowlist has no effect.

Users were signed out after a config change Expected under signup_and_signin: saving the policy revokes the sessions of every account outside the list.

A subdomain is rejected even though the parent domain is listed Expected — add the subdomain as its own entry.

Saving returns 400 invalid allowed_email_domains entry An entry is not a bare domain. Use domain1.com, not user@domain1.com, https://domain1.com, or localhost.

Saving returns 400 invalid allowed_email_domains_mode The mode must be disabled, signup, or signup_and_signin.

Saving returns 403 the email domain allowlist is only available on the PRO plan The project is on HOBBY. Upgrade to SUPERAGENT to set or widen the list. A project that was on SUPERAGENT keeps the list it saved, unenforced, and can still remove it.

An outside domain signed up even though the list is set Check the plan. On anything but SUPERAGENT the list is parked: it stays in the config and stops being enforced. Upgrade to put it back in force.

On this page