Edit Page

Configuration

RESTHeart

All restheart-accounts settings live in restheart.yml across three top-level keys: accountsConfig, oauthConfig, and emails. Every section is independent; omitting a section disables the corresponding feature.

Full configuration reference

accountsConfig

accountsConfig:
  # Since 9.7.0 the database and collection holding users, teams, invitations
  # and oauth_codes are sourced from mongoRealmAuthenticator (users-db and
  # users-collection), so accounts writes users where the authenticator reads
  # them. There is no accountsConfig key for either.

  # Application display name β€” used in email subjects and bodies.
  app-name: "My App"

  # JWT access token time-to-live in minutes.
  # The key and issuer are sourced automatically from jwtConfigProvider.
  jwt-ttl: 15

  # Domain used for the JWT authentication cookie (Set-Cookie: Domain=...).
  cookie-domain: dev.example.com

  # Whether the auth cookie carries the Secure attribute (HTTPS only). Since 9.6.0.
  # Default true; set false only for local HTTP development.
  cookie-secure: true

  # Name of the JWT authentication cookie.
  # Default: "rh_auth".
  cookie-name: rh_auth

  # Public URL of the frontend β€” used to build links in transactional emails.
  frontend-url: https://dev.example.com

  # URL the browser is redirected to after a successful auto-login
  # (email verification, invitation activation, password reset, OAuth callback).
  frontend-app-url: https://dev.example.com/app

  # Legal policy versions. Required only when using OAuth with consentsAccepted=true.
  # The OAuth callback reads these values to build the ConsentRecord passed to
  # MembershipProvider.activateViaOAuth(), so the deployment layer knows which
  # version the user accepted. Omit if you do not track consent versions via OAuth.
  # See: Consents Management.
  terms-version: "1.0"
  privacy-version: "1.0"

  # Default BCP 47 locale for email templates (used when the user document
  # has no "locale" field). Falls back to "en" if omitted.
  default-locale: en

  # Paths to custom email template HTML files (relative to the RESTHeart
  # working directory). Omit any key to use the built-in template.
  # See: Email Templates guide.
  templates:
    verification:   etc/email-templates/verification.html
    password-reset: etc/email-templates/password-reset.html
    invite:         etc/email-templates/invite.html

  # ── Membership SPI (9.4.1+) ──────────────────────────────────────────────

  # JWT claim name used to carry the active team identifier.
  # Default: "team".
  # See: Custom Membership Providers.
  team-claim-name: team

  # Role name assigned to regular (non-admin) team members.
  # Default: "member". Set to "user" if existing ACL rules use that label.
  member-role-name: member

  # System ACL role assigned to users after email verification or OAuth login.
  # New unverified users get roles: ["$unauthenticated"] β€” they can only access
  # public endpoints until verification assigns the real role.
  # Default: "user". Override per-team via override-accounts-default-role.
  default-role: user

  # Team role assigned to the user who creates a new team.
  # Stored in user.teams[].role and team.members[].role.
  # Default: "owner". Override per-team via override-accounts-ownership-role.
  ownership-role: owner

  # Set to false to disable the built-in membership management endpoints:
  #   POST /auth/invite, POST /auth/resend-invite,
  #   GET  /auth/teams, POST /auth/teams, POST /auth/switch-team,
  #   GET  /auth/team/members, PATCH /auth/team, DELETE /auth/team
  # Default: true
  membership-endpoints-enabled: true

  # Additional JWT claims propagated from request attached-parameters to tokens
  # issued by accounts endpoints (verify, activate, reset-password, switch-team, OAuth).
  # Mirrors jwtTokenManager.account-properties-claims: use the same list so that
  # all token-issuance paths produce identical JWTs.
  # authDb is always included automatically (no need to list it here).
  account-properties-claims:
  # - srvNode  # example: attached-param set by SrvNodeEnricher in multi-team deployments

oauthConfig

Omit this section entirely to disable OAuth 2.0 social login.

oauthConfig:
  # Master switch. Set to false to disable all OAuth endpoints while keeping
  # the configuration in place.
  enabled: true

  # Base URL of the RESTHeart API instance. Each provider's callback URL is
  # constructed as: {api-base-url}/auth/oauth/callback/{provider}
  api-base-url: https://api.example.com

  # Browser is redirected here after a successful login.
  frontend-success-url: https://app.example.com/app

  # Browser is redirected here when the OAuth flow fails (e.g. user denied
  # consent, invalid state).
  frontend-error-url: https://app.example.com/login?error=oauth_error

  providers:
    google:
      enabled: true
      client-id:     "123….apps.googleusercontent.com"
      client-secret: "GOCSPX-…"
      scope:         "openid email profile"   # optional β€” default for google

    github:
      enabled: true
      client-id:     "Iv1.…"
      client-secret: "…"
      scope:         "user:email"             # optional β€” default for github

    # Custom provider β€” requires a matching OAuthProvider plugin in plugins/
    # myapp:
    #   enabled: true
    #   client-id: "…"
    #   client-secret: "…"
    #   scope: "read:profile"

emails

The emails plugin (from the restheart-emails module) provides SMTP email sending for transactional emails (registration verification, team invitations, password reset).

See the Emails Plugin documentation for full configuration reference and usage.

emails:
  enabled: true
  # Display name used in the email "From" header.
  app-name: "My App"
  # From email address.
  sender-email: noreply@example.com
  # SMTP connection settings.
  smtp-hostname: email-smtp.eu-central-1.amazonaws.com
  smtp-port: 465            # 465 = SMTPS (implicit TLS); 587 = STARTTLS
  smtp-username: AKIAxxxxxxxx
  smtp-password: secret

JWT token integration

The JWT access tokens issued by restheart-accounts (after login, verification, activation, password reset, and OAuth callback) are verified by RESTHeart’s own jwtAuthenticationMechanism.

Since RESTHeart v9.5, restheart-accounts reads the JWT signing key and issuer directly from jwtConfigProvider β€” there is no separate jwt-key or jwt-issuer in accountsConfig. This eliminates the previous misconfiguration risk where the two keys could diverge, causing 401 Unauthorized errors on auto-login flows.

The only JWT-related setting remaining in accountsConfig is jwt-ttl, which controls how long the auto-login tokens last (independently from the main jwtTokenManager TTL):

# restheart.yml β€” single source of truth for the JWT key
jwtConfigProvider:
  key: your-secret   # used by jwtTokenManager, jwtAuthenticationMechanism
                     # AND restheart-accounts (automatically)
  issuer: example.com

accountsConfig:
  jwt-ttl: 15        # auto-login token TTL in minutes (accounts-specific)
Note
If jwt-key or jwt-issuer are still present in your accountsConfig block (from a pre-9.5 configuration), RESTHeart will log a warning and ignore them.

Propagating attached-parameters as JWT claims

By default the tokens issued by accounts endpoints (/auth/verify, /auth/activate, /auth/reset-password, /auth/switch-team, OAuth callbacks) include:

  • sub β€” the user’s email

  • roles β€” the user’s roles

  • authDb β€” the MongoDB authentication database (always included automatically; used by JwtAuthDbVerifier in multi-team deployments)

  • team / status (or whichever explicit claims each flow adds)

In multi-team or custom deployments, interceptors such as SrvNodeEnricher attach additional parameters to the request before it reaches the service. To propagate those parameters as JWT claims β€” so that the resulting token matches what POST /token would issue β€” list them in account-properties-claims:

accountsConfig:
  # Must mirror jwtTokenManager.account-properties-claims so all issuance paths
  # produce identical JWTs.
  account-properties-claims:
    - srvNode   # set by SrvNodeEnricher; required by JwtAuthDbVerifier
jwtTokenManager:
  account-properties-claims:
    - srvNode   # keep in sync with accountsConfig.account-properties-claims

authDb is always included automatically from the effective override-users-db value and never needs to appear in the list.

Tip
Keep accountsConfig.account-properties-claims and jwtTokenManager.account-properties-claims identical. Any discrepancy means /token and accounts endpoints will issue JWTs with different claims, which can cause subtle authentication failures.

Since 9.7.0, account-properties-claims can also be overridden per team via override-accounts-account-properties-claims (see Multi-tenancy) β€” useful when different tenants want different token payloads (e.g. one wants consents for ACL predicates, another wants a plan identifier, another neither). Whichever list is in effect, a fixed denylist is enforced at token issuance and cannot be bypassed by the override β€” see JWT claims denylist.

Multi-tenancy with AuthDbResolver and TeamConfigInterceptor

To serve multiple teams from a single restheart-accounts deployment, configure two interceptors at REQUEST_BEFORE_EXCHANGE_INIT:

  • AuthDbResolver β€” maps the incoming hostname to override-users-db and override-cookie-domain.

  • TeamConfigInterceptor β€” reads a confs/{srvId}.accounts document from MongoDB and attaches per-team app name, frontend URLs, email templates, and OAuth credentials. Since 9.6.0 the OAuth override surface covers all of oauthConfig, not just provider credentials β€” see Multi-tenant / per-team overrides for the OAuth-specific details, in particular override-accounts-oauth-api-base-url, which a multi-tenant deployment should derive from the request’s own hostname rather than store, since every team is served on a different one.

Neither interceptor is bundled with restheart-accounts; implement them in your deployment layer. restheart-accounts automatically detects and uses all override attributes when present.

Table 1. Override attribute reference
Attribute Source Overrides

override-users-db

AuthDbResolver

the users database (also honoured by mongoRealmAuthenticator)

override-cookie-domain

AuthDbResolver

accountsConfig.cookie-domain

override-cookie-secure

AuthDbResolver

accountsConfig.cookie-secure (since 9.6.0)

override-accounts-default-role

TeamConfigInterceptor

accountsConfig.default-role

override-accounts-ownership-role

TeamConfigInterceptor

accountsConfig.ownership-role

override-accounts-app-name

TeamConfigInterceptor

accountsConfig.app-name

override-accounts-frontend-url

TeamConfigInterceptor

accountsConfig.frontend-url

override-accounts-frontend-app-url

TeamConfigInterceptor

accountsConfig.frontend-app-url

override-accounts-tmpl-verification

TeamConfigInterceptor

email template (inline HTML)

override-accounts-tmpl-password-reset

TeamConfigInterceptor

email template (inline HTML)

override-accounts-tmpl-invite

TeamConfigInterceptor

email template (inline HTML)

override-accounts-oauth-api-base-url

TeamConfigInterceptor

oauthConfig.api-base-url (since 9.6.0)

override-accounts-oauth-frontend-success-url

TeamConfigInterceptor

oauthConfig.frontend-success-url (since 9.6.0)

override-accounts-oauth-frontend-error-url

TeamConfigInterceptor

oauthConfig.frontend-error-url (since 9.6.0)

override-accounts-oauth-{provider}-enabled

TeamConfigInterceptor

oauthConfig.providers.{provider}.enabled (since 9.6.0; was google-only before)

override-accounts-oauth-{provider}-client-id

TeamConfigInterceptor

oauthConfig.providers.{provider}.client-id (since 9.6.0; was google-only before)

override-accounts-oauth-{provider}-client-secret

TeamConfigInterceptor

oauthConfig.providers.{provider}.client-secret (since 9.6.0; was google-only before)

override-accounts-oauth-{provider}-scope

TeamConfigInterceptor

oauthConfig.providers.{provider}.scope (since 9.6.0)

override-accounts-users-unrestricted-roles

TeamConfigInterceptor

accountsConfig.users-unrestricted-roles (since 9.6.0)

override-accounts-signup-mgmt-enabled

TeamConfigInterceptor

whether Sign-up Management is enabled for this team (since 9.6.0)

override-accounts-terms-version

TeamConfigInterceptor

accountsConfig.terms-version (since 9.7.0)

override-accounts-privacy-version

TeamConfigInterceptor

accountsConfig.privacy-version (since 9.7.0)

override-accounts-account-properties-claims

TeamConfigInterceptor

accountsConfig.account-properties-claims (since 9.7.0) β€” see JWT claims denylist for what a tenant cannot override

Note
override-accounts-users-unrestricted-roles and override-accounts-signup-mgmt-enabled both control the /users self-service write restriction on a per-team basis β€” the former exempts specific roles, the latter turns the restriction off entirely for a team that never opted into Sign-up Management. See that section for the full explanation of what each one does and why.
Note
{provider} is any provider name registered with OAuthService β€” a built-in one (google, github) or a custom OAuthProvider plugin. The override key format is the same regardless of provider; see Multi-tenant / per-team overrides for the full OAuth override reference, including scope semantics and why api-base-url should be derived per-request rather than stored.

confs/{srvId}.accounts document schema

The TeamConfigInterceptor reads an accounts sub-document from the existing service-configuration document (e.g. cloud.confs for free/shared nodes, restheart.confs for dedicated nodes). All fields are optional; absent fields fall back to the node-level static configuration.

{
  "_id": "ea820b",
  "accounts": {
    "app-name":         "Customer App",
    "frontend-url":     "https://app.customer.com",
    "frontend-app-url": "https://app.customer.com/app",
    "terms-version":    "2.0",
    "privacy-version":  "2.0",
    "account-properties-claims": ["consents", "plan"],
    "templates": {
      "verification":   "<html>…full HTML string…</html>",
      "password-reset": "<html>…</html>",
      "invite":         "<html>…</html>"
    },
    "oauth": {
      "google": {
        "enabled":       true,
        "client-id":     "123….apps.googleusercontent.com",
        "client-secret": "GOCSPX-…",
        "scope":         "openid email profile"
      }
    }
  }
}

Inline HTML templates stored in MongoDB follow the same format as file-based templates (subject in <title>, i18n via <span lang>, {{variable}} placeholders). See Email Templates for the full format reference.

Note
oauth.{provider} accepts any provider name (google, github, or a custom OAuthProvider plugin), with the same enabled / client-id / client-secret / scope shape.
Important
oauthConfig.api-base-url and the frontend-success-url / frontend-error-url pair are deliberately not part of this document. On a multi-tenant node every team is served on a different hostname, so TeamConfigInterceptor should derive override-accounts-oauth-api-base-url from the incoming request (scheme + host) rather than read it from a stored value β€” a stored value could not be correct for more than one team. The frontend URLs are typically derived from the team’s own frontend-app-url (already in this document) instead of stored separately, unless a team needs a distinct post-OAuth destination. See Multi-tenant / per-team overrides.
Tip

AuthDbResolver and TeamConfigInterceptor are both disabled by default. Enable them only on deployments that need multi-tenancy. In single-team deployments the plugin uses the static accountsConfig values exclusively.

Secrets management (RHO)

Avoid hardcoding secrets in restheart.yml. The JWT key belongs to jwtConfigProvider (not accountsConfig) and is already documented in the JWT configuration section. Use RESTHeart’s built-in environment variable substitution to inject sensitive values at runtime:

oauthConfig:
  providers:
    google:
      client-secret: $(GOOGLE_CLIENT_SECRET)
    github:
      client-secret: $(GITHUB_CLIENT_SECRET)

emails:
  smtp-password: $(SMTP_PASSWORD)

Set the corresponding environment variables before starting RESTHeart.

ACL requirements

Each public-facing service registers its own ACL allow rule automatically on startup via the ACLRegistry. No manual ACL entry is needed for these paths. If a service is disabled (or membership-endpoints-enabled: false), its path is not opened.

Path Method Registered by

/auth/register

POST

registerService

/auth/verify

GET

emailVerificationService

/auth/forgot-password

POST

forgotPasswordService

/auth/reset-password

PATCH

resetPasswordService

/auth/activate

PATCH

activateService

/auth/invite †

POST

inviteService

/auth/resend-invite †

POST

resendInviteService

/auth/teams †

GET, POST

getTeamsService

/auth/switch-team †

POST

switchTeamService

/auth/oauth/authorize/{provider}

GET

oauthInitiator

/auth/oauth/callback/{provider}

GET

oauthCallback

/auth/remove-member †

DELETE

removeMemberService

/auth/accept-invite †

POST

acceptInviteService

/auth/member-role †

PATCH

updateMemberRoleService

/auth/invitation †

GET

invitationInfoService

/auth/team/members †

GET

listTeamMembersService

/auth/team †

PATCH, DELETE

teamService

/auth/profile

PATCH

updateProfileService

/auth/change-password

PATCH

changePasswordService

† Disabled when membership-endpoints-enabled: false.

The invite management endpoints are not public. Add the following rules to your ACL (file-based or MongoDB-based) to restrict them to owner role:

# invite and resend-invite require owner or admin
- roles: [owner]
  predicate: path('/auth/invite') and method(POST)
  priority: 10

- roles: [owner]
  predicate: path('/auth/resend-invite') and method(POST)
  priority: 10

Minimal configuration (email + password only)

The smallest working setup β€” no OAuth social login, no custom templates:

accountsConfig:
  db: myapp
  app-name: "My App"
  jwt-ttl: 15
  cookie-domain: example.com
  frontend-url: https://example.com
  frontend-app-url: https://example.com/app

emails:
  enabled: true
  app-name: "My App"
  sender-email: noreply@example.com
  smtp-hostname: email-smtp.eu-central-1.amazonaws.com
  smtp-port: 465
  smtp-username: $(SMTP_USERNAME)
  smtp-password: $(SMTP_PASSWORD)
Note
The JWT key and issuer are inherited from jwtConfigProvider and do not need to appear in accountsConfig.