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