processone / processone/ejabberd
API permissions
@badlop is already working on this.
Since Sep 18, 2026.
- Dominant language
- Erlang
- Stars
- 6.7k
- Forks
- 1.6k
- PR merge metrics
- No merged PRs in 30d
Description
Hello,
I am working with ejabberd 26.07.7 and have encountered an authorization issue involving a custom API command, policy = user, and api_permissions.
I would like to confirm whether the behavior described below is intentional and, if so, what the officially supported configuration is for this use case.
- Custom command
I have developed a small custom module exposing the following ejabberd command:
#ejabberd_commands{
name = get_account_created_at,
tags = [accounts],
desc = "Get XMPP account creation timestamp",
longdesc =
"Returns the creation timestamp of an XMPP account. "
"The authenticated caller can only query an account "
"belonging to the same XMPP host.",
module = ?MODULE,
function = get_account_created_at,
policy = user,
args = [
{target_user, binary},
{target_host, binary}
],
args_desc = [
"Target username",
"Target XMPP host"
],
result = {created_at, string}
}.
The implementation is deliberately restricted to the caller's own XMPP host:
get_account_created_at(
_CallerUser,
CallerHost,
TargetUser,
TargetHost
) ->
case CallerHost =:= TargetHost of
true ->
query_created_at(TargetUser, TargetHost);
false ->
{error, <<"forbidden">>}
end.
- API authorization
The command is exposed through mod_http_api.
The relevant api_permissions configuration is:
api_permissions:
"account_creation_api":
from:
- mod_http_api
who:
acl: account_creation_api_user
what:
- "get_account_created_at"
acl:
account_creation_api_user:
user: "authorized_m_instance@totototo.tld"
The account is intentionally a normal XMPP account:
authorized_m_instance@totototo.tld
It is not intended to have administrative privileges.
- Access configuration
The server currently has:
access_rules:
configure:
- allow: admin
- allow: hostadmin
- allow: loopback
- deny: all
and:
mod_configure:
access: configure
mod_http_api:
default_version: 2
- Observed behavior
When authorized_m_instance@totototo.tld is neither admin nor hostadmin, the authenticated HTTP request returns:
HTTP/1.1 403 Forbidden
{"code":32,"message":"AccessRules: Account does not have the right to perform the operation.","status":"error"}
However, if I add exactly the same account to the hostadmin group, the same HTTP request succeeds and returns:
HTTP/1.1 200 OK
"2026-08-01 13:50:22.84842"
Making the account an administrator also makes the request succeed.
The command itself works correctly when executed through ejabberdctl, so the problem is specifically related to authorization when the command is invoked through the HTTP API as a normal authenticated user.
- Source-code/history investigation
While investigating the ejabberd source code, I found two relevant commits in the ejabberd history.
The first is:
16f4c90 — "When command has host argument, check account is admin on that host"
The commit modifies ejabberd_access_permissions.erl and introduces a can_access/4 path which extracts a host argument and checks it using the configure access rule:
acl:match_rule(
HostArg,
configure,
jid:make(USR)
)
The commit also changes command execution so that this extended permission check receives the command arguments.
The second relevant commit is:
62957e6 — "When policy=user, add arguments user,host during command registration"
This adds the user and host arguments to commands declared with:
policy = user
This appears particularly relevant because the command in my module is declared with policy = user.
The resulting interaction appears to be:
policy = user
|
+--> implicit user argument
|
+--> implicit host argument
|
v
host/configure permission check
|
+----------+----------+
| |
admin/hostadmin normal user
| |
permitted denied
This would explain the observed behavior:
normal authenticated user → HTTP 403
hostadmin → HTTP 200
admin → HTTP 200
I may be misunderstanding the intended interaction between these mechanisms, so I would appreciate confirmation from the ejabberd developers.
- Security requirement
The intended security model is very strict.
The account:
authorized_m_instance@totototo.tld
should:
remain a normal XMPP account;
not be an admin;
not be a hostadmin;
not receive general configure privileges;
be authorized only for get_account_created_at;
be unable to execute other administrative API commands.
The command itself additionally verifies that:
CallerHost == TargetHost
The purpose of api_permissions is specifically to restrict this account to this single command.
I therefore do not want to solve the problem by granting hostadmin or configure privileges to this account.
- Question
According to the ejabberd documentation, policy = user is the mechanism used to make a command available to a server user through the REST/XML-RPC API, while api_permissions controls which authenticated users may execute particular API commands.
Could you please clarify the following:
Is it intentional that a policy = user command containing the implicit host argument requires the authenticated user to have configure access for that host?
If this is intentional, what is the officially supported way to expose a custom policy = user command through mod_http_api to a normal non-administrative XMPP account?
Is there another command policy or command definition that should be used when the desired authorization is exclusively controlled by api_permissions?
If api_permissions is intended to authorize the specific command, should the configure check introduced by the host argument occur before or after the api_permissions authorization?
If there is currently no supported way to implement this security model, should this behavior be considered a limitation or a bug in the interaction between policy = user, the implicit host argument, and api_permissions?
I am specifically looking for a solution that does not require:
making the API account an administrator;
making it a host administrator;
granting it general configure privileges;
modifying the ejabberd core source code.
The goal is simply to allow one authenticated XMPP account to execute one specifically authorized custom HTTP API command.
Environment:
ejabberd: 26.07.7
mod_http_api: enabled
API version: 2
Custom module: mod_account_created_at
Schema: singlehost
Thank you for clarifying the intended authorization model and the recommended supported solution for this use case.
Best regards,
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.