049-auth.rst (8783B)
1 DD 49: Authentication 2 ##################### 3 4 :Design status: Accepted 5 :Implementation status: Implemented 6 :DD shepherd: TBD 7 :Historical contributors: Florian Dold, Sebastian, Antoine A, Martin Schanzenbach, Christian Grothoff 8 :First published: 2023-09-06 9 :Last substantive change: 2025-06-19 10 :Implementation evidence: merchant (2023-09-06), libeufin (2024-11-15) 11 :Normative references: ``core/api-merchant.rst``, ``core/api-corebank.rst`` 12 13 .. note:: 14 15 Authentication is implemented, but token-refresh syntax is 16 component-specific. The component API specifications are normative. 17 18 Summary 19 ======= 20 21 This design document specifies a simple authentication framework to be used by multiple Taler 22 components that require authentication. 23 24 Motivation 25 ========== 26 27 SPAs currently store the username and password in locals storage (or at least 28 session storage). 29 30 There's also no way to manage auth tokens third parties (e.g. 31 auditors). 32 33 Requirements 34 ============ 35 36 * simple specification 37 * simple implementation 38 * simple to use 39 * must cover two main use cases: 40 41 * SPA login 42 * delegating (possibly restricted) access to a third party using a token 43 44 Proposed Solution 45 ================= 46 47 We define a ``token`` endpoint that can be used to obtain access tokens from 48 other forms of authentication, typically HTTP Basic auth. 49 50 Token Creation 51 -------------- 52 53 .. http:post:: /${RESOURCE...}/token 54 55 Create an authentification token. 56 57 **Request:** 58 59 .. ts:def:: TokenRequest 60 61 interface TokenRequest { 62 // Service-defined scope for the token. 63 // Typical scopes would be "readonly" or "readwrite". 64 scope: string; 65 66 // Server may impose its own upper bound 67 // on the token validity duration 68 duration?: RelativeTime; 69 70 // Is the token refreshable into a new token during its 71 // validity? 72 // Refreshable tokens effectively provide indefinite 73 // access if they are refreshed in time. 74 // Deprecated, use ":refreshable" suffix in scope instead. 75 refreshable?: boolean; 76 77 // Optional token description 78 // @since v4 79 description?: string; 80 } 81 82 **Response:** 83 84 :http:statuscode:`200 Ok`: 85 The response is a `TokenSuccessResponse` 86 87 **Details:** 88 89 .. ts:def:: TokenSuccessResponse 90 91 interface TokenSuccessResponse { 92 // Expiration determined by the server. 93 // Can be based on the token_duration 94 // from the request, but ultimately the 95 // server decides the expiration. 96 expiration: Timestamp; 97 98 // Opque access token. 99 access_token: string; 100 } 101 102 Token Revocation 103 ---------------- 104 105 Clients using session tokens log by forgetting the session token. 106 Tokens can be explicitly revoked by making a ``DELETE`` request on 107 the token endpoint. 108 109 .. http:delete:: /${RESOURCE...}/token 110 111 Invalidate the access token that is being used to make the request. 112 **Authentication:** The client must authenticate 113 with a valid access token. 114 115 Token Information 116 ----------------- 117 118 List existing token information. 119 120 .. http:get:: /${RESOURCE...}/tokens 121 122 **Request:** 123 124 :query delta: *Optional.* 125 Takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` are returned. Defaults to ``-20`` to return the last 20 entries. 126 :query start: *Optional.* 127 Row number threshold, see ``delta`` for its interpretation. Defaults to smallest or biggest row id possible according to ``delta`` sign. 128 129 **Response:** 130 131 :http:statuscode:`200 OK`: 132 Response is a `TokenInfos`. 133 :http:statuscode:`204 No content`: 134 No tokens. 135 136 **Details:** 137 138 .. ts:def:: TokenInfos 139 140 interface TokenInfos { 141 tokens: TokenInfo[]; 142 } 143 144 .. ts:def:: TokenInfo 145 146 interface TokenInfo { 147 // Time when the token was created. 148 creation_time: Timestamp; 149 150 // Expiration determined by the server. 151 // Can be based on the token_duration 152 // from the request, but ultimately the 153 // server decides the expiration. 154 expiration: Timestamp; 155 156 // Service-defined scope for the token. 157 // Typical scopes would be "readonly" or "readwrite". 158 scope: string; 159 160 // Is the token refreshable into a new token during its 161 // validity? 162 // Refreshable tokens effectively provide indefinite 163 // access if they are refreshed in time. 164 refreshable: boolean; 165 166 // Optional token description 167 description?: string; 168 169 // Time when the token was last used. 170 last_access: Timestamp; 171 172 // Opaque unique ID used for pagination. 173 row_id: Integer; 174 } 175 176 Permissions 177 =========== 178 179 Each API request to an endpoint **may** be associated with a *permission*. 180 A permission is a descriptive string, e.g. ``orders-read`` for a ``GET`` request on the endpoint ``/private/orders``. 181 Another example would be ``orders-write`` for a ``POST`` or ``PUT`` request on the same endpoint. 182 If no permission is defined for a request, no access control is enforced. 183 184 Each component API **must** define and document appropriate permissions for its requests. 185 Permission strings best practice include that *read-only* access end with the suffix ``-read``, e.g. ``orders-read``. 186 If the access to the endpoint modifies the state it is suffixed with ``-write``, e.g. ``orders-write``. 187 Special permissions may deviate from this. 188 Two endpoints **may** use the same permission. 189 190 In the API documentaction where the **Request** to an endpoint is defined, **Required permission** entry should be added. 191 See the Merchant API for examples. 192 193 Scopes 194 ====== 195 196 A ``scope`` is a set of permissions that is associated with a token. 197 The scope is provided when requesting the token, see `TokenRequest`. 198 199 Default scopes that can be requested in a `TokenRequest` are or rather **must** be defined and documented by the component. 200 Here are some *examples* of possible scopes: 201 202 * ``readonly``: ``*-read`` -- This wildcard match will grant access to all endpoints protected with a permission that has the ``-read`` suffix. 203 * ``admin``: ``*`` -- This matches all permissions, essentially the *key to the kingdom*. 204 * ``orders-simple``: ``orders-read,orders-write`` -- Access to reading and writing orders. 205 * ``orders-full``: ``orders-read,orders-write,orders-refund`` -- Like ``orders-simple``, but also allows for refunds. 206 207 In the merchant component, scopes are currently hard-coded. In the future, additional scopes may be configurable 208 through configuration files and/or default scopes overridden. 209 210 Token refresh 211 ============= 212 213 Tokens may be requested to be refreshable. Merchant APIs express this by 214 suffixing the requested scope with ``:refreshable``, for example 215 ``orders-full:refreshable``. The Core Bank API instead retains the 216 ``refreshable`` field in its ``TokenRequest``. Clients must follow the 217 normative API of the component they use. 218 219 Definition of Done 220 ================== 221 222 * [x] spec reviewed 223 * [x] implemented in merchant backend 224 * [x] implemented in libeufin-bank 225 * [x] implemented in the bank webui SPA 226 * [x] implemented in the merchant backoffice SPA 227 228 229 Alternatives 230 ============ 231 232 * use something much closer to OAuth2 233 234 * would be unnecessarly generic and complex 235 236 Session Tokens / Signatures 237 --------------------------- 238 239 For performance reasons, OAuth 2.0 uses two types of tokens: Short-lived access 240 tokens and long-lived refresh tokens. The access tokens can be implemented via 241 signatures and the long-lived refresh tokens via server-stored tokens. This 242 allows to cheaply validate access tokens, while still allowing longer expiration times 243 for refresh tokens. 244 245 We could do something similar by introducing login and session tokens. A login 246 token is a server-stored token. In addition to being used directly as an 247 access token, a login token can also be converted to a short-lived session 248 token. 249 250 Session access tokens should be implemented as "self-encoded tokens", i.e. 251 as tokens signed by the server without requiring server-side token storage. 252 Session access tokens should have a rather short maximum expiration. 253 254 The signature should be over ``(username, kind, scope, creation_timestamp, expiry)``. 255 256 To revoke session tokens, the server must store the timestamp of the last 257 revocation and only accept tokens with a ``creation_timestamp`` larger than the 258 last revocation timestamp. Individual session tokens cannot be revoked, only 259 all issued session tokens can be revoked at once. 260 261 However, we decided against doing this because the performance benefits 262 are not significant enough for us and having multiple token types would 263 lead to unnecessary complexity. 264 265 Drawbacks 266 ========= 267 268 * still more complex than simple auth tokens or HTTP basic auth 269 270 Discussion / Q&A 271 ================ 272 273 (This should be filled in with results from discussions on mailing lists / personal communication.)