taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

077-merchant-self-provisioning.rst (9778B)


      1 DD 77: Merchant Multi-Tenancy and Self-Provisioning
      2 ###################################################
      3 
      4 :Design status: Abandoned
      5 :Implementation status: Not started
      6 :DD shepherd: TBD
      7 :Historical contributors: Martin Schanzenbach
      8 :First published: 2025-12-11
      9 :Last substantive change: 2025-12-11
     10 
     11 .. warning::
     12 
     13    This proposal was abandoned.  The OIDC-based multi-tenancy model below was
     14    not implemented and must not be treated as the merchant authentication or
     15    provisioning contract.
     16 
     17 Summary
     18 =======
     19 
     20 A new requirement is planned that allows a self-provisioning feature of instances in the merchant backend.
     21 
     22 Motivation
     23 ==========
     24 
     25 We want to enable self-provisioning feature of instances in the merchant backend.
     26 A lot of if not most banks have an OpenID-Connect-based IdP that has all their customers already enrolled.
     27 Banks may choose to run merchant instances for their customers (Merchant-as-a-Service).
     28 In order to simplify enrollment and facilitate adoption, it should be possible to authenticate
     29 against such an IdP and allow users to create and use their own instances.
     30 
     31 This conflates self-provisioning itself with OIDC integration on purpose:
     32 Self-provisioning without OIDC integration provides little benefit to users.
     33 Only if they are enabled to re-use their accounts/credentials of the Merchant-as-a-Service provider
     34 this feature becomes useful.
     35 
     36 The limitation on OIDC is also on purpose: It is the de-facto standard for Identity Federation.
     37 
     38 
     39 Requirements
     40 ============
     41 
     42 1. Support OpenID-Connect authentication.
     43 2. Support self-provisioning of instances for users through the API/UI.
     44 3. Instances can be associated with users through the API/UI.
     45 
     46 Proposed Solution
     47 =================
     48 
     49 The proposed solution is as follows:
     50 
     51 User
     52 ----
     53 
     54 A user consists of the following properties:
     55 
     56 1. User ID
     57 2. Associated instances (may be empty)
     58 3. Password (optional)
     59 4. External IdP
     60 
     61 Instance association can also be viewed as a property of the instance (associated users) but
     62 effectively it will be its own table mapping instances to users (n*m).
     63 
     64 OIDC login:
     65 -----------
     66 
     67 (Note that the following assumes that the user logged in before).
     68 
     69 Two new endpoints must be implemented: ``/oidc-login`` and ``/oidc-callback``.
     70 The ``/oidc-login`` endpoint is used when the user clicks on *Login with OIDC* button on the login page of the
     71 Merchant Backoffice. The endpoint will redirect the user to the IdP, startin the OIDC flow.
     72 The flow will be initiated with the ``/oidc-callback`` endpoint as ``redirect_uri``, meaning that the user upon
     73 successful authentication will be redirected to ``/oidc-callback`` with an authorization code.
     74 The merchant backend will exchange the code for ID/access token, and return a cookie associating this state with the new user session.
     75 The SPA can then use the Cookie as a credential at  the existing ``/token`` endpoint to receive a native merchant access token, at which point the session cookie expires.
     76 
     77 The OIDC IdP is configured globally (not per instance) by the admin in ``merchant.conf``.
     78 
     79 Self provisioning:
     80 ------------------
     81 
     82 Once a user logs in with an external (OIDC) IdP for the first time, a new user entry is created in the merchant backend which is not associated with any instance.
     83 This user/token only has access to the self-service page of the Merchant backoffice UI.
     84 The user may create a new instance (and is immediately added as a user to the new instance as its creator).
     85 We may want to require that OIDC users have an email address (either as their external ID or as a property) and
     86 use this as our local User ID.
     87 Alternatively (or additionally), other users may add this new user to their instances.
     88 The authorization logic of the merchant backend must be modified such that any user that is not associated with an instance is not allowed to perform any operations on it.
     89 For now, all associated users have the same roles/rights and are effectively instance admins.
     90 
     91 Migration:
     92 ----------
     93 
     94 Currently, authentication is tied to the instance itself, which is protected by a password.
     95 The current design can be migrated by (automatically) creating a user for each existing instance and its password moved to the new user.
     96 The ID of the new user is then also immediately associated with the instance as a valid (admin) user.
     97 
     98 Example:
     99 
    100 ::
    101 
    102   BEGIN;
    103   
    104   -- Check patch versioning is in place.
    105   SELECT _v.register_patch('merchant-0028', NULL, NULL);
    106   
    107   SET search_path TO merchant;
    108   
    109   -------------------------- Users  ---------------------------
    110   
    111   CREATE TABLE IF NOT EXISTS merchant_users
    112     (user_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
    113     ,user_id TEXT NOT NULL UNIQUE
    114     ,auth_hash BYTEA CHECK(LENGTH(auth_hash)=64)
    115     ,auth_salt BYTEA CHECK(LENGTH(auth_salt)=32)
    116     );
    117   COMMENT ON TABLE merchant_users
    118     IS 'all the users enrolled in this backend';
    119   COMMENT ON COLUMN merchant_users.user_id
    120     IS 'identifier of the user (required)';
    121   COMMENT ON COLUMN merchant_users.auth_hash
    122     IS 'hash used for merchant back office authorization, may be NULL (unset)';
    123   COMMENT ON COLUMN merchant_users.auth_salt
    124     IS 'salt to use when hashing password before comparing with auth_hash';
    125   
    126   
    127   --- FIXME not sure if that is what we want...
    128   CREATE TABLE IF NOT EXISTS merchant_instance_users
    129     (user_serial BIGINT
    130        REFERENCES merchant_users (user_serial) ON DELETE CASCADE,
    131     merchant_serial BIGINT
    132        REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE  );
    133   COMMENT ON COLUMN merchant_instance_users.user_serial
    134     IS 'identifies an the admin user of the instance';
    135   
    136   COMMENT ON COLUMN merchant_login_tokens.merchant_serial
    137     IS 'identifies the instance for which the user is admin';
    138   
    139   
    140   INSERT INTO merchant_users (user_id, auth_hash, auth_salt)
    141   SELECT merchant_id, auth_hash, auth_salt FROM merchant_instances;
    142   
    143   ALTER TABLE merchant_instances
    144   DROP COLUMN auth_hash;
    145   
    146   ALTER TABLE merchant_instances
    147   DROP COLUMN auth_salt;
    148   
    149   COMMIT;
    150 
    151 Test Plan
    152 =========
    153 
    154 (If this DD concerns a new or changed feature, describe how it can be tested.)
    155 
    156 Locally, OIDC logins can be tested by running a local test OIDC server, e.g. "pipx run oidc-provider-mock" and
    157 configuring the endpoints accordingly.
    158 
    159 Definition of Done
    160 ==================
    161 
    162 (Only applicable to design documents that describe a new feature.  While the
    163 DoD is not satisfied yet, a user-facing feature **must** be behind a feature
    164 flag or dev-mode flag.)
    165 
    166 Alternatives
    167 ============
    168 
    169 1. No user concept
    170 
    171 It is theoretically possible to implement authentication and self-provisioning without adding the concept of a *User* to the merchant.
    172 This will require some gymnastics around the SPA such that first, OIDC users can log-in to an instance at all.
    173 This means adding a list of authorized OIDC IDs (careful: Must be unique/include the IdP ID) to the instance.
    174 This modification is also required in the proposed solution.
    175 This will allow OIDC federated users to log in to *existing* instances for which this login was pre-configured by an admin.
    176 In order to further support self-provisioning, we need a mechanism that allows OIDC to log in without an associated instance.
    177 This is tricky because the authentication is tightly integrated with the concept of having an instance behind it.
    178 The simplest solution would be to have the SPA do the OIDC flow browser side, and allow the self-provisioning API endpoint to accept the OIDC access token (or ID token) as credentials which will create an instance with this identity as authroized admin user.
    179 Note that this goes off-spec of OIDC as both the ID token and access token are not supposed to be presented to the Merchant backend API (ID token audience limited to the client = SPA, access token audience is limited to the IdP Userinfo endpoint).
    180 
    181 
    182 Drawbacks
    183 =========
    184 
    185 Introducing the concept of a User is a rather big change to the authentication logic of the merchant.
    186 However, it will edge its architecture conceptually closer to common OIDC-based approaches.
    187 Meaning that if in the future the Merchant authentication is delegated completely to an OIDC IdP, this change becomes easier.
    188 
    189 The alternatives do not provide this but also incur rather big changes that are kind of messy as elaborated above.
    190 
    191 Discussion / Q&A
    192 ================
    193 
    194 1. On the difficulty of not having a *User*
    195 
    196 So after some drafting of an OIDC implementation I can see that IF you need some kind of self-service instance creation (via OIDC) I think we really really should add the concept of a user.
    197 As long as we only configure an IDP and an authorized user for an instance/instances, the implementation is trivial. Once we need the login before the instance even exists, it gets pretty ugly.
    198 I am not sure if I should continue with what I am doing rn until we know what exactly this self provisioning requirement is. Because if we currently authenticate against the instance, and then want to authenticate before the instance even exists, the whole concept falls apart.
    199 
    200 Without the self-service instance creation and only loggin into an existing instance w/o password and with OIDC instead, I can have an implementation done pretty soon.
    201 But with self-service instance creation most of that implementation will become obsolete because conceptually, authentication will have to be tied to the user and not the instance.
    202 
    203 What I mean with it gets ugly: What we could do is go completely off spec and have the SPA do the OIDC flow,aquiring the ID and access tokens. Then, we allow some kind of token exchange (OIDC token -> merchant token, kind of ugly as this is not really allowed) with the permission to create instances. Somehow we then have to limit the number of instances that can be created with that token (which is where it gets really ugly)