merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_auth.c (22607B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014--2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file src/backend/taler-merchant-httpd_auth.c
     18  * @brief client authentication logic
     19  * @author Martin Schanzenbach
     20  * @author Christian Grothoff
     21  */
     22 #include "platform.h"
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include <gnunet/gnunet_db_lib.h>
     25 #include <taler/taler_json_lib.h>
     26 #include "taler-merchant-httpd_auth.h"
     27 #include "taler-merchant-httpd_helper.h"
     28 
     29 /**
     30  * Maximum length of a permissions string of a scope
     31  */
     32 #define TMH_MAX_SCOPE_PERMISSIONS_LEN 4096
     33 
     34 /**
     35  * Maximum length of a name of a scope
     36  */
     37 #define TMH_MAX_NAME_LEN 255
     38 
     39 /**
     40  * Represents a hard-coded set of default scopes with their
     41  * permissions and names
     42  */
     43 struct ScopePermissionMap
     44 {
     45   /**
     46    * The scope enum value
     47    */
     48   enum TMH_AuthScope as;
     49 
     50   /**
     51    * The scope name
     52    */
     53   char name[TMH_MAX_NAME_LEN];
     54 
     55   /**
     56    * The scope permissions string.
     57    * Comma-separated.
     58    */
     59   char permissions[TMH_MAX_SCOPE_PERMISSIONS_LEN];
     60 };
     61 
     62 /**
     63  * The default scopes array for merchant
     64  */
     65 static struct ScopePermissionMap scope_permissions[] = {
     66   /* Deprecated since v19 */
     67   {
     68     .as = TMH_AS_ALL,
     69     .name = "write",
     70     .permissions = "*"
     71   },
     72   /* Full access for SPA */
     73   {
     74     .as = TMH_AS_ALL,
     75     .name = "all",
     76     .permissions = "*"
     77   },
     78   /* Full access for SPA */
     79   {
     80     .as = TMH_AS_SPA,
     81     .name = "spa",
     82     .permissions = "*"
     83   },
     84   /* Read-only access */
     85   {
     86     .as = TMH_AS_READ_ONLY,
     87     .name = "readonly",
     88     .permissions = "*-read"
     89   },
     90   /* Simple order management */
     91   {
     92     .as = TMH_AS_ORDER_SIMPLE,
     93     .name = "order-simple",
     94     .permissions = "orders-read,orders-write"
     95   },
     96   /* Order management for PoS, allows inventory locking and refunds */
     97   {
     98     .as = TMH_AS_ORDER_POS,
     99     .name = "order-pos",
    100     .permissions = "orders-read,orders-write,pos-read,products-read,products-lock,orders-refund"
    101   },
    102   /* Simple order management, also allows refunding */
    103   {
    104     .as = TMH_AS_ORDER_MGMT,
    105     .name = "order-mgmt",
    106     .permissions = "orders-read,orders-write,pos-read,orders-refund"
    107   },
    108   /* Full order management, allows inventory locking and refunds */
    109   {
    110     .as = TMH_AS_ORDER_FULL,
    111     .name = "order-full",
    112     .permissions = "orders-read,orders-write,pos-read,products-lock,orders-refund"
    113   },
    114   /* No permissions, dummy scope */
    115   {
    116     .as = TMH_AS_NONE,
    117   }
    118 };
    119 
    120 
    121 /**
    122  * Get permissions string for scope.
    123  * Also extracts the leftmost bit into the @a refreshable
    124  * output parameter.
    125  *
    126  * @param as the scope to get the permissions string from
    127  * @param[out] refreshable true if the token associated with this scope is refreshable.
    128  * @return the permissions string, or NULL if no such scope found
    129  */
    130 static const char*
    131 get_scope_permissions (enum TMH_AuthScope as,
    132                        bool *refreshable)
    133 {
    134   *refreshable = as & TMH_AS_REFRESHABLE;
    135   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    136   {
    137     /* We ignore the TMH_AS_REFRESHABLE bit */
    138     if ( (as & ~TMH_AS_REFRESHABLE)  ==
    139          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
    140       return scope_permissions[i].permissions;
    141   }
    142   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    143               "Failed to find required permissions for scope %d\n",
    144               as);
    145   return NULL;
    146 }
    147 
    148 
    149 /**
    150  * Extract the token from authorization header value @a auth.
    151  * The @a auth value can be a bearer token or a Basic
    152  * authentication header. In both cases, this function
    153  * updates @a auth to point to the actual credential,
    154  * skipping spaces.
    155  *
    156  * NOTE: We probably want to replace this function with MHD2
    157  * API calls in the future that are more robust.
    158  *
    159  * @param[in,out] auth pointer to authorization header value,
    160  *        will be updated to point to the start of the token
    161  *        or set to NULL if header value is invalid
    162  * @param[out] is_basic_auth will be set to true if the
    163  *        authorization header uses basic authentication,
    164  *        otherwise to false
    165  */
    166 static void
    167 extract_auth (const char **auth,
    168               bool *is_basic_auth)
    169 {
    170   const char *bearer = "Bearer ";
    171   const char *basic = "Basic ";
    172   const char *tok = *auth;
    173   size_t offset = 0;
    174   bool is_bearer = false;
    175 
    176   *is_basic_auth = false;
    177   if (0 == strncmp (tok,
    178                     bearer,
    179                     strlen (bearer)))
    180   {
    181     offset = strlen (bearer);
    182     is_bearer = true;
    183   }
    184   else if (0 == strncmp (tok,
    185                          basic,
    186                          strlen (basic)))
    187   {
    188     offset = strlen (basic);
    189     *is_basic_auth = true;
    190   }
    191   else
    192   {
    193     *auth = NULL;
    194     return;
    195   }
    196   tok += offset;
    197   while (' ' == *tok)
    198     tok++;
    199   if ( (is_bearer) &&
    200        (0 != strncasecmp (tok,
    201                           RFC_8959_PREFIX,
    202                           strlen (RFC_8959_PREFIX))) )
    203   {
    204     *auth = NULL;
    205     return;
    206   }
    207   *auth = tok;
    208 }
    209 
    210 
    211 /**
    212  * Check if @a userpass grants access to @a instance.
    213  *
    214  * @param userpass base64 encoded "$USERNAME:$PASSWORD" value
    215  *        from HTTP Basic "Authentication" header
    216  * @param instance the access controlled instance
    217  */
    218 static enum GNUNET_GenericReturnValue
    219 check_auth_instance (const char *userpass,
    220                      struct TMH_MerchantInstance *instance)
    221 {
    222   char *tmp;
    223   char *colon;
    224   char *instance_name;
    225   const char *password;
    226   const char *target_instance = "admin";
    227   enum GNUNET_GenericReturnValue ret;
    228 
    229   /* implicitly a zeroed out hash means no authentication */
    230   if (GNUNET_is_zero (&instance->auth.auth_hash))
    231     return GNUNET_OK;
    232   if (NULL == userpass)
    233   {
    234     GNUNET_break_op (0);
    235     return GNUNET_SYSERR;
    236   }
    237   if (0 ==
    238       GNUNET_STRINGS_base64_decode (userpass,
    239                                     strlen (userpass),
    240                                     (void**) &tmp))
    241   {
    242     /* GNUNET_STRINGS_base64_decode() always allocates its output
    243        buffer, even when it returns 0; free it to avoid a leak. */
    244     GNUNET_break_op (0);
    245     GNUNET_free (tmp);
    246     return GNUNET_SYSERR;
    247   }
    248   colon = strchr (tmp,
    249                   ':');
    250   if (NULL == colon)
    251   {
    252     GNUNET_break_op (0);
    253     GNUNET_free (tmp);
    254     return GNUNET_SYSERR;
    255   }
    256   *colon = '\0';
    257   password = colon + 1;
    258   /* Instance IDs are stored in canonical (lower-case) form (see
    259      GNUNET_STRINGS_utf8_tolower() in the instance creation and lookup
    260      paths), so we must fold the username to the same canonical form
    261      before comparing; otherwise a mixed-case username would not match
    262      the stored id ("myshop") and Basic auth would fail with HTTP 401. */
    263   instance_name = GNUNET_STRINGS_utf8_tolower (tmp);
    264   /* instance->settings.id can be NULL if there is no instance yet */
    265   if (NULL != instance->settings.id)
    266     target_instance = instance->settings.id;
    267   if (0 != strcmp (instance_name,
    268                    target_instance))
    269   {
    270     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    271                 "Somebody tried to login to instance %s with username %s (login failed).\n",
    272                 target_instance,
    273                 instance_name);
    274     GNUNET_free (instance_name);
    275     GNUNET_free (tmp);
    276     return GNUNET_SYSERR;
    277   }
    278   GNUNET_free (instance_name);
    279   ret = TMH_check_auth (password,
    280                         &instance->auth.auth_salt,
    281                         &instance->auth.auth_hash);
    282   GNUNET_free (tmp);
    283   if (GNUNET_OK != ret)
    284   {
    285     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    286                 "Password provided does not match credentials for %s\n",
    287                 target_instance);
    288   }
    289   return ret;
    290 }
    291 
    292 
    293 void
    294 TMH_compute_auth (const char *token,
    295                   struct TALER_MerchantAuthenticationSaltP *salt,
    296                   struct TALER_MerchantAuthenticationHashP *hash)
    297 {
    298   GNUNET_CRYPTO_random_block (salt,
    299                               sizeof (*salt));
    300   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    301               "Computing initial auth using token with salt %s\n",
    302               TALER_B2S (salt));
    303   TALER_merchant_instance_auth_hash_with_salt (hash,
    304                                                salt,
    305                                                token);
    306 }
    307 
    308 
    309 /**
    310  * Function used to process Basic authorization header value.
    311  * Sets correct scope in the auth_scope parameter of the
    312  * #TMH_HandlerContext.
    313  *
    314  * @param hc the handler context
    315  * @param authn_s the value of the authorization header
    316  */
    317 static void
    318 process_basic_auth (struct TMH_HandlerContext *hc,
    319                     const char *authn_s)
    320 {
    321   /* Handle token endpoint slightly differently: Only allow
    322    * instance password (Basic auth) to retrieve access token.
    323    * We need to handle authorization with Basic auth here first
    324    * The only time we need to handle authentication like this is
    325    * for the token endpoint!
    326    */
    327   if ( (0 != strcmp (hc->rh->url_prefix,
    328                      "/token")) ||
    329        (NULL == hc->rh->method) ||
    330        (0 != strcmp (MHD_HTTP_METHOD_POST,
    331                      hc->rh->method)) ||
    332        (NULL == hc->instance))
    333   {
    334     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    335                 "Called endpoint `%s' with Basic authentication. Rejecting...\n",
    336                 hc->rh->url_prefix);
    337     hc->auth_scope = TMH_AS_NONE;
    338     return;
    339   }
    340   if (GNUNET_OK ==
    341       check_auth_instance (authn_s,
    342                            hc->instance))
    343   {
    344     hc->auth_scope = TMH_AS_ALL;
    345   }
    346   else
    347   {
    348     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    349                 "Basic authentication failed!\n");
    350     hc->auth_scope = TMH_AS_NONE;
    351   }
    352 }
    353 
    354 
    355 /**
    356  * Function used to process Bearer authorization header value.
    357  * Sets correct scope in the auth_scope parameter of the
    358  * #TMH_HandlerContext..
    359  *
    360  * @param hc the handler context
    361  * @param authn_s the value of the authorization header
    362  * @return TALER_EC_NONE on success.
    363  */
    364 static enum TALER_ErrorCode
    365 process_bearer_auth (struct TMH_HandlerContext *hc,
    366                      const char *authn_s)
    367 {
    368   if (NULL == hc->instance)
    369   {
    370     hc->auth_scope = TMH_AS_NONE;
    371     return TALER_EC_NONE;
    372   }
    373   if (GNUNET_is_zero (&hc->instance->auth.auth_hash))
    374   {
    375     /* hash zero means no authentication for instance */
    376     hc->auth_scope = TMH_AS_ALL;
    377     return TALER_EC_NONE;
    378   }
    379   {
    380     enum TALER_ErrorCode ec;
    381 
    382     ec = TMH_check_token (authn_s,
    383                           hc->instance->settings.id,
    384                           &hc->auth_scope);
    385     if (TALER_EC_NONE != ec)
    386     {
    387       char *dec;
    388       size_t dec_len;
    389       const char *token;
    390 
    391       /* NOTE: Deprecated, remove sometime after v1.1 */
    392       if (0 != strncasecmp (authn_s,
    393                             RFC_8959_PREFIX,
    394                             strlen (RFC_8959_PREFIX)))
    395       {
    396         GNUNET_break_op (0);
    397         hc->auth_scope = TMH_AS_NONE;
    398         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    399                     "Authentication token invalid: %d\n",
    400                     (int) ec);
    401         return ec;
    402       }
    403       if (TALER_EC_GENERIC_TOKEN_MALFORMED != ec)
    404       {
    405         /* The credential is a well-formed login token that is unknown
    406            or expired, or the lookup failed: report that, instead of
    407            trying to interpret the token as a password. */
    408         hc->auth_scope = TMH_AS_NONE;
    409         return ec;
    410       }
    411       {
    412         const struct TMH_MerchantInstance *mi = hc->instance;
    413         bool have_mfa
    414           = ( (NULL != mi->settings.phone) &&
    415               (NULL != TMH_helper_sms) &&
    416               (mi->settings.phone_validated) ) ||
    417             ( (NULL != mi->settings.email) &&
    418               (NULL != TMH_helper_email) &&
    419               (mi->settings.email_validated) );
    420 
    421         if ( (GNUNET_YES == TMH_strict_v19) ||
    422              have_mfa)
    423         {
    424           /* Passing the password as a bearer token would bypass the
    425              second factor required by POST /private/token. */
    426           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    427                       "Refusing deprecated secret-token:password API authN\n");
    428           hc->auth_scope = TMH_AS_NONE;
    429           return TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED;
    430         }
    431       }
    432       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    433                   "Trying deprecated secret-token:password API authN\n");
    434       token = authn_s + strlen (RFC_8959_PREFIX);
    435       dec_len = GNUNET_STRINGS_urldecode (token,
    436                                           strlen (token),
    437                                           &dec);
    438       if ( (0 == dec_len) ||
    439            (GNUNET_OK !=
    440             TMH_check_auth (dec,
    441                             &hc->instance->auth.auth_salt,
    442                             &hc->instance->auth.auth_hash)) )
    443       {
    444         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    445                     "Login failed\n");
    446         hc->auth_scope = TMH_AS_NONE;
    447         GNUNET_free (dec);
    448         return TALER_EC_NONE;
    449       }
    450       hc->auth_scope = TMH_AS_ALL;
    451       GNUNET_free (dec);
    452     }
    453   }
    454   return TALER_EC_NONE;
    455 }
    456 
    457 
    458 /**
    459  * Checks if @a permission_required is in permissions of
    460  * @a scope.
    461  *
    462  * @param permission_required the permission to check.
    463  * @param scope the scope to check.
    464  * @return true if @a permission_required is in the permissions set of @a scope.
    465  */
    466 static bool
    467 permission_in_scope (const char *permission_required,
    468                      enum TMH_AuthScope scope)
    469 {
    470   char *permissions;
    471   const char *perms_tmp;
    472   bool is_read_perm = false;
    473   bool is_write_perm = false;
    474   bool refreshable;
    475   const char *last_dash;
    476 
    477   perms_tmp = get_scope_permissions (scope,
    478                                      &refreshable);
    479   if (NULL == perms_tmp)
    480   {
    481     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    482                 "Permission check failed: scope %d not understood\n",
    483                 (int) scope);
    484     return false;
    485   }
    486   last_dash = strrchr (permission_required,
    487                        '-');
    488   if (NULL != last_dash)
    489   {
    490     is_write_perm = (0 == strcmp (last_dash,
    491                                   "-write"));
    492     is_read_perm = (0 == strcmp (last_dash,
    493                                  "-read"));
    494   }
    495 
    496   if (0 == strcmp ("token-refresh",
    497                    permission_required))
    498   {
    499     if (! refreshable)
    500     {
    501       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    502                   "Permission check failed: token not refreshable\n");
    503     }
    504     return refreshable;
    505   }
    506   permissions = GNUNET_strdup (perms_tmp);
    507   {
    508     char *sp;
    509     const char *perm = strtok_r (permissions,
    510                                  ",",
    511                                  &sp);
    512 
    513     if (NULL == perm)
    514     {
    515       GNUNET_free (permissions);
    516       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    517                   "Permission check failed: empty permission set\n");
    518       return false;
    519     }
    520     while (NULL != perm)
    521     {
    522       if (0 == strcmp ("*",
    523                        perm))
    524       {
    525         GNUNET_free (permissions);
    526         return true;
    527       }
    528       if ( (0 == strcmp ("*-write",
    529                          perm)) &&
    530            (is_write_perm) )
    531       {
    532         GNUNET_free (permissions);
    533         return true;
    534       }
    535       if ( (0 == strcmp ("*-read",
    536                          perm)) &&
    537            (is_read_perm) )
    538       {
    539         GNUNET_free (permissions);
    540         return true;
    541       }
    542       if (0 == strcmp (permission_required,
    543                        perm))
    544       {
    545         GNUNET_free (permissions);
    546         return true;
    547       }
    548       perm = strtok_r (NULL,
    549                        ",",
    550                        &sp);
    551     }
    552   }
    553   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    554               "Permission check failed: %s not found in %s\n",
    555               permission_required,
    556               perms_tmp);
    557   GNUNET_free (permissions);
    558   return false;
    559 }
    560 
    561 
    562 bool
    563 TMH_scope_is_subset (enum TMH_AuthScope as,
    564                      enum TMH_AuthScope candidate)
    565 {
    566   const char *as_perms;
    567   const char *candidate_perms;
    568   char *permissions;
    569   bool as_refreshable;
    570   bool cand_refreshable;
    571 
    572   as_perms = get_scope_permissions (as,
    573                                     &as_refreshable);
    574   candidate_perms = get_scope_permissions (candidate,
    575                                            &cand_refreshable);
    576   if (! as_refreshable && cand_refreshable)
    577     return false;
    578   if ( (NULL == as_perms) &&
    579        (NULL != candidate_perms) )
    580     return false;
    581   if ( (NULL == candidate_perms) ||
    582        (0 == strcmp ("*",
    583                      as_perms)))
    584     return true;
    585   permissions = GNUNET_strdup (candidate_perms);
    586   {
    587     char *sp;
    588     const char *perm;
    589 
    590     /* permission_in_scope() tokenizes as well, so we must not
    591        use the non-reentrant strtok() here. */
    592     perm = strtok_r (permissions,
    593                      ",",
    594                      &sp);
    595     if (NULL == perm)
    596     {
    597       GNUNET_free (permissions);
    598       return true;
    599     }
    600     while (NULL != perm)
    601     {
    602       if (! permission_in_scope (perm,
    603                                  as))
    604       {
    605         GNUNET_free (permissions);
    606         return false;
    607       }
    608       perm = strtok_r (NULL,
    609                        ",",
    610                        &sp);
    611     }
    612   }
    613   GNUNET_free (permissions);
    614   return true;
    615 }
    616 
    617 
    618 enum TMH_AuthScope
    619 TMH_get_scope_by_name (const char *name)
    620 {
    621   if (NULL == name)
    622     return TMH_AS_NONE;
    623   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    624   {
    625     if (0 == strcasecmp (scope_permissions[i].name,
    626                          name))
    627       return scope_permissions[i].as;
    628   }
    629   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    630               "Name `%s' does not match any scope we understand\n",
    631               name);
    632   return TMH_AS_NONE;
    633 }
    634 
    635 
    636 const char*
    637 TMH_get_name_by_scope (enum TMH_AuthScope scope,
    638                        bool *refreshable)
    639 {
    640   *refreshable = scope & TMH_AS_REFRESHABLE;
    641   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    642   {
    643     /* We ignore the TMH_AS_REFRESHABLE bit */
    644     if ( (scope & ~TMH_AS_REFRESHABLE)  ==
    645          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
    646       return scope_permissions[i].name;
    647   }
    648   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    649               "Scope #%d does not match any scope we understand\n",
    650               (int) scope);
    651   return NULL;
    652 }
    653 
    654 
    655 enum GNUNET_GenericReturnValue
    656 TMH_check_auth (const char *password,
    657                 struct TALER_MerchantAuthenticationSaltP *salt,
    658                 struct TALER_MerchantAuthenticationHashP *hash)
    659 {
    660   struct TALER_MerchantAuthenticationHashP val;
    661 
    662   if (GNUNET_is_zero (hash))
    663     return GNUNET_OK;
    664   if (NULL == password)
    665   {
    666     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    667                 "Denying access: empty password provided\n");
    668     return GNUNET_SYSERR;
    669   }
    670   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    671               "Checking against token with salt %s\n",
    672               TALER_B2S (salt));
    673   TALER_merchant_instance_auth_hash_with_salt (&val,
    674                                                salt,
    675                                                password);
    676   if (0 !=
    677       GNUNET_memcmp (&val,
    678                      hash))
    679   {
    680     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    681                 "Access denied: password does not match\n");
    682     return GNUNET_SYSERR;
    683   }
    684   return GNUNET_OK;
    685 }
    686 
    687 
    688 /**
    689  * Check if the client has provided the necessary credentials
    690  * to access the selected endpoint of the selected instance.
    691  *
    692  * @param[in,out] hc handler context
    693  * @return #GNUNET_OK on success,
    694  *         #GNUNET_NO if an error was queued (return #MHD_YES)
    695  *         #GNUNET_SYSERR to close the connection (return #MHD_NO)
    696  */
    697 enum GNUNET_GenericReturnValue
    698 TMH_perform_access_control (struct TMH_HandlerContext *hc)
    699 {
    700   const char *auth;
    701   bool is_basic_auth = false;
    702   bool auth_malformed = false;
    703 
    704   auth = MHD_lookup_connection_value (hc->connection,
    705                                       MHD_HEADER_KIND,
    706                                       MHD_HTTP_HEADER_AUTHORIZATION);
    707   if (NULL != auth)
    708   {
    709     extract_auth (&auth,
    710                   &is_basic_auth);
    711     if (NULL == auth)
    712       auth_malformed = true;
    713     hc->auth_token = auth;
    714   }
    715 
    716   /* If we have zero configured instances (not even ones that have been
    717      purged) or explicitly disabled authentication, THEN we accept anything
    718      (no access control), as we then also have no data to protect. */
    719   if ((0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) ||
    720       (GNUNET_YES == TMH_auth_disabled))
    721   {
    722     hc->auth_scope = TMH_AS_ALL;
    723   }
    724   else if (is_basic_auth)
    725   {
    726     process_basic_auth (hc,
    727                         auth);
    728   }
    729   else   /* Check bearer token */
    730   {
    731     enum TALER_ErrorCode ec;
    732 
    733     ec = process_bearer_auth (hc,
    734                               auth);
    735     if (TALER_EC_NONE != ec)
    736     {
    737       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    738                   "Bearer authentication failed: %d\n",
    739                   (int) ec);
    740       return (MHD_YES ==
    741               TALER_MHD_reply_with_ec (hc->connection,
    742                                        ec,
    743                                        NULL))
    744           ? GNUNET_NO
    745           : GNUNET_SYSERR;
    746     }
    747   }
    748   /* We grant access if:
    749      - Endpoint does not require permissions
    750      - Authorization scope of bearer token contains permissions
    751        required by endpoint.
    752    */
    753   if ( (NULL != hc->rh->permission) &&
    754        (! permission_in_scope (hc->rh->permission,
    755                                hc->auth_scope)))
    756   {
    757     if (auth_malformed &&
    758         (TMH_AS_NONE == hc->auth_scope) )
    759     {
    760       GNUNET_break_op (0);
    761       return (MHD_YES ==
    762               TALER_MHD_reply_with_error (
    763                 hc->connection,
    764                 MHD_HTTP_UNAUTHORIZED,
    765                 TALER_EC_GENERIC_PARAMETER_MALFORMED,
    766                 "'" RFC_8959_PREFIX
    767                 "' prefix or 'Bearer' missing in 'Authorization' header"))
    768           ? GNUNET_NO
    769           : GNUNET_SYSERR;
    770     }
    771     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    772                 "Credentials provided are %d which are insufficient for access to `%s'\n",
    773                 (int) hc->auth_scope,
    774                 hc->rh->permission);
    775     return (MHD_YES ==
    776             TALER_MHD_reply_with_error (
    777               hc->connection,
    778               MHD_HTTP_UNAUTHORIZED,
    779               TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
    780               "Check credentials in 'Authorization' header"))
    781         ? GNUNET_NO
    782         : GNUNET_SYSERR;
    783   }
    784   return GNUNET_OK;
    785 }