taler-docs

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

098-token-fountains.rst (10532B)


      1 .. _dd-98:
      2 
      3 DD 98: Token Fountains for Promotions
      4 #####################################
      5 
      6 Summary
      7 =======
      8 
      9 This document proposes token fountains as a simple way for institutions to
     10 distribute promotional ``discount`` and ``subscription`` tokens.  A wallet
     11 imports a bearer credential, periodically retrieves the available grants and
     12 withdraws blind-signed tokens within limits configured by the institution.
     13 
     14 .. note::
     15 
     16   This design document proposes an extension for discussion.  The protocol
     17   version name ``vTokenFountains`` is a placeholder.
     18 
     19 Motivation
     20 ==========
     21 
     22 Institutions sometimes want to distribute promotional benefits to a group
     23 without creating and paying a separate order for every recipient.  For
     24 example, a university may give students discount tokens accepted by a campus
     25 merchant, or periodically provide subscription tokens for a continuing
     26 promotion.
     27 
     28 GNU Taler already defines blind-signed merchant tokens.  What is missing is a
     29 simple distribution channel through which a wallet can obtain an authorized
     30 set of those tokens over time.  A **token fountain** is a bearer credential
     31 that authorizes such withdrawals.
     32 
     33 The institution can distribute a fountain as a QR code or deep link.  The
     34 wallet imports the link, learns which token families are available and
     35 withdraws blind-signed tokens subject to the configured limits.  Blind
     36 signatures keep a later use of a token unlinkable from its withdrawal.
     37 
     38 User Flow
     39 =========
     40 
     41 The recipient scans the promotion once.  The wallet obtains promotional
     42 tokens and offers an applicable token later at checkout.
     43 
     44 .. uml:: images/098/promotions-user-flow.puml
     45    :align: center
     46    :width: 90%
     47    :caption: Joining and using a token-fountain promotion
     48    :alt: Scan the promotion, receive promotional tokens in the wallet, use a
     49          token at checkout and receive the discount or benefit.
     50 
     51 Requirements
     52 ============
     53 
     54 * Institutions must be able to distribute promotional tokens without
     55   creating a separate order for every withdrawal.
     56 
     57 * A fountain must support grants for existing ``discount`` and
     58   ``subscription`` token families.
     59 
     60 * Token withdrawals must use blind signatures so that later token use is not
     61   linkable to withdrawal.
     62 
     63 * The merchant backend must enforce per-period withdrawal limits atomically.
     64 
     65 * Wallets must be able to import a fountain using a QR code or Taler URI.
     66 
     67 * The bearer secret must not appear in HTTP request URLs after onboarding.
     68 
     69 * Deployments must be able to update or revoke future withdrawals without
     70   invalidating tokens that were already issued.
     71 
     72 Proposed Solution
     73 =================
     74 
     75 Terminology
     76 -----------
     77 
     78 In protocol descriptions, this document uses the existing token-family
     79 ``kind`` values:
     80 
     81 * A ``discount`` token is useful for a one-time or otherwise limited
     82   promotion.  It may be consumed without the merchant issuing a replacement.
     83 
     84 * A ``subscription`` token represents a continuing promotion.  When an order
     85   consumes it, the existing merchant token mechanism can return a replacement
     86   token envelope as an output of the selected choice.
     87 
     88 User-facing applications may call either benefit a **pass**.  The protocol
     89 continues to distinguish them by token-family kind.
     90 
     91 Fountain Credential
     92 -------------------
     93 
     94 A fountain has:
     95 
     96 * a public ``fountain_id`` identifying the fountain within a merchant
     97   instance;
     98 
     99 * a random ``fountain_secret`` used as its bearer credential;
    100 
    101 * a polling frequency; and
    102 
    103 * one or more grants describing which token families may be withdrawn and
    104   at what rate.
    105 
    106 The onboarding URI is:
    107 
    108 ::
    109 
    110   taler://fountain/$MERCHANT_HOST[/$INSTANCE_PATH]/$FOUNTAIN_ID/$FOUNTAIN_SECRET
    111 
    112 The URI may be displayed as a QR code or opened as a wallet deep link.
    113 ``fountain_id`` tells the wallet which fountain it is importing, while
    114 ``fountain_secret`` authenticates the import.  The wallet must treat the full
    115 URI as sensitive.
    116 
    117 After import, the secret must not be placed in subsequent HTTP URLs.  It is
    118 sent in the ``Authorization`` header when reading fountain information and in
    119 the request body when withdrawing tokens, as specified by the merchant API.
    120 
    121 Deployment Models
    122 -----------------
    123 
    124 An institution may use one fountain for an entire campaign.  This is simple
    125 to distribute, but every recipient shares one bearer credential and the same
    126 withdrawal limits.  A leaked link cannot be revoked for only one recipient.
    127 
    128 Alternatively, the institution may create one fountain per recipient and
    129 distribute each link through its existing student communication system.  This
    130 allows independent limits and revocation without requiring the merchant
    131 backend to store the student's real identity.  The fountain description can
    132 contain an opaque reference meaningful only to the institution.
    133 
    134 The choice is operational rather than cryptographic.  Blind token issuance
    135 prevents the merchant from linking a withdrawn token to its later use in
    136 either model.
    137 
    138 Grant Model
    139 -----------
    140 
    141 Each :ts:type:`FountainGrant` names a token family and defines:
    142 
    143 * ``tokens_per_period_limit``, the maximum number of tokens signed for one
    144   issue-key validity period;
    145 
    146 * ``tokens_per_period_stash``, the number of tokens the wallet should try to
    147   keep available for that period; and
    148 
    149 * ``key_window_size``, the number of issue-key slots into the future for
    150   which the wallet may prepare tokens.
    151 
    152 The merchant may update grants without replacing the bearer credential.
    153 Wallets periodically retrieve the current grants according to ``poll_freq``.
    154 Removing a grant prevents future withdrawals from that family, but tokens
    155 already withdrawn remain valid according to the token family's issue keys.
    156 
    157 Protocol Flow
    158 -------------
    159 
    160 1. The institution configures suitable ``discount`` or ``subscription`` token
    161    families on the merchant backend.
    162 
    163 2. It creates a fountain with grants for those token families.
    164 
    165 3. The backend returns ``fountain_id`` and ``fountain_secret`` once.  The
    166    institution forms the Taler fountain URI and gives it to the intended
    167    recipients.
    168 
    169 4. A wallet imports the URI and calls ``GET /fountain/info`` using the bearer
    170    credential.
    171 
    172 5. The wallet prepares blinded token envelopes for any grants it needs to
    173    replenish and submits them to ``POST /fountain/withdraw``.
    174 
    175 6. The backend checks the grant, issue-key window and withdrawal limit, then
    176    blind-signs the accepted envelopes.
    177 
    178 7. The wallet unblinds and stores the tokens.  Their later use follows the
    179    existing merchant token protocol.
    180 
    181 Merchant API
    182 ------------
    183 
    184 Private Management Operations
    185 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    186 
    187 The merchant API adds private operations to create, inspect, update, list and
    188 delete fountains:
    189 
    190 * ``POST /private/fountains``
    191 
    192 * ``GET /private/fountains``
    193 
    194 * ``GET /private/fountains/$FOUNTAIN_ID``
    195 
    196 * ``PATCH /private/fountains/$FOUNTAIN_ID``
    197 
    198 * ``DELETE /private/fountains/$FOUNTAIN_ID``
    199 
    200 Creation returns:
    201 
    202 .. ts:def:: FountainCreateResponse
    203 
    204   interface FountainCreateResponse {
    205     // Public identifier included in the wallet onboarding URI.
    206     fountain_id: string;
    207 
    208     // 256-bit bearer credential, Crockford Base32 encoded.
    209     // Returned only at creation; the backend stores only its hash.
    210     fountain_secret: string;
    211   }
    212 
    213 Wallet Operations
    214 ~~~~~~~~~~~~~~~~~
    215 
    216 ``GET /fountain/info`` returns the current grants, token-family metadata and
    217 issue public keys needed to prepare blinded envelopes.
    218 
    219 ``POST /fountain/withdraw`` accepts the bearer credential and blinded
    220 envelopes.  The request is grouped by token family and issue-key slot so that
    221 the backend can enforce each grant's limits.
    222 
    223 Deletion invalidates future requests using the credential.  It does not
    224 invalidate tokens that the wallet already withdrew.
    225 
    226 Security Considerations
    227 =======================
    228 
    229 Anyone possessing a fountain URI can withdraw under its grants.  Distribution
    230 systems must therefore protect the URI like any bearer credential.  Wallets
    231 must avoid exposing it in history, telemetry, screenshots or backups that are
    232 not intended to contain secrets.
    233 
    234 The backend stores only a hash of ``fountain_secret``.  It must rate-limit
    235 failed authentication and enforce withdrawal limits atomically to prevent
    236 concurrent requests from exceeding a grant.
    237 
    238 Fountains are not proof of student identity.  Eligibility is decided by the
    239 institution when it distributes the link.  A shared campaign link can be
    240 forwarded, while a per-recipient link can only be individually disabled after
    241 the institution learns that it was compromised.
    242 
    243 Privacy Considerations
    244 ======================
    245 
    246 Fountain descriptions should contain opaque administrative references rather
    247 than names or student identifiers.  The merchant backend does not need a
    248 recipient's identity to issue blind-signed tokens.
    249 
    250 Network metadata can still correlate a wallet's requests.  Deployments that
    251 require stronger network-level privacy need an appropriate transport or proxy;
    252 blind signatures alone do not hide IP addresses or request timing.
    253 
    254 Drawbacks
    255 =========
    256 
    257 The fountain API adds persistent bearer credentials, polling and quota state
    258 to the merchant backend.
    259 
    260 Shared campaign fountains trade administrative simplicity for coarse
    261 revocation and shared quotas.  Per-recipient fountains provide better control
    262 but require the institution to create and distribute many distinct links.
    263 
    264 Test Plan
    265 =========
    266 
    267 * Test creation, inspection, update, listing and deletion of fountains.
    268 
    269 * Test importing a URI containing both ``fountain_id`` and
    270   ``fountain_secret``.
    271 
    272 * Test withdrawals for both ``discount`` and ``subscription`` token families.
    273 
    274 * Test atomic enforcement of per-period limits under concurrent requests.
    275 
    276 * Test rejection of unknown grants, unsupported issue-key slots and invalid
    277   credentials.
    278 
    279 * Test that deleting a fountain prevents new withdrawals without invalidating
    280   tokens already held by a wallet.
    281 
    282 * Test that token use cannot be linked to its fountain withdrawal from the
    283   blind-signature transcript.
    284 
    285 Definition of Done
    286 ==================
    287 
    288 * The merchant backend implements fountain management, information and
    289   withdrawal operations.
    290 
    291 * The onboarding URI includes the merchant location, ``fountain_id`` and
    292   ``fountain_secret``.
    293 
    294 * Wallet Core can import a fountain, poll its grants, replenish tokens within
    295   the configured limits and stop when the credential is revoked.
    296 
    297 * Merchant applications can create and distribute promotional fountains
    298   without supplying recipient identities to the merchant backend.
    299 
    300 * Discount and subscription token families retain their existing consumption
    301   and replacement semantics.