RFC 6750 OAuth 2.0 Authorization Framework: Bearer Token Usage

The attached patch adds a minimal implementation of Bearer
authentication scheme to Squid. It consists of three components:

1) Squid build system infrastructure for building Bearer authentication

2) A testing fake-auth helper (bearer_fake_auth).

Helper which takes Bearer helper input an always returns OK.

3) Bearer authentication library ("module") for Squid.

 * implements the logics for squid.conf "Bearer" auth_param scheme and
necessary configuration options.

 * implements the helper management and API for Bearer helpers.

 * implements logics for www-auth and proxy-auth header parsing and
generating.

At present no restriction between HTTP and HTTPS is defined by Squid.
Challenges will be made for both. It is left to the client to ensure
adequate security on the connection it sends Bearer tokens.

 * implements helper driven TTL for token caching.

Due to significant security risks with Bearer tokens the TTL is not
configurable from squid.conf. Instead the helper is expected to provide
a ttl= parameter from the auth backend explicitly determining the time
in seconds for which each response may be cached and re-used. In absence
of ttl= value the helper response is treated as already expired (a nonce).
 A garbage collection TTL "cleanup_interval" is configurable and removes
cache entries which have been stale for at least 1 hr.


 * uses a default token scope of "proxy:HTTP" for generic HTTP proxies



NOTES:
 * At present no web browsers implement Bearer authentication in
response to a proxy-authenticate challenge.
  - However some of the common browsers should support Bearer
authentication with reverse proxies over HTTPS (Firefox and IE
apparently, not Chrome).
  - command line tools and AJAX / XHR implementations which allow header
customisation can be scripted to support Bearer.

 * This is only a minimal implementation, emitting only the realm= and
scope= parameters to clients.
 - The key_extras mechanism can be used to pass extension client request
parameters to the Bearer helper.
 - Extension parameters in Squid responses is not supported.

 * Bearer authentication to cache_peers is not supported explicitly.
  - implicit support exists with login=PASSTHRU, which may be used to
relay Bearer tokens for SSO to multiple proxies.

Amos
=== modified file 'configure.ac'
--- configure.ac        2014-07-13 08:49:42 +0000
+++ configure.ac        2014-07-30 23:59:58 +0000
@@ -1765,40 +1765,53 @@
 SQUID_YESNO([$enableval],
             [unrecognized argument to --enable-auth: $enableval])
 ])
 AC_MSG_NOTICE([Authentication support enabled: ${enable_auth:=yes}])
 SQUID_DEFINE_BOOL(USE_AUTH,$enable_auth,[Enable support for authentication])
 AM_CONDITIONAL(ENABLE_AUTH, test "x$enable_auth" != "xno")
 AUTH_MODULES=""
 
 AC_ARG_ENABLE(auth-basic,
   AS_HELP_STRING([--enable-auth-basic="list of helpers"],
      [Enable the basic authentication scheme, and build the specified helpers.
       Not providing an explicit list of helpers will attempt build of
       all possible helpers. Default is to do so.
       To disable the basic authentication scheme, use --disable-auth-basic.
       To enable but build no helpers, specify "none".
       To see available helpers, see the helpers/basic_auth directory. ]),[
 #nothing to do really
 ])
 m4_include([helpers/basic_auth/modules.m4])
 
+AC_ARG_ENABLE(auth-bearer,
+  AS_HELP_STRING([--enable-auth-bearer="list of helpers"],
+     [Enable the OAuth 2.0 Bearer authentication scheme, and build the
+      specified helpers.
+      Not providing an explicit list of helpers will attempt build of
+      all possible helpers. Default is to do so.
+      To disable the Bearer authentication scheme, use --disable-auth-bearer.
+      To enable but build no helpers, specify "none".
+      To see available helpers, see the helpers/bearer_auth directory. ]),[
+#nothing to do really
+])
+m4_include([helpers/bearer_auth/modules.m4])
+
 AC_ARG_ENABLE(auth-ntlm,
   AS_HELP_STRING([--enable-auth-ntlm="list of helpers"],
      [Enable the NTLM authentication scheme, and build the specified helpers.
       Not providing an explicit list of helpers will attempt build of
       all possible helpers. Default is to do so.
       To disable the NTLM authentication scheme, use --disable-auth-ntlm.
       To enable but build no helpers, specify "none".
       To see available helpers, see the helpers/ntlm_auth directory. ]),[
 ])
 m4_include([helpers/ntlm_auth/modules.m4])
 
 AC_ARG_ENABLE(auth-negotiate,
   AS_HELP_STRING([--enable-auth-negotiate="list of helpers"],
      [Enable the Negotiate authentication scheme, and build the specified 
       helpers.
       Not providing an explicit list of helpers will attempt build of
       all possible helpers. Default is to do so.
       To disable the Negotiate authentication scheme, 
       use --disable-auth-negotiate.
       To enable but build no helpers, specify "none".
@@ -3446,40 +3459,41 @@
 AC_CONFIG_FILES([
        Makefile
        compat/Makefile
        lib/Makefile
        lib/ntlmauth/Makefile
        lib/libTrie/Makefile
        lib/libTrie/test/Makefile
        lib/profiler/Makefile
        lib/rfcnb/Makefile
        lib/smblib/Makefile
        lib/snmplib/Makefile
        scripts/Makefile
        src/Makefile
        src/anyp/Makefile
        src/base/Makefile
        src/acl/Makefile
        src/fs/Makefile
        src/repl/Makefile
        src/auth/Makefile
        src/auth/basic/Makefile
+       src/auth/bearer/Makefile
        src/auth/digest/Makefile
        src/auth/negotiate/Makefile
        src/auth/ntlm/Makefile
        src/adaptation/Makefile
        src/adaptation/icap/Makefile
        src/adaptation/ecap/Makefile
        src/comm/Makefile
        src/esi/Makefile
        src/eui/Makefile
        src/format/Makefile
        src/http/Makefile
        src/icmp/Makefile
        src/ident/Makefile
        src/ip/Makefile
        src/log/Makefile
        src/ipc/Makefile
        src/ssl/Makefile
        src/mgr/Makefile
        src/parser/Makefile
        src/snmp/Makefile
@@ -3488,40 +3502,42 @@
        errors/Makefile
        test-suite/Makefile
        doc/Makefile
        doc/manuals/Makefile
        helpers/Makefile
        helpers/basic_auth/Makefile
        helpers/basic_auth/DB/Makefile
        helpers/basic_auth/fake/Makefile
        helpers/basic_auth/getpwnam/Makefile
        helpers/basic_auth/LDAP/Makefile
        helpers/basic_auth/MSNT/Makefile
        helpers/basic_auth/MSNT-multi-domain/Makefile
        helpers/basic_auth/NCSA/Makefile
        helpers/basic_auth/NIS/Makefile
        helpers/basic_auth/PAM/Makefile
        helpers/basic_auth/POP3/Makefile
        helpers/basic_auth/RADIUS/Makefile
        helpers/basic_auth/SASL/Makefile
        helpers/basic_auth/SMB/Makefile
        helpers/basic_auth/SSPI/Makefile
+       helpers/bearer_auth/Makefile
+       helpers/bearer_auth/fake/Makefile
        helpers/digest_auth/Makefile
        helpers/digest_auth/eDirectory/Makefile
        helpers/digest_auth/file/Makefile
        helpers/digest_auth/LDAP/Makefile
        helpers/ntlm_auth/Makefile
        helpers/ntlm_auth/fake/Makefile
        helpers/ntlm_auth/smb_lm/Makefile
        helpers/ntlm_auth/SSPI/Makefile
        helpers/negotiate_auth/Makefile
        helpers/negotiate_auth/kerberos/Makefile
        helpers/negotiate_auth/SSPI/Makefile
        helpers/negotiate_auth/wrapper/Makefile
        helpers/external_acl/Makefile
        helpers/external_acl/AD_group/Makefile
        helpers/external_acl/delayer/Makefile
        helpers/external_acl/eDirectory_userip/Makefile
        helpers/external_acl/file_userip/Makefile
        helpers/external_acl/kerberos_ldap_group/Makefile
        helpers/external_acl/LDAP_group/Makefile
        helpers/external_acl/LM_group/Makefile

=== modified file 'helpers/Makefile.am'
--- helpers/Makefile.am 2013-07-09 11:15:51 +0000
+++ helpers/Makefile.am 2014-05-22 09:19:25 +0000
@@ -1,30 +1,32 @@
 EXTRA_DIST = defines.h
 
 DIST_SUBDIRS = \
        basic_auth \
+       bearer_auth \
        digest_auth \
        external_acl \
        log_daemon \
        negotiate_auth \
        ntlm_auth \
        url_rewrite \
        ssl \
        storeid_rewrite
 
 SUBDIRS = \
        basic_auth \
+       bearer_auth \
        digest_auth \
        external_acl \
        log_daemon \
        negotiate_auth \
        url_rewrite \
        storeid_rewrite
 
 if ENABLE_AUTH_NTLM
 SUBDIRS += ntlm_auth
 endif
 
 if ENABLE_SSL
 SUBDIRS += ssl
 endif
 

=== added directory 'helpers/bearer_auth'
=== added file 'helpers/bearer_auth/Makefile.am'
--- helpers/bearer_auth/Makefile.am     1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/Makefile.am     2014-05-22 09:19:10 +0000
@@ -0,0 +1,7 @@
+## Alphabetical list of sub-directories to distribute with Squid:
+DIST_SUBDIRS = \
+       fake
+
+SUBDIRS = $(BEARER_AUTH_HELPERS)
+
+EXTRA_DIST = modules.m4

=== added directory 'helpers/bearer_auth/fake'
=== added file 'helpers/bearer_auth/fake/Makefile.am'
--- helpers/bearer_auth/fake/Makefile.am        1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/fake/Makefile.am        2014-05-22 09:19:10 +0000
@@ -0,0 +1,8 @@
+include $(top_srcdir)/src/Common.am
+
+libexec_PROGRAMS = bearer_fake_auth
+bearer_fake_auth_SOURCES = fake.cc
+
+LDADD = $(COMPAT_LIB)
+
+EXTRA_DIST = required.m4

=== added file 'helpers/bearer_auth/fake/fake.cc'
--- helpers/bearer_auth/fake/fake.cc    1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/fake/fake.cc    2014-05-26 07:54:14 +0000
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, Treehouse Networks Ltd. New Zealand
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Fake Bearer Authentication program for Squid.
+ *
+ * This code gets the user details and returns OK.
+ * It is intended for testing use and as a base for further implementation.
+ */
+
+#include "squid.h"
+#include "helpers/defines.h"
+
+#include <cstring>
+
+/**
+ * options:
+ * -d enable debugging.
+ * -h interface help.
+ */
+char *program_name = NULL;
+
+static void
+usage(void)
+{
+    fprintf(stderr,
+            "Usage: %s [-d] [-v] [-h]\n"
+            " -d  enable debugging.\n"
+            " -h  this message\n\n",
+            program_name);
+}
+
+static void
+process_options(int argc, char *argv[])
+{
+    int opt;
+
+    opterr = 0;
+    while (-1 != (opt = getopt(argc, argv, "hd"))) {
+        switch (opt) {
+        case 'd':
+            debug_enabled = 1;
+            break;
+        case 'h':
+            usage();
+            exit(0);
+        default:
+            fprintf(stderr, "%s: FATAL: unknown option: -%c. Exiting\n", 
program_name, opt);
+            usage();
+            exit(1);
+        }
+    }
+}
+
+int
+main(int argc, char *argv[])
+{
+    char buf[HELPER_INPUT_BUFFER];
+    int buflen = 0;
+
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+
+    program_name = argv[0];
+
+    process_options(argc, argv);
+
+    debug("%s build " __DATE__ ", " __TIME__ " starting up...\n", 
program_name);
+
+    while (fgets(buf, HELPER_INPUT_BUFFER, stdin) != NULL) {
+        char *p;
+
+        if ((p = strchr(buf, '\n')) != NULL) {
+            *p = '\0';         /* strip \n */
+            buflen = p - buf;   /* length is known already */
+        } else
+            buflen = strlen(buf);   /* keep this so we only scan the buffer 
for \0 once per loop */
+
+        debug("Got %d bytes '%s' from Squid\n", buflen, buf);
+
+        /* send 'OK' result back to Squid */
+        fprintf(stdout, "OK user=fake/%s ttl=60\n", buf);
+    }
+    debug("%s build " __DATE__ ", " __TIME__ " shutting down...\n", 
program_name);
+    return 0;
+}

=== added file 'helpers/bearer_auth/fake/required.m4'
--- helpers/bearer_auth/fake/required.m4        1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/fake/required.m4        2014-05-21 06:20:59 +0000
@@ -0,0 +1 @@
+BUILD_HELPER="fake"

=== added file 'helpers/bearer_auth/modules.m4'
--- helpers/bearer_auth/modules.m4      1970-01-01 00:00:00 +0000
+++ helpers/bearer_auth/modules.m4      2014-05-21 06:24:16 +0000
@@ -0,0 +1,56 @@
+# This file is supposed to run all the tests required to identify which
+# configured modules are able to be built in this environment
+
+# FIXME: de-duplicate $enable_auth_bearer list containing double entries.
+
+#not specified. Inherit global
+if test "x$enable_auth_bearer" = "x"; then
+    enable_auth_bearer=$enable_auth
+fi
+#conflicts with global
+if test "x$enable_auth_bearer" != "xno" -a "x$enable_auth" = "xno" ; then
+    AC_MSG_ERROR([Bearer auth requested but auth disabled])
+fi
+#define list of modules to build
+if test "x$enable_auth_bearer" = "xyes" ; then
+    SQUID_LOOK_FOR_MODULES([$srcdir/helpers/bearer_auth],[enable_auth_bearer])
+fi
+#handle the "none" special case
+if test "x$enable_auth_bearer" = "xnone" ; then
+    enable_auth_bearer=""
+fi
+
+BEARER_AUTH_HELPERS=""
+#enable_auth_bearer contains either "no" or the list of modules to be built
+enable_auth_bearer="`echo $enable_auth_bearer| sed -e 's/,/ /g;s/  */ /g'`"
+if test "x$enable_auth_bearer" != "xno" ; then
+    AUTH_MODULES="$AUTH_MODULES bearer"
+    AC_DEFINE([HAVE_AUTH_MODULE_BEARER],1,[Bearer auth module is built])
+    for helper in $enable_auth_bearer; do
+      dir="$srcdir/helpers/bearer_auth/$helper"
+
+      # modules converted to autoconf macros already
+      # NP: we only need this list because m4_include() does not accept 
variables
+      if test "x$helper" = "xfake" ; then
+        m4_include([helpers/bearer_auth/fake/required.m4])
+
+      # modules not yet converted to autoconf macros (or third party drop-in's)
+      elif test -f "$dir/config.test" && sh "$dir/config.test" 
"$squid_host_os"; then
+        BUILD_HELPER="$helper"
+      fi
+
+      if test -d "$srcdir/helpers/bearer_auth/$helper"; then
+        if test "$BUILD_HELPER" != "$helper"; then
+          AC_MSG_NOTICE([Bearer auth helper $helper ... found but cannot be 
built])
+        else
+          BEARER_AUTH_HELPERS="$BEARER_AUTH_HELPERS $BUILD_HELPER"
+        fi
+      else
+        AC_MSG_ERROR([Bearer auth helper $helper ... not found])
+      fi
+    done
+fi
+
+AC_MSG_NOTICE([Bearer auth helpers to be built: $BEARER_AUTH_HELPERS])
+AM_CONDITIONAL(ENABLE_AUTH_BEARER, test "x$enable_auth_bearer" != "xno")
+AC_SUBST(BEARER_AUTH_HELPERS)

=== modified file 'src/AuthReg.cc'
--- src/AuthReg.cc      2012-08-31 16:57:39 +0000
+++ src/AuthReg.cc      2014-05-22 09:21:50 +0000
@@ -1,48 +1,55 @@
 #include "squid.h"
 
 #if USE_AUTH
 #include "AuthReg.h"
 
 #if HAVE_AUTH_MODULE_BASIC
 #include "auth/basic/Scheme.h"
 #endif
+#if HAVE_AUTH_MODULE_BEARER
+#include "auth/bearer/Scheme.h"
+#endif
 #if HAVE_AUTH_MODULE_DIGEST
 #include "auth/digest/Scheme.h"
 #endif
 #if HAVE_AUTH_MODULE_NEGOTIATE
 #include "auth/negotiate/Scheme.h"
 #endif
 #if HAVE_AUTH_MODULE_NTLM
 #include "auth/ntlm/Scheme.h"
 #endif
 
 #include "Debug.h"
 
 /**
  * Initialize the authentication modules (if any)
  * This is required once, before any configuration actions are taken.
  */
 void
 Auth::Init()
 {
     debugs(29,DBG_IMPORTANT,"Startup: Initializing Authentication Schemes 
...");
 #if HAVE_AUTH_MODULE_BASIC
     static const char *basic_type = Auth::Basic::Scheme::GetInstance()->type();
     debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" << 
basic_type << "'");
 #endif
+#if HAVE_AUTH_MODULE_BEARER
+    static const char *bearer_type = 
Auth::Bearer::Scheme::GetInstance()->type();
+    debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" << 
bearer_type << "'");
+#endif
 #if HAVE_AUTH_MODULE_DIGEST
     static const char *digest_type = 
Auth::Digest::Scheme::GetInstance()->type();
     debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" << 
digest_type << "'");
 #endif
 #if HAVE_AUTH_MODULE_NEGOTIATE
     static const char *negotiate_type = 
Auth::Negotiate::Scheme::GetInstance()->type();
     debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" << 
negotiate_type << "'");
 #endif
 #if HAVE_AUTH_MODULE_NTLM
     static const char *ntlm_type = Auth::Ntlm::Scheme::GetInstance()->type();
     debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication Scheme '" << 
ntlm_type << "'");
 #endif
     debugs(29,DBG_IMPORTANT,"Startup: Initialized Authentication.");
 }
 
 #endif /* USE_AUTH */

=== modified file 'src/auth/Makefile.am'
--- src/auth/Makefile.am        2013-10-31 04:39:36 +0000
+++ src/auth/Makefile.am        2014-05-22 09:21:41 +0000
@@ -1,25 +1,25 @@
 include $(top_srcdir)/src/Common.am
 include $(top_srcdir)/src/TestHeaders.am
 
 SUBDIRS = $(AUTH_MODULES)
-DIST_SUBDIRS = basic digest negotiate ntlm
+DIST_SUBDIRS = basic bearer digest negotiate ntlm
 
 noinst_LTLIBRARIES = libauth.la libacls.la
 ## not needed? $(AUTH_LIBS_TO_BUILD)
 ## EXTRA_LTLIBRARIES = libdigest.la libntlm.la libnegotiate.la
 
 ## authentication framework; this library is always built
 libauth_la_SOURCES = \
        Type.h \
        Type.cc \
        Config.cc \
        Config.h \
        CredentialState.cc \
        CredentialState.h \
        Gadgets.cc \
        Gadgets.h \
        QueueNode.h \
        Scheme.cc \
        Scheme.h \
        State.h \
        State.cc \

=== modified file 'src/auth/QueueNode.h'
--- src/auth/QueueNode.h        2013-03-25 05:41:24 +0000
+++ src/auth/QueueNode.h        2014-05-29 06:14:35 +0000
@@ -1,26 +1,31 @@
 #ifndef SQUID_SRC_AUTH_QUEUENODE_H
 #define SQUID_SRC_AUTH_QUEUENODE_H
 
+// NP: keep in sync with auth/UserRequest.h definition
+typedef void AUTHCB(void*);
+
 namespace Auth
 {
 
+class UserRequest;
+
 /**
  * A queue of auth requests waiting for verification to occur.
  *
  * Certain authentication schemes such a Basic and Bearer auth
  * permit credentials tokens to be repeated from multiple sources
  * simultaneously. This queue node allows multiple validation
  * queries to be collapsed into one backend helper lookup.
  * CBDATA and handlers stored in these queue nodes can be notified
  * all at once with a result when the lookup completes.
  */
 class QueueNode
 {
 
 private:
     // we store CBDATA here, copy is not safe
     QueueNode(const QueueNode &);
     QueueNode &operator =(const QueueNode &);
 
 public:
     QueueNode(Auth::UserRequest *aRequest, AUTHCB *aHandler, void *aData) :

=== modified file 'src/auth/Type.h'
--- src/auth/Type.h     2011-03-28 13:47:37 +0000
+++ src/auth/Type.h     2014-05-22 09:21:48 +0000
@@ -1,23 +1,24 @@
 #ifndef _SQUID__SRC_AUTH_AUTHTYPE_H
 #define _SQUID__SRC_AUTH_AUTHTYPE_H
 
 #if USE_AUTH
 
 namespace Auth
 {
 
 typedef enum {
     AUTH_UNKNOWN,               /* default */
     AUTH_BASIC,
+    AUTH_BEARER,
     AUTH_NTLM,
     AUTH_DIGEST,
     AUTH_NEGOTIATE,
     AUTH_BROKEN                 /* known type, but broken data */
 } Type;
 
 extern const char *Type_str[];
 
 }; // namespace Auth
 
 #endif /* USE_AUTH */
 #endif

=== added directory 'src/auth/bearer'
=== added file 'src/auth/bearer/Config.cc'
--- src/auth/bearer/Config.cc   1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Config.cc   2014-07-31 00:21:43 +0000
@@ -0,0 +1,253 @@
+/*
+ * DEBUG: section 29    Bearer Authentication
+ */
+#include "squid.h"
+#include "auth/bearer/Config.h"
+#include "auth/bearer/Scheme.h"
+#include "auth/bearer/Token.h"
+#include "auth/bearer/User.h"
+#include "auth/bearer/UserRequest.h"
+#include "base/CharacterSet.h"
+#include "cache_cf.h"
+#include "HttpHeaderTools.h"
+#include "HttpReply.h"
+#include "mgr/Registration.h"
+#include "Store.h"
+#include "wordlist.h"
+
+// Bearer Scheme statistics
+static AUTHSSTATS authenticateBearerStats;
+
+helper *bearerauthenticators = NULL;
+
+static int authbearer_initialised = 0;
+
+bool
+Auth::Bearer::Config::active() const
+{
+    return authbearer_initialised == 1;
+}
+
+bool
+Auth::Bearer::Config::configured() const
+{
+    if ((authenticateProgram != NULL) && (authenticateChildren.n_max != 0) &&
+            !realm.isEmpty() && (bearerAuthScope != NULL)) {
+        debugs(29, 9, "returning configured");
+        return true;
+    }
+
+    debugs(29, 9, "returning unconfigured");
+    return false;
+}
+
+const char *
+Auth::Bearer::Config::type() const
+{
+    return Auth::Bearer::Scheme::GetInstance()->type();
+}
+
+void
+Auth::Bearer::Config::fixHeader(Auth::UserRequest::Pointer, HttpReply *rep, 
http_hdr_type hdrType, HttpRequest *)
+{
+    if (authenticateProgram) {
+        debugs(29, 9, "Sending type:" << hdrType << " header: 'Bearer 
realm=\"" << realm << "\", scope=\"" << bearerAuthScope << "\"'");
+        httpHeaderPutStrf(&rep->header, hdrType, "Bearer realm=\"" SQUIDSBUFPH 
"\", scope=\"%s\"", SQUIDSBUFPRINT(realm), bearerAuthScope);
+    }
+}
+
+void
+Auth::Bearer::Config::rotateHelpers()
+{
+    /* schedule closure of existing helpers */
+    if (bearerauthenticators) {
+        helperShutdown(bearerauthenticators);
+    }
+
+    /* NP: dynamic helper restart will ensure they start up again as needed. */
+}
+
+/** shutdown the auth helpers and free any allocated configuration details */
+void
+Auth::Bearer::Config::done()
+{
+    Auth::Config::done();
+
+    authbearer_initialised = 0;
+
+    if (bearerauthenticators) {
+        helperShutdown(bearerauthenticators);
+    }
+
+    delete bearerauthenticators;
+    bearerauthenticators = NULL;
+
+    if (authenticateProgram)
+        wordlistDestroy(&authenticateProgram);
+
+    safe_free(bearerAuthScope);
+}
+
+void
+Auth::Bearer::Config::dump(StoreEntry * entry, const char *name, Auth::Config 
* scheme)
+{
+    if (!Auth::Config::dump(entry, name, scheme))
+        return false; // not configured
+
+    storeAppendPrintf(entry, "%s bearer scope %s\n", name, bearerAuthScope);
+    storeAppendPrintf(entry, "%s bearer cleanup_interval %" PRId64 " 
seconds\n", name, static_cast<int64_t>(tokenGCInterval));
+}
+
+Auth::Bearer::Config::Config()
+{
+    static const SBuf defaultRealm("Squid proxy-caching web server");
+    realm = defaultRealm;
+    bearerAuthScope = xstrdup("proxy:HTTP");
+}
+
+Auth::Bearer::Config::~Config()
+{
+    safe_free(bearerAuthScope);
+}
+
+void
+Auth::Bearer::Config::parse(Auth::Config * scheme, int n_configured, char 
*param_str)
+{
+    if (strcmp(param_str, "scope") == 0) {
+        parse_eol(&bearerAuthScope);
+    } else if (strcmp(param_str, "cleanup_interval") == 0) {
+        parse_time_t(&tokenGCInterval);
+    } else
+        Auth::Config::parse(scheme, n_configured, param_str);
+}
+
+static void
+authenticateBearerStats(StoreEntry * sentry)
+{
+    helperStats(sentry, bearerauthenticators, "Bearer Authenticator 
Statistics");
+}
+
+/**
+ * Decode a Bearer [Proxy-]Auth string. Looking for an existing
+ * Auth::UserRequest structure with matching token, or create a
+ * new one if needed.
+ *
+ * Note that just returning will be treated as
+ * "cannot decode credentials". Use the message field to return
+ * a descriptive message to the user.
+ */
+Auth::UserRequest::Pointer
+Auth::Bearer::Config::decode(char const *proxy_auth, const char *aRequestRealm)
+{
+    Auth::UserRequest::Pointer auth_user_request = 
dynamic_cast<Auth::UserRequest*>(new Auth::Bearer::UserRequest);
+
+    // retrieve the b68token
+    SBuf blob(proxy_auth);
+    if (!blob.isEmpty()) {
+        // trim prefix: WSP "Bearer" WSP
+        SBuf::size_type p = blob.findFirstNotOf(CharacterSet::WSP);
+        blob.consume(p);
+        p = blob.findFirstOf(CharacterSet::WSP);
+        blob.consume(p);
+        p = blob.findFirstNotOf(CharacterSet::WSP);
+        blob.consume(p);
+    }
+
+    // empty header? no auth details produced...
+    if (blob.isEmpty())
+        return auth_user_request;
+
+    // XXX: if the token contains non-b68token characters it is invalid.
+
+    Auth::User::Pointer auth_user;
+
+    // if there is a cached entry for this token use it
+    TokenCache::iterator itr = Auth::Bearer::Token::Cache.find(SBuf(blob));
+    if (!itr->second) {
+        // generate a User object for this token and cache it
+        Auth::Bearer::User *usr = new Auth::Bearer::User(this, aRequestRealm);
+        usr->token = new Token(blob);
+        usr->token->user = usr;
+        usr->auth_type = Auth::AUTH_BEARER;
+
+        Auth::Bearer::Token::Cache.insert(std::pair<const 
SBuf,Auth::Bearer::TokenPointer>(SBuf(blob), usr->token));
+        auth_user = usr;
+
+    } else
+        auth_user = itr->second->user;
+
+    debugs(29, 3, "Found Bearer token " << blob <<" , user=" << 
auth_user->username() << ", " << auth_user);
+
+    assert(auth_user != NULL);
+    auth_user_request->user(auth_user);
+    return auth_user_request;
+}
+
+/**
+ * Initialize helpers and the like for this auth scheme.
+ * Called AFTER parsing the config file
+ */
+void
+Auth::Bearer::Config::init(Auth::Config * schemeCfg)
+{
+    if (authenticateProgram && !authbearer_initialised) {
+        authbearer_initialised = 1;
+
+        if (bearerauthenticators == NULL)
+            bearerauthenticators = new helper("bearerauthenticator");
+
+        bearerauthenticators->cmdline = authenticateProgram;
+
+        bearerauthenticators->childs.updateLimits(authenticateChildren);
+
+        bearerauthenticators->ipc_type = IPC_STREAM;
+
+        helperOpenServers(bearerauthenticators);
+
+        TokenCacheCleanup(NULL); // kick-start garbage collection events
+    }
+}
+
+void
+Auth::Bearer::Config::registerWithCacheManager(void)
+{
+    Mgr::RegisterAction("bearerauthenticator",
+                        "Bearer User Authenticator Stats",
+                        authenticateBearerStats, 0, 1);
+}
+
+void
+Auth::Bearer::Config::TokenCacheCleanup(void *)
+{
+    debugs(29, 3, "Cleaning the token cache at time=" << current_time.tv_sec);
+
+    // only clear tokens out of cache after 1 hour stale
+    // could make this configurable
+    time_t stalenessLimit = current_time.tv_sec - 60*60;
+
+    /*
+     * For big caches we could consider stepping through
+     * 100/200 entries at a time. Lets see how it flies
+     * first.
+     */
+    Auth::Bearer::TokenCache *cache = &Auth::Bearer::Token::Cache;
+    for (Auth::Bearer::TokenCache::iterator itr = cache->begin(); itr != 
cache->end();) {
+        debugs(29, 3, "token " << itr->second->b68encoded
+               << ", user=" << itr->second->user->username()
+               << ", expires=" << itr->second->expires);
+
+        // remove if it is too old
+        if (itr->second->expires < stalenessLimit) {
+            debugs(29, 3, "token " << itr->second->b68encoded << " removed");
+            Auth::Bearer::TokenCache::iterator remove = itr++;
+            cache->erase(remove);
+        } else
+            ++itr;
+    }
+
+    debugs(29, 3, "Finished cleaning the token cache.");
+
+    const Auth::Bearer::Config *cfg = 
static_cast<Auth::Bearer::Config*>(Auth::Config::Find("bearer"));
+    if (cfg && cfg->active())
+        eventAdd("Bearer token cache maintenance", 
Auth::Bearer::Config::TokenCacheCleanup, NULL, cfg->tokenGCInterval, 1);
+}

=== added file 'src/auth/bearer/Config.h'
--- src/auth/bearer/Config.h    1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Config.h    2014-07-31 00:17:41 +0000
@@ -0,0 +1,48 @@
+#ifndef _SQUID_SRC_AUTH_BEARER_CONFIG_H
+#define _SQUID_SRC_AUTH_BEARER_CONFIG_H
+
+#include "auth/bearer/forward.h"
+#include "auth/Config.h"
+#include "auth/Gadgets.h"
+#include "auth/UserRequest.h"
+#include "helper.h"
+
+namespace Auth
+{
+namespace Bearer
+{
+
+/// Bearer authentication configuration data
+class Config : public Auth::Config
+{
+public:
+    Config();
+    ~Config();
+    virtual bool active() const;
+    virtual bool configured() const;
+    virtual Auth::UserRequest::Pointer decode(char const *proxy_auth, const 
char *requestRealm);
+    virtual void done();
+    virtual void rotateHelpers();
+    virtual void dump(StoreEntry *, const char *, Auth::Config *);
+    virtual void fixHeader(Auth::UserRequest::Pointer, HttpReply *, 
http_hdr_type, HttpRequest *);
+    virtual void init(Auth::Config *);
+    virtual void parse(Auth::Config *, int, char *);
+    void decode(char const *httpAuthHeader, Auth::UserRequest::Pointer);
+    virtual void registerWithCacheManager(void);
+    virtual const char * type() const;
+
+public:
+    char *bearerAuthScope;
+    time_t tokenGCInterval;
+
+private:
+    void tokenCacheSetup();
+    static void TokenCacheCleanup(void *);
+};
+
+} // namespace Bearer
+} // namespace Auth
+
+extern helper *bearerauthenticators;
+
+#endif /* _SQUID_SRC_AUTH_BEARER_CONFIG_H */

=== added file 'src/auth/bearer/Makefile.am'
--- src/auth/bearer/Makefile.am 1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Makefile.am 2014-07-30 12:25:52 +0000
@@ -0,0 +1,17 @@
+include $(top_srcdir)/src/Common.am
+include $(top_srcdir)/src/TestHeaders.am
+
+noinst_LTLIBRARIES = libbearer.la
+
+libbearer_la_SOURCES = \
+       Config.cc \
+       Config.h \
+       forward.h \
+       Scheme.cc \
+       Scheme.h \
+       Token.cc \
+       Token.h \
+       User.cc \
+       User.h \
+       UserRequest.cc \
+       UserRequest.h

=== added file 'src/auth/bearer/Scheme.cc'
--- src/auth/bearer/Scheme.cc   1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Scheme.cc   2014-05-22 09:21:34 +0000
@@ -0,0 +1,40 @@
+#include "squid.h"
+#include "auth/bearer/Config.h"
+#include "auth/bearer/Scheme.h"
+#include "Debug.h"
+#include "helper.h"
+
+Auth::Scheme::Pointer Auth::Bearer::Scheme::_instance = NULL;
+
+Auth::Scheme::Pointer
+Auth::Bearer::Scheme::GetInstance()
+{
+    if (_instance == NULL) {
+        _instance = new Auth::Bearer::Scheme();
+        AddScheme(_instance);
+    }
+    return _instance;
+}
+
+char const *
+Auth::Bearer::Scheme::type() const
+{
+    return "bearer";
+}
+
+void
+Auth::Bearer::Scheme::shutdownCleanup()
+{
+    if (_instance == NULL)
+        return;
+
+    _instance = NULL;
+    debugs(29, DBG_CRITICAL, "Shutdown: " << type() << " authentication.");
+}
+
+Auth::Config *
+Auth::Bearer::Scheme::createConfig()
+{
+    Auth::Bearer::Config *newCfg = new Auth::Bearer::Config;
+    return dynamic_cast<Auth::Config*>(newCfg);
+}

=== added file 'src/auth/bearer/Scheme.h'
--- src/auth/bearer/Scheme.h    1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Scheme.h    2014-05-27 01:41:29 +0000
@@ -0,0 +1,36 @@
+#ifndef SQUID_AUTH_BEARER_SCHEME_H
+#define SQUID_AUTH_BEARER_SCHEME_H
+
+#include "auth/Scheme.h"
+
+namespace Auth
+{
+namespace Bearer
+{
+
+/// scheme instance for OAuth 2.0 Bearer
+class Scheme : public Auth::Scheme
+{
+
+public:
+    static Auth::Scheme::Pointer GetInstance();
+    Scheme() {};
+    virtual ~Scheme() {}
+
+    /* per scheme */
+    virtual char const *type() const;
+    virtual void shutdownCleanup();
+    virtual Auth::Config *createConfig();
+
+private:
+    /* Not implemented */
+    Scheme(Scheme const &);
+    Scheme &operator=(Scheme const &);
+
+    static Auth::Scheme::Pointer _instance;
+};
+
+} // namespace Bearer
+} // namespace Auth
+
+#endif /* SQUID_AUTH_BEARER_SCHEME_H */

=== added file 'src/auth/bearer/Token.cc'
--- src/auth/bearer/Token.cc    1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Token.cc    2014-05-27 05:56:21 +0000
@@ -0,0 +1,9 @@
+#include "squid.h"
+#include "auth/bearer/Token.h"
+
+Auth::Bearer::TokenCache Auth::Bearer::Token::Cache;
+
+Auth::Bearer::Token::~Token()
+{
+    // TODO detatch this token from the Auth:User ??
+}

=== added file 'src/auth/bearer/Token.h'
--- src/auth/bearer/Token.h     1970-01-01 00:00:00 +0000
+++ src/auth/bearer/Token.h     2014-05-28 11:06:21 +0000
@@ -0,0 +1,43 @@
+#ifndef _SQUID_SRC_AUTH_BEARER_TOKEN_H
+#define _SQUID_SRC_AUTH_BEARER_TOKEN_H
+
+#include "auth/bearer/forward.h"
+#include "base/RefCount.h"
+#include "SquidTime.h"
+
+namespace Auth {
+namespace Bearer {
+
+/// a Bearer token we have seen and details we associate with it
+class Token : public RefCountable
+{
+public:
+    MEMPROXY_CLASS(Auth::Bearer::Token);
+
+    Token() : user(NULL), expires(squid_curtime) {}
+    explicit Token(const SBuf &token) : b68encoded(token), user(NULL), 
expires(squid_curtime) {}
+    ~Token();
+
+    /// the B68 encoding form of this token
+    SBuf b68encoded;
+
+    /// the Auth::User this token is tied to
+    Auth::Bearer::User *user;
+
+    /// when this token will expire
+    time_t expires;
+
+    /// a cache of known tokens
+    static TokenCache Cache;
+
+private:
+    Token(const Token &); // do not implement
+    Token &operator =(const Token&); // do not implement
+};
+
+MEMPROXY_CLASS_INLINE(Auth::Bearer::Token);
+
+} // namespace Bearer
+} // namespace Auth
+
+#endif /* _SQUID_SRC_AUTH_BEARER_TOKEN_H */

=== added file 'src/auth/bearer/User.cc'
--- src/auth/bearer/User.cc     1970-01-01 00:00:00 +0000
+++ src/auth/bearer/User.cc     2014-05-28 11:49:31 +0000
@@ -0,0 +1,28 @@
+#include "squid.h"
+#include "auth/bearer/User.h"
+#include "auth/bearer/UserRequest.h"
+#include "auth/Config.h"
+#include "auth/QueueNode.h"
+#include "Debug.h"
+
+Auth::Bearer::User::User(Auth::Config *aConfig, const char *aRequestRealm) :
+        Auth::User(aConfig, aRequestRealm),
+        queue(NULL)
+{
+}
+
+Auth::Bearer::User::~User()
+{
+    debugs(29, 5, "doing nothing to clear Bearer scheme data for " << this);
+}
+
+int32_t
+Auth::Bearer::User::ttl() const
+{
+    // no token? should never happen, but treat as expired
+    if (token == NULL)
+        return -1;
+
+    // credentials expiry depends on the Token age.
+    return static_cast<int32_t>(token->expires - squid_curtime);
+}

=== added file 'src/auth/bearer/User.h'
--- src/auth/bearer/User.h      1970-01-01 00:00:00 +0000
+++ src/auth/bearer/User.h      2014-05-27 01:45:19 +0000
@@ -0,0 +1,38 @@
+#ifndef _SQUID_AUTH_BEARER_USER_H
+#define _SQUID_AUTH_BEARER_USER_H
+
+#include "auth/bearer/Token.h"
+#include "auth/User.h"
+
+namespace Auth
+{
+
+class QueueNode;
+
+namespace Bearer
+{
+
+/// User credentials for the Bearer authentication protocol
+class User : public Auth::User
+{
+public:
+    MEMPROXY_CLASS(Auth::Bearer::User);
+
+    User(Auth::Config *, const char *requestRealm);
+    ~User();
+
+    virtual int32_t ttl() const;
+
+    /// authentication attempts waiting on helper feedback
+    QueueNode *queue;
+
+    /// the token used to create this User object
+    TokenPointer token;
+};
+
+MEMPROXY_CLASS_INLINE(Auth::Bearer::User);
+
+} // namespace Bearer
+} // namespace Auth
+
+#endif /* _SQUID_AUTH_BEARER_USER_H */

=== added file 'src/auth/bearer/UserRequest.cc'
--- src/auth/bearer/UserRequest.cc      1970-01-01 00:00:00 +0000
+++ src/auth/bearer/UserRequest.cc      2014-05-28 11:48:22 +0000
@@ -0,0 +1,251 @@
+#include "squid.h"
+#include "auth/bearer/Config.h"
+#include "auth/bearer/User.h"
+#include "auth/bearer/UserRequest.h"
+#include "auth/QueueNode.h"
+#include "auth/State.h"
+#include "cbdata.h"
+#include "Debug.h"
+
+Auth::Bearer::UserRequest::UserRequest()
+{
+}
+
+Auth::Bearer::UserRequest::~UserRequest()
+{
+}
+
+int
+Auth::Bearer::UserRequest::authenticated() const
+{
+    if (user() != NULL && user()->credentials() == Auth::Ok) {
+        debugs(29, 9, "user authenticated");
+        return 1;
+    }
+
+    debugs(29, 9, "user not fully authenticated");
+    return 0;
+}
+
+const char *
+Auth::Bearer::UserRequest::credentialsStr()
+{
+    static char buf[MAX_AUTHTOKEN_LEN];
+    buf[0] = '\0';
+
+    Auth::Bearer::User const *usr = dynamic_cast<Auth::Bearer::User const 
*>(user().getRaw());
+    if (usr)
+        snprintf(buf, sizeof(buf), SQUIDSBUFPH "\n", 
SQUIDSBUFPRINT(usr->token->b68encoded));
+    return buf;
+}
+
+Auth::Direction
+Auth::Bearer::UserRequest::module_direction()
+{
+    // null auth_user is checked for by Auth::UserRequest::direction()
+
+    if (user()->auth_type != Auth::AUTH_BEARER)
+        return Auth::CRED_ERROR;
+
+    switch (user()->credentials()) {
+
+    case Auth::Unchecked:
+    case Auth::Pending:
+        return Auth::CRED_LOOKUP;
+
+    case Auth::Ok:
+        if (user()->ttl() <= 0)
+            return Auth::CRED_LOOKUP;
+        return Auth::CRED_VALID;
+
+    case Auth::Failed:
+        return Auth::CRED_VALID;
+
+    default:
+        debugs(29, DBG_IMPORTANT, "WARNING: Bearer Authentication in 
unexpected state: " << user()->credentials());
+        return Auth::CRED_ERROR;
+    }
+}
+
+void
+Auth::Bearer::UserRequest::startHelperLookup(HttpRequest *req, 
AccessLogEntry::Pointer &al, AUTHCB * handler, void *data)
+{
+    assert(data);
+    assert(handler);
+
+    assert(user() != NULL);
+    assert(user()->auth_type == Auth::AUTH_BEARER);
+
+    if 
(static_cast<Auth::Bearer::Config*>(Auth::Config::Find("bearer"))->authenticateProgram
 == NULL) {
+        debugs(29, DBG_CRITICAL, "ERROR: No Bearer authentication program 
configured.");
+        handler(data);
+        return;
+    }
+
+    Auth::Bearer::User *bearer_auth = dynamic_cast<Auth::Bearer::User 
*>(user().getRaw());
+    assert(bearer_auth != NULL);
+
+    // check to see if the user already has a request outstanding
+    if (user()->credentials() == Auth::Pending) {
+
+        debugs(29, 8, "token " << bearer_auth->token->b68encoded << " queue 
after lookup already underway");
+        // there is a request with the same credentials already being verified
+        Auth::QueueNode *node = new Auth::QueueNode(this, handler, data);
+
+        // queue this validation request to be informed of the pending lookup 
results
+        node->next = bearer_auth->queue;
+        bearer_auth->queue = node;
+        return;
+    }
+    // otherwise submit this request to the auth helper(s) for validation
+
+    debugs(29, 8, "credentials state is " << user()->credentials());
+
+    static char buf[MAX_AUTHTOKEN_LEN];
+    int sz;
+    if (const char *keyExtras = helperRequestKeyExtras(req, al))
+        sz = snprintf(buf, sizeof(buf), SQUIDSBUFPH " %s\n", 
SQUIDSBUFPRINT(bearer_auth->token->b68encoded), keyExtras);
+    else
+        sz = snprintf(buf, sizeof(buf), SQUIDSBUFPH "\n", 
SQUIDSBUFPRINT(bearer_auth->token->b68encoded));
+
+    if (sz<=0) {
+        debugs(9, DBG_CRITICAL, "ERROR: Bearer Authentication Failure. Can not 
build helper validation request.");
+        handler(data);
+    } else if (static_cast<size_t>(sz) >= sizeof(buf)) {
+        debugs(9, DBG_CRITICAL, "ERROR: Bearer Authentication Failure. Helper 
request line exceeds " << sizeof(buf) << " bytes.");
+        handler(data);
+    } else {
+        user()->credentials(Auth::Pending);
+        debugs(29, 3, "token " << bearer_auth->token->b68encoded << " lookup 
started");
+        helperSubmit(bearerauthenticators, buf, 
Auth::Bearer::UserRequest::HandleReply,
+                     new Auth::StateData(this, handler, data));
+    }
+}
+
+void
+Auth::Bearer::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData 
* conn, http_hdr_type type)
+{
+    assert(user() != NULL);
+
+    // if the password is not ok, do an identity
+    if (!user() || user()->credentials() != Auth::Ok)
+        return;
+
+    // are we about to recheck the credentials externally?
+    if (user()->ttl() <= 0) {
+        debugs(29, 4, "credentials expired - rechecking");
+        user()->credentials(Auth::Unchecked);
+        return;
+    }
+
+    // we have been through the external helper, and the credentials haven't 
expired
+    debugs(29, 9, "user " << user()->username() << " authenticated");
+}
+
+void
+Auth::Bearer::UserRequest::HandleReply(void *data, const HelperReply &reply)
+{
+    Auth::StateData *r = static_cast<Auth::StateData *>(data);
+
+    debugs(29, 8, "helper: " << reply.whichServer << " sent us reply=" << 
reply);
+
+    if (!cbdataReferenceValid(r->data)) {
+        debugs(29, DBG_IMPORTANT, "ERROR: Bearer Authentication invalid 
callback data.");
+        delete r;
+        return;
+    }
+
+    Auth::UserRequest::Pointer auth_user_request = r->auth_user_request;
+    assert(auth_user_request != NULL);
+
+    // add new helper kv-pair notes to the credentials object
+    // so that any transaction using those credentials can access them
+    auth_user_request->user()->notes.appendNewOnly(&reply.notes);
+
+    assert(auth_user_request->user() != NULL);
+    assert(auth_user_request->user()->auth_type == Auth::AUTH_BEARER);
+
+    switch (reply.result) {
+    case HelperReply::Okay: {
+        const char *userNote = reply.notes.findFirst("user");
+        const char *ttlNote = reply.notes.findFirst("ttl");
+        if (userNote == NULL) {
+            /* protocol error */
+            fatalf("Auth::Bearer::HandleReply: *** Unsupported helper response 
***, '%s'\n", reply.other().content());
+            break;
+        }
+
+        Auth::Bearer::User *usr = dynamic_cast<Auth::Bearer::User 
*>(auth_user_request->user().getRaw());
+        /* we're finished, user is authenticated */
+        usr->username(userNote);
+        auth_user_request->denyMessage("Login successful");
+        const int64_t ttl = (ttlNote!= NULL) ? strtoll(ttlNote, NULL, 10) : -1;
+        usr->token->expires = current_time.tv_sec + ttl;
+        usr->expiretime = max(usr->expiretime, usr->token->expires);
+        usr->credentials(Auth::Ok);
+        usr->addToNameCache();
+        debugs(29, 4, "Successfully validated user via Bearer. Username " << 
auth_user_request->user()->username());
+    }
+    break;
+
+    case HelperReply::Error: {
+        const char *messageNote = reply.notes.find("message");
+        const char *ttlNote = reply.notes.findFirst("ttl");
+
+        /* authentication failure (wrong password, etc.) */
+        if (messageNote != NULL)
+            auth_user_request->denyMessage(messageNote);
+        else
+            auth_user_request->denyMessage("Bearer Authentication denied with 
no reason given");
+
+        Auth::Bearer::User *usr = dynamic_cast<Auth::Bearer::User 
*>(auth_user_request->user().getRaw());
+
+        const int64_t ttl = (ttlNote!= NULL) ? strtoll(ttlNote, NULL, 10) : -1;
+        usr->token->expires = current_time.tv_sec + ttl;
+        usr->expiretime = max(usr->expiretime, usr->token->expires);
+        usr->credentials(Auth::Failed);
+        debugs(29, 4, "Failed validating user via Bearer. Result: " << reply);
+    }
+    break;
+
+    case HelperReply::Unknown:
+        debugs(29, DBG_IMPORTANT, "ERROR: Bearer Authentication Helper '" << 
reply.whichServer << "' crashed!.");
+        /* continue to the next case */
+
+    case HelperReply::TT:
+        /* continue to the next case */
+
+    case HelperReply::BrokenHelper: {
+        const char *errNote = reply.notes.find("message");
+        if (reply.result == HelperReply::Unknown)
+            auth_user_request->denyMessage("Internal Error");
+        else if (errNote != NULL)
+            auth_user_request->denyMessage(errNote);
+        else
+            auth_user_request->denyMessage("Bearer Authentication failed with 
no reason given");
+        auth_user_request->user()->credentials(Auth::Failed);
+        debugs(29, DBG_IMPORTANT, "ERROR: Bearer Authentication validating 
user. Result: " << reply);
+    } // break;
+    }
+
+    // Notify all waiting transactions of the result
+    void *cbdata;
+    if (cbdataReferenceValidDone(r->data, &cbdata))
+        r->handler(cbdata);
+
+    cbdataReferenceDone(r->data);
+
+    Auth::Bearer::User *local_usr = dynamic_cast<Auth::Bearer::User 
*>(auth_user_request->user().getRaw());
+    while (local_usr->queue) {
+        if (cbdataReferenceValidDone(local_usr->queue->data, &cbdata))
+            local_usr->queue->handler(cbdata);
+
+        Auth::QueueNode *tmpnode = local_usr->queue->next;
+        local_usr->queue->next = NULL;
+        delete local_usr->queue;
+
+        local_usr->queue = tmpnode;
+    }
+
+    delete r;
+}

=== added file 'src/auth/bearer/UserRequest.h'
--- src/auth/bearer/UserRequest.h       1970-01-01 00:00:00 +0000
+++ src/auth/bearer/UserRequest.h       2014-05-28 11:48:12 +0000
@@ -0,0 +1,38 @@
+#ifndef _SQUID_SRC_AUTH_BEARER_USERREQUEST_H
+#define _SQUID_SRC_AUTH_BEARER_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+
+namespace Auth
+{
+namespace Bearer
+{
+
+class UserRequest : public Auth::UserRequest
+{
+public:
+    MEMPROXY_CLASS(Auth::Bearer::UserRequest);
+
+    UserRequest();
+    virtual ~UserRequest();
+    virtual int authenticated() const;
+    virtual void authenticate(HttpRequest * request, ConnStateData * conn, 
http_hdr_type type);
+    virtual Direction module_direction();
+    virtual void startHelperLookup(HttpRequest *request, 
AccessLogEntry::Pointer &al, AUTHCB *, void *);
+    virtual const char *credentialsStr();
+
+private:
+    static HLPCB HandleReply;
+};
+
+} // namespace Bearer
+} // namespace Auth
+
+MEMPROXY_CLASS_INLINE(Auth::Bearer::UserRequest);
+
+#endif /* _SQUID_SRC_AUTH_BEARER_USERREQUEST_H */

=== added file 'src/auth/bearer/forward.h'
--- src/auth/bearer/forward.h   1970-01-01 00:00:00 +0000
+++ src/auth/bearer/forward.h   2014-05-28 08:11:03 +0000
@@ -0,0 +1,29 @@
+#ifndef _SQUID_SRC_AUTH_BEARER_FORWARD_H
+#define _SQUID_SRC_AUTH_BEARER_FORWARD_H
+
+#include "base/RefCount.h"
+#include "SBuf.h"
+
+#include <map>
+
+namespace Auth {
+
+/** OAuth 2.0 Bearer token Authentication in HTTP
+ *
+ * RFC 6750 OAuth 2.0 Authorization Framework: Bearer Token Usage
+ * http://tools.ietf.org/rfc/rfc6750
+ */
+namespace Bearer {
+
+class Token;
+class User;
+class UserRequest;
+
+typedef RefCount<Auth::Bearer::Token> TokenPointer;
+
+typedef std::map<const SBuf, TokenPointer> TokenCache;
+
+} // namespace Bearer
+} // namespace Auth
+
+#endif /* _SQUID_SRC_AUTH_BEARER_FORWARD_H */

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre     2014-07-30 15:31:10 +0000
+++ src/cf.data.pre     2014-07-31 06:12:39 +0000
@@ -352,41 +352,41 @@
 
                The expanded key_extras value is added to the Squid credentials
                cache and, hence, will affect authentication. It can be used to
                autenticate different users with identical user names (e.g.,
                when user authentication depends on http_port).
 
                Avoid adding frequently changing information to key_extras. For
                example, if you add user source IP, and it changes frequently
                in your environment, then max_user_ip ACL is going to treat
                every user+IP combination as a unique "user", breaking the ACL
                and wasting a lot of memory on those user records. It will also
                force users to authenticate from scratch whenever their IP
                changes.
 
        "realm" string
                Specifies the protection scope (aka realm name) which is to be
                reported to the client for the authentication scheme. It is
                commonly part of the text the user will see when prompted for
                their username and password.
 
-               For Basic the default is "Squid proxy-caching web server".
+               For Basic and Bearer the default is "Squid proxy-caching web 
server".
                For Digest there is no default, this parameter is mandatory.
                For NTLM and Negotiate this parameter is ignored.
 
        "children" numberofchildren [startup=N] [idle=N] [concurrency=N]
 
                The maximum number of authenticator processes to spawn. If
                you start too few Squid will have to wait for them to process
                a backlog of credential verifications, slowing it down. When
                password verifications are done via a (slow) network you are
                likely to need lots of authenticator processes.
 
                The startup= and idle= options permit some skew in the exact
                amount run. A minimum of startup=N will begin during startup
                and reconfigure. Squid will start more in groups of up to
                idle=N in an attempt to meet traffic needs and to keep idle=N
                free above those traffic needs up to the maximum.
 
                The concurrency= option sets the number of concurrent requests
                the helper can process.  The default of 0 is used for helpers
                who only supports one request at a time. Setting this to a
@@ -414,40 +414,55 @@
        "credentialsttl" timetolive
                Specifies how long squid assumes an externally validated
                username:password pair is valid for - in other words how
                often the helper program is called for that user. Set this
                low to force revalidation with short lived passwords.
 
                NOTE: setting this high does not impact your susceptibility
                to replay attacks unless you are using an one-time password
                system (such as SecureID). If you are using such a system,
                you will be vulnerable to replay attacks unless you also
                use the max_user_ip ACL in an http_access rule.
 
        "casesensitive" on|off
                Specifies if usernames are case sensitive. Most user databases
                are case insensitive allowing the same username to be spelled
                using both lower and upper case letters, but some are case
                sensitive. This makes a big difference for user_max_ip ACL
                processing and similar.
 
 ENDIF
+IF HAVE_AUTH_MODULE_BEARER
+       === Bearer authentication parameters ===
+
+       "scope" scopestring
+               Specifies the scope name(s) which are to be reported to the
+               client for the Bearer proxy authentication scheme.
+               The default is:   proxy:HTTP
+
+       "cleanup_interval" timeinterval
+               Specifies the interval for garbage collection events which
+               remove excessively stale Bearer tokens. Tokens are deemed
+               excessively stale if they have not been seen for an hour or
+               more since their expiry time.
+
+ENDIF
 IF HAVE_AUTH_MODULE_DIGEST
        === Digest authentication parameters ===
 
        "utf8" on|off
                HTTP uses iso-latin-1 as character set, while some
                authentication backends such as LDAP expects UTF-8. If this is
                set to on Squid will translate the HTTP iso-latin-1 charset to
                UTF-8 before sending the username and password to the helper.
 
        "nonce_garbage_interval" timeinterval
                Specifies the interval that nonces that have been issued
                to client_agent's are checked for validity.
 
        "nonce_max_duration" timeinterval
                Specifies the maximum length of time a given nonce will be
                valid for.
 
        "nonce_max_count" number
                Specifies the maximum number of times a given nonce can be
                used.
@@ -492,40 +507,43 @@
                are supported by the proxy.
 ENDIF
 
        === Example Configuration ===
 
        This configuration displays the recommended authentication scheme
        order from most to least secure with recommended minimum configuration
        settings for each scheme:
 
 #auth_param negotiate program <uncomment and complete this line to activate>
 #auth_param negotiate children 20 startup=0 idle=1
 #auth_param negotiate keep_alive on
 #
 #auth_param digest program <uncomment and complete this line to activate>
 #auth_param digest children 20 startup=0 idle=1
 #auth_param digest realm Squid proxy-caching web server
 #auth_param digest nonce_garbage_interval 5 minutes
 #auth_param digest nonce_max_duration 30 minutes
 #auth_param digest nonce_max_count 50
 #
+#auth_param bearer program <uncomment and complete this line>
+#auth_param bearer scope proxy:squid
+#
 #auth_param ntlm program <uncomment and complete this line to activate>
 #auth_param ntlm children 20 startup=0 idle=1
 #auth_param ntlm keep_alive on
 #
 #auth_param basic program <uncomment and complete this line>
 #auth_param basic children 5 startup=5 idle=1
 #auth_param basic realm Squid proxy-caching web server
 #auth_param basic credentialsttl 2 hours
 DOC_END
 
 NAME: authenticate_cache_garbage_interval
 TYPE: time_t
 DEFAULT: 1 hour
 LOC: Config.authenticateGCInterval
 DOC_START
        The time period between garbage collection across the username cache.
        This is a trade-off between memory utilization (long intervals - say
        2 days) and CPU (short intervals - say 1 minute). Only change if you
        have good reason to.
 DOC_END

Reply via email to