Identity environments

Your app runs in more than one place: on a laptop, in CI, on a staging server and in production. An identity environment gives each of those places its own directory of app users. A person who signs in to your local build is a different user from the same person in production, and a key made for development cannot sign anyone in to production.

Each environment has its own:

  • Users, with their sessions and linked social accounts. An email address is unique within an environment, so the same address can exist in two environments as two separate users.

  • Keys. A key with identity:auth belongs to one environment.

  • Web origins and redirect URIs, where a browser may call Mocco from and where social sign-in may return to.

  • Social provider apps, the client IDs and secrets for Google, Apple and the others.

  • Sign-in access, the rule for who may sign in.

  • Token audience, the aud claim of the access tokens it issues.

The issuer, the signing key and the JWKS are shared by every environment of a project, and so are the provider callback URLs.

The two defaults

A project gets two environments the first time it needs one, for example when you open the App users page or create a key with identity:auth:

EnvironmentWho may sign inAudienceWeb origins and redirect URIsUse it for
productionAnyoneThe project IDCopied from the project's apps when it was createdYour real users
developmentWorkspace membersIts own IDNoneLocal development and other work by your team

production is the project's first environment. Keys, console actions and agent tools that don't name an environment use it. Its audience is the project ID, so verifiers that expected the project ID before environments existed keep working.

If your project used sign-in before environments existed, it has only production, which holds your existing users. Add a development environment yourself, as below.

Add an environment

Add one environment for each place your app runs that should have users of its own, such as staging or ci. A project can have up to ten.

In the console, open the project's App users page, then use Add environment next to the environment switcher. Give it a key (lower-case letters, digits and hyphens, up to 32 characters) and a name. The key can't be changed later. A new environment has its own audience and only workspace members can sign in to it; set its web origins and redirect URIs on its Environment settings tab.

An agent can do the same over MCP:

  • mocco_identity_environments_search lists a project's environments with their IDs, audiences and settings, and the provider callback URLs.

  • mocco_identity_environments_create adds one, with a key, a name, and optionally webOrigins and redirectUris.

  • mocco_identity_environments_update changes the name, origins, redirect URIs, sign-in access, allowed emails and test addresses.

  • mocco_identity_environments_remove removes one.

Changes need the identity:write scope, the workspace's agent opt-in and a confirmed round trip. Any workspace member can read environments; owners and administrators can change them.

The Overview tab shows every environment's settings side by side and marks the ones that differ, so you can check that staging matches production where it should.

Removing an environment deletes its users, their sessions and linked accounts, and its provider apps. It can't be undone. Revoke the keys bound to it first; the project's only environment can't be removed.

Get a key for an environment

A key's environment is chosen when the key is created and doesn't change. To move an app to another environment, create a new key.

  • Console: on the API keys page, tick identity:auth. An Identity environment select appears; choose the environment the key signs people in to.

  • MCP: call mocco_api_keys_create with identityEnvironmentId set to an ID from mocco_identity_environments_search. Without it, the key belongs to the project's first environment.

Use a publishable key (mk_pub_…) in a web or React Native app. See API keys for the kinds of key and where each one goes.

Web origins and redirect URIs

A publishable key works from a browser only on its project's web origins or its environment's web origins. Social sign-in may return only to an exact redirect URI of the environment, or to a URL on one of its web origins. See where sign-in may return for the rules.

Put each address on the environment that uses it:

  • production: https://app.example.com, myapp://auth/callback

  • staging: https://staging.example.com

  • development: http://localhost:5173, myapp-dev://auth/callback

Add localhost and other loopback addresses only to development (or another environment that real users never reach). production was seeded from your project's apps, so check that it doesn't carry a local address from before.

Social providers

Each environment has its own provider apps. Set them on the Sign-in methods tab with the environment you want selected in the switcher, or with mocco_identity_providers_set and an environmentId.

We recommend a separate OAuth app at each provider for each environment, such as "Example (development)" and "Example (production)". A development app can then stay in the provider's testing mode, and a leaked development secret doesn't touch production.

Every environment uses the same callback URL, so register the same address with each provider app:

<IDENTITY_ISSUER>/api/ext/v1/identity/oauth/<provider>/callback

The Environment settings tab lists the callback URL for each provider. Mocco knows which environment a callback belongs to from the sign-in it started, so the shared URL never mixes environments.

If you import users who signed in with social accounts, configure the environment they go into with the provider apps your current system uses. A provider gives a person a different ID in each app. See move existing users in.

Sign-in access

Each environment says who may sign in. Choose it under Who can sign in on the Environment settings tab, or with signInAccess in mocco_identity_environments_update.

SettingValueWho may sign in
AnyoneopenAnyone with an email address or a social account
Workspace membersmembersAn address equal to the Mocco account email of an accepted member of the workspace
Listed addressesallowlistAn address under Allowed emails, or any address at a listed @domain (not its subdomains)

Addresses are compared without case. With social sign-in, the rule is checked against the email the provider has verified; under members or allowlist, a provider account without a verified email is refused.

The rule is checked at sign-in. Someone who is already signed in keeps refreshing their session after you tighten the rule; revoke their sessions on the Users tab to sign them out now.

A staging environment that your QA team or a client should reach usually works best as allowlist with @example.com.

Test addresses for end-to-end tests

End-to-end tests can't read an inbox. With Test addresses turned on, an address whose local part ends in +mocco_test signs in with the fixed code 424242 and gets no email. It skips the sign-in access rule, so it works even where only workspace members may sign in.

Test addresses only work in an environment where sign-in isn't open. Because the code is public, Mocco refuses to turn test addresses on while sign-in is Anyone, and refuses to open sign-in while they are on. Never use test addresses in production.

Turn them on in the environment's Environment settings tab, or with testAddresses: true in mocco_identity_environments_update. Then sign in from a test the same way a person would:

import { createIdentityClient } from '@mocco/js/identity';

// `storage` keeps the session, as in the social sign-in guide; an in-memory Map is enough in a test.
const identity = createIdentityClient({ publishableKey: process.env.MOCCO_KEY!, storage });

const { challengeId } = await identity.emailStart('someone+mocco_test@example.com');
const session = await identity.emailVerify(challengeId, '424242');

Or with plain HTTP:

curl -s https://api.mocco.club/v1/identity/email/start \
  -H "X-Mocco-Key: $MOCCO_KEY" -H 'Content-Type: application/json' \
  -d '{"email":"someone+mocco_test@example.com"}'
# → {"challengeId":"…"}

curl -s https://api.mocco.club/v1/identity/email/verify \
  -H "X-Mocco-Key: $MOCCO_KEY" -H 'Content-Type: application/json' \
  -d '{"challengeId":"…","code":"424242"}'

In a browser test, type the test address and 424242 into your app's own sign-in form.

The usual limits still apply: the code expires after ten minutes, a wrong code counts toward the five attempts, and starting a sign-in is rate-limited. Use a different test address per test run (for example run-1042+mocco_test@example.com) when tests run in parallel. Test addresses apply to email codes only; social sign-in always follows the access rule. Once the setting is off, 424242 signs no one in.

Token audience

Access tokens carry the environment's audience as aud, and a token from one environment fails verification in another. Verify each token with the audience of the environment your key belongs to:

  • Read it from /config. GET /v1/identity/config with the environment's key returns its audience and environmentKey. A backend can compare them with the values it expects before it enables sign-in.

  • Pass it to the SDK. createIdentityVerifier({ projectId, audience }) in Node, or VerifierConfig { audience: Some(…), … } in Rust. See verify identity tokens.

production keeps the project ID as its audience, which is also the verifiers' default, so a production backend needs no change. Every other environment's audience is its own ID. Keep it in the same configuration as the key, for example MOCCO_IDENTITY_AUDIENCE next to MOCCO_KEY.

Local development setup

  1. Open App users, switch to development, and on Environment settings add your dev server's origin under Web origins, for example http://localhost:5173. Add a redirect URI such as myapp-dev://auth/callback if a native build uses social sign-in.

  2. On API keys, create a publishable key with identity:auth and choose development under Identity environment.

  3. Put the key and the audience in a git-ignored .env file. Read the audience from /config or from the environment's settings.

    # .env (listed in .gitignore)
    MOCCO_KEY=mk_pub_…
    MOCCO_IDENTITY_AUDIENCE=…
  4. Start your app and sign in as yourself, with the email address of your Mocco account: development admits workspace members. Teammates who aren't members can be added under Listed addresses instead.

  5. To sign in without an inbox, turn on Test addresses and use you+mocco_test@example.com with the code 424242.

Production deploys use a different key from the production environment, set in your hosting provider's secrets. The code stays the same; only the key and the audience differ.

Troubleshooting

What you seeWhyWhat to do
403 insufficient_scopeThe key doesn't hold identity:authCreate a key with identity:auth and choose its environment
403 identity_environment_requiredThe key isn't bound to an identity environment, so there is no directory to sign anyone in toCreate a new key with identity:auth, choose its environment, and revoke the old key
403 sign_in_not_allowed, or error=sign_in_not_allowed on your social redirectThe environment's sign-in access rule doesn't admit the addressUse a member's account email, add the address or its domain under Allowed emails, or use a test address where they are on. Under members or allowlist, a social account needs a verified email
403 origin_not_allowedThe page's origin isn't on the project's web origins or the key's environment'sAdd the origin to the environment that the key belongs to. A key of another environment is refused from that environment's origins
400 invalid_redirect_uriThe social redirect isn't on the environment's redirect URIs or web originsAdd it to the key's environment
The verifier rejects every token (Node authenticate returns null, Rust returns InvalidToken)The verifier expects another environment's audience, often the project ID while the key belongs to developmentPass the audience from /config for that key
424242 is refusedTest addresses are off, the address doesn't end in +mocco_test, or the challenge expired or ran out of attemptsCheck the environment's Test addresses setting and start a new sign-in
Turning on test addresses is refusedSign-in is AnyoneChoose Workspace members or Listed addresses first