Stalwart 0.16 JMAP impersonation auth test results (single-string username attempts)

We are sharing our test results for JMAP impersonation login on Stalwart 0.16.

We have already granted impersonation capability to the master account, and we are testing username patterns where the login is treated as a single HTTP Basic Auth username string.

Setup

  • Stalwart version: 0.16

  • Endpoint tested: GET /.well-known/jmap

  • Auth method: HTTP Basic

Accounts:

  • master: {master}

  • impersonated: {impersonate}

Password used:

  • We used the app password of the master account for all impersonation login attempts.

CLI behavior in our code

Our CLI:

  • Builds Basic auth as base64(username)

  • Calls /.well-known/jmap

  • On success, uses the returned apiUrl for subsequent JMAP calls (Email/query, Email/get)

Username strings tested

We passed each of the following as the full --user value (single string):

  • {master}%{impersonate}

  • {impersonate}%{master}

  • {master}*{impersonate}

  • {impersonate}*{master}

  • {master}%{impersonate_localpart}

  • {master}*{impersonate_localpart}

Result

All impersonation-format attempts returned:

  • 401 Unauthorized

  • response detail: “You have to authenticate first.”

Control checks

  • Normal login with {master} succeeds against the same endpoint/environment.

  • The same endpoint and identical CLI code path are used for both the control and impersonation-format tests.

We’re posting these results to ask what the correct impersonation login flow is for Stalwart v0.16, and whether a different authentication format or configuration is required beyond assigning impersonation roles

Let me look up the current JMAP impersonation authentication documentation.

The key issue is the order of the username components in the composite login string. The correct format is:

{impersonate}%{master}

i.e. target first, impersonator second, separated by %.

From the docs (emphasis mine):

Any account that holds the impersonate permission can sign in on behalf of another account by supplying a composite login of the form <target>%<impersonator> together with the impersonator’s password.

So for your setup the Basic Auth username string must be:

[email protected]%[email protected]

with master’s password as the password. Your tests used {master}%{impersonate} (impersonator first), which is the reverse of what Stalwart expects — hence the 401s.

A few additional checks:

  • Use % as the separator (not * or \*). The * variants you tested are not a documented format.
  • Use the full email addresses for both accounts if your deployment uses user@domain logins.
  • Confirm the master account’s role/permission actually includes impersonate (visible in the WebUI under the account’s permissions).

Reference: Administrators & Impersonation docs

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.

Thanks for the clarification. I already tested the <target>%<impersonator> format as well, using the impersonator account’s password, but the server still returns 401 Unauthorized.

For example, I tested:

[email protected]%[email protected]

with the master account password, and authentication still failed.

At this point I’m wondering if there may be an additional configuration requirement beyond granting the impersonate permission in the WebUI. Is there any server-level setting, authentication backend limitation, or JMAP-specific restriction that also needs to be enabled for impersonation logins to work?

For reference, regular login works correctly for both accounts individually — only the impersonation login fails.

The source code reveals the exact reason your impersonation attempt is failing: you are using an app password for the master account’s credential, which is explicitly blocked.

From the server source (line 150 in authentication.rs):

"App passwords cannot be used for impersonation"

The impersonation flow only works when the password supplied is the master account’s main account password, not an app password. The code checks whether the submitted secret matches the AppPassword format, and if it does and the login string is a composite (target%master), it immediately returns a 401 with that reason.

How to fix it:

  1. In the WebUI, go to the master account’s profile and locate its main (primary) password — not any app password listed under secondary credentials.
  2. Use that primary password as the Basic Auth password in your impersonation request.
  3. The username string should remain [email protected]%[email protected] (target first, impersonator second, % separator).

Quick checklist:

  • :white_check_mark: Username format: <target>%<impersonator> (full email addresses if user@domain style)
  • :white_check_mark: Password: master account’s primary password (not an app password)
  • :white_check_mark: The master account has the impersonate permission (verify in WebUI under the account’s roles/permissions)
  • :cross_mark: * separator — not a supported format, only % works

Reference: Administrators & Impersonation docs

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.

It worked. Thank you.

Glad it’s working! To summarize the two key points for anyone finding this thread later:

  1. App passwords cannot be used for impersonation — you must use the master account’s primary password.
  2. Username order matters — the correct format is <target>%<impersonator> (target account first, impersonating account second).

Reference: Administrators & Impersonation docs

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.