commit ac681f0e44fd68a7977c72bf033c237d510acb45
parent f98568776d9e6d0f1515ff865ef8653096df3bec
Author: Florian Dold <dold@taler.net>
Date: Sat, 29 Aug 2026 18:49:01 +0200
merchant authentication: document password reauthentication
Diffstat:
4 files changed, 52 insertions(+), 13 deletions(-)
diff --git a/core/merchant/post-instances-INSTANCE-forgot-password.rst b/core/merchant/post-instances-INSTANCE-forgot-password.rst
@@ -1,10 +1,14 @@
.. http:post:: /instances/$INSTANCE/forgot-password
- Same as ``/management/instances/$INSTANCE/private/auth`` it will update the password of the instance but
- without requiring the current password. On the first call it will validate the request
- and return the multi-factor authentication challenge IDs with
- a ``202 Accepted`` response. Once the challenges are solved the
- request needs to be repeated with the ``Taler-Challenge-Ids`` header.
+ Like ``[/instances/$INSTANCE]/private/auth``, this endpoint updates the
+ password of the instance, but it does not require the current password.
+ Instead, every TAN channel listed in ``MANDATORY_TAN_CHANNELS`` must be
+ solved. Thus, configurations listing both ``sms`` and ``email`` require
+ both challenges, while configurations listing only one of them require only
+ that channel. On the first call the backend validates the request and
+ returns the challenge IDs with a ``202 Accepted`` response. Once all
+ required challenges are solved, the exact request must be repeated with the
+ ``Taler-Challenge-Ids`` header.
This endpoint is **not** available for the ``admin`` instance.
The administrator must use the command-line tool if they forgot
@@ -21,8 +25,8 @@
refreshable ``spa`` access token in a `LoginTokenSuccessResponse`, as
requested with ``token_duration``. @since **v39**
:http:statuscode:`202 Accepted`:
- 2FA is required for this operation. This returns
- the `ChallengeResponse`. @since **v21**
+ TAN authentication is required for this operation. This returns the
+ `ChallengeResponse` with ``combi_and`` set to true. @since **v21**
:http:statuscode:`204 No content`:
The backend has successfully changed the credentials for the instance.
No login token was requested.
diff --git a/core/merchant/post-management-instances-INSTANCE-auth.rst b/core/merchant/post-management-instances-INSTANCE-auth.rst
@@ -9,7 +9,11 @@
**Required permission:** ``auth-write`` on the ``/private`` path,
``instances-auth-write`` on the ``/management/`` path (see :ref:`Scopes <merchant-api-scopes>`)
- **Request** the request must be an `InstanceAuthConfigurationMessage`.
+ **Request:** Requests to ``/management/instances/$INSTANCE/auth`` use an
+ `InstanceAuthConfigurationMessage`. Requests to the instance's own
+ ``/private/auth`` endpoint use an `InstanceAuthChangeRequest` and must
+ reauthenticate with the current password whenever the existing
+ authentication method is ``token``.
**Response:**
@@ -18,7 +22,9 @@
:http:statuscode:`204 No content`:
The backend has successfully changed the credentials for the instance.
:http:statuscode:`401 Unauthorized`:
- The request is unauthorized.
+ The request is unauthorized. A missing or incorrect ``old_password`` on
+ the ``/private`` path is returned with
+ ``TALER_EC_MERCHANT_PRIVATE_POST_INSTANCE_AUTH_BAD_OLD_PASSWORD``.
:http:statuscode:`403 Forbidden`:
MFA channels are not available for this instance.
Returned with ``TALER_EC_MERCHANT_GENERIC_MFA_MISSING``.
@@ -41,6 +47,17 @@
type InstanceAuthConfigurationMessage = InstanceAuthConfigToken | InstanceAuthConfigTokenOLD | InstanceAuthConfigExternal
+ .. ts:def:: InstanceAuthChangeRequest
+
+ // @since **v40**
+ type InstanceAuthChangeRequest = InstanceAuthConfigurationMessage & {
+ // Required on the instance's own /private/auth endpoint when the
+ // current authentication method is "token". It is checked by the
+ // backend against the stored password before the update is committed.
+ // Administrative changes through /management do not use this field.
+ old_password: string;
+ };
+
.. ts:def:: InstanceAuthConfigToken
// @since **v19**
diff --git a/design-documents/049-auth.rst b/design-documents/049-auth.rst
@@ -6,8 +6,8 @@ DD 49: Authentication
:DD shepherd: TBD
:Historical contributors: Florian Dold, Sebastian, Antoine A, Martin Schanzenbach, Christian Grothoff
:First published: 2023-09-06
-:Last substantive change: 2025-06-19
-:Implementation evidence: merchant (2023-09-06), libeufin (2024-11-15)
+:Last substantive change: 2026-08-29
+:Implementation evidence: merchant (2023-09-06, 2026-08-29), libeufin (2024-11-15)
:Normative references: ``core/api-merchant.rst``, ``core/api-corebank.rst``
.. note::
@@ -216,6 +216,22 @@ suffixing the requested scope with ``:refreshable``, for example
``refreshable`` field in its ``TokenRequest``. Clients must follow the
normative API of the component they use.
+Password changes and recovery
+=============================
+
+A bearer token authorizes access to the account, but it is not sufficient by
+itself to replace password authentication. A merchant changing its own
+password must reauthenticate with the current password, which the backend
+checks against its stored password hash. An administrator resetting another
+instance's authentication is exempt because the administrative credential is
+the authority for that operation.
+
+Forgotten-password recovery cannot provide the current password. The public
+merchant recovery endpoint therefore requires all channels configured in
+``MANDATORY_TAN_CHANNELS``. If both SMS and e-mail are configured, both must
+be solved; if exactly one is configured, that one challenge is sufficient. A
+merchant backend with no mandatory TAN channel refuses public recovery.
+
Definition of Done
==================
diff --git a/manpages/taler-merchant.conf.5.rst b/manpages/taler-merchant.conf.5.rst
@@ -166,8 +166,10 @@ ENABLE_SELF_PROVISIONING
Set to YES to allow unauthenticated clients to create new instances.
MANDATORY_TAN_CHANNELS
- Space-separted list of TAN channels required for password reset.
- Can be "sms" or "email" or both.
+ Space-separated list of TAN channels required for password reset.
+ Supported channels are ``sms`` and ``email``. Every channel in the list is
+ mandatory: listing both requires both challenges, while listing only one
+ requires only that challenge.
PHONE_REGEX
Posix regular expression specifying which phone numbers are acceptable for