Support multiple Auth modules in a single server or proxy config. Minor fixes to the OAuth2 module to comply with the spec.

git-svn-id: https://svn.apache.org/repos/asf/tuscany/sca-cpp/trunk@1308244 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/hosting/server/htdocs/app/index.html b/hosting/server/htdocs/app/index.html
index 0b01c1d..19fa748 100644
--- a/hosting/server/htdocs/app/index.html
+++ b/hosting/server/htdocs/app/index.html
@@ -74,8 +74,12 @@
     document.head.appendChild(ui.declareCSS(appcache.get('/ui-min.css')));
 })();
 
+</script>
+
+<script type="text/javascript">
+
 // Redirect to login page if not signed in
-if (document.location.protocol == 'https:' && !ui.signedin())
+if (document.location.protocol == 'https:' && !hasauthcookie())
     document.location = '/login/';
 
 </script>
diff --git a/hosting/server/htdocs/index.html b/hosting/server/htdocs/index.html
index 3bc1529..468461c 100644
--- a/hosting/server/htdocs/index.html
+++ b/hosting/server/htdocs/index.html
@@ -74,8 +74,11 @@
     document.head.appendChild(ui.declareCSS(appcache.get('/ui-min.css')));
 })();
 
+</script>
+<script type="text/javascript">
+
 // Redirect to login page if not signed in
-if (document.location.protocol == 'https:' && !ui.signedin())
+if (document.location.protocol == 'https:' && !hasauthcookie())
     document.location = '/login/';
 
 </script>
@@ -260,7 +263,7 @@
                         ui.menu(isNil(config.compose)? 'Composition' : config.compose, '/#view=graph&app=' + appname, '_view', view == 'graph'))),
         mklist(
             ui.menu('Account', '/#view=account', '_view', view == 'account'),
-            ui.signedin()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
+            hasauthcookie()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
 }
 
 /**
@@ -434,8 +437,7 @@
  */
 function logout() {
     // Clear session cookie and user-specific local storage entries
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     localStorage.removeItem('/r/EditWidget/accounts');
     localStorage.removeItem('/r/EditWidget/dashboards');
     //localStorage.clear();
diff --git a/hosting/server/htdocs/login/index.html b/hosting/server/htdocs/login/index.html
index 9052abe..359afc1 100644
--- a/hosting/server/htdocs/login/index.html
+++ b/hosting/server/htdocs/login/index.html
@@ -32,31 +32,50 @@
 
 <h1>Sign in</h1>
 
-<form name="openIDForm">
+<form name="googleOpenIDForm">
 <table border="0">
-<tr><td><b>Sign in with your Google account</b></td></tr>
-<tr><td><input type="button" value="Sign in" class="graybutton" style="font-weight: bold;" onclick="submitOpenIDSignin(withGoogle)"/></td></tr>
+<tr><td><b>Sign in with your Google account (using OpenID)</b></td></tr>
+<tr><td><input type="button" value="Sign in" class="graybutton" style="font-weight: bold;" onclick="submitOpenIDSignin(withGoogleOpenID)"/></td></tr>
 </table>
 </form>
 
-<form name="oauth2Form">
+<form name="facebookOAuth2Form">
 <table border="0">
-<tr><td><b>Sign in with your Facebook account</b></td></tr>
-<tr><td><input type="button"  value="Sign in" class="graybutton" style="font-weight: bold;" onclick="submitOAuth2Signin(withFacebook)"/></td></tr>
+<tr><td><b>Sign in with your Facebook account (using OAuth)</b></td></tr>
+<tr><td><input type="button" value="Sign in" class="graybutton" style="font-weight: bold;" onclick="submitOAuth2Signin(withFacebook)"/></td></tr>
 </table>
 </form>
 
+<form name="googleOAuth2Form">
+<table border="0">
+<tr><td><b>Sign in with your Google account (using OAuth)</b></td></tr>
+<tr><td><input type="button" value="Sign in" class="graybutton" style="font-weight: bold;" onclick="submitOAuth2Signin(withGoogleOAuth)"/></td></tr>
+</table>
+</form>
+
+<form name="formSignin" method="POST" action="/login/dologin">
+<table border="0">
+<tr><td colspan="2"><b>Sign in with your user id and password</b></td></tr>
+<tr><td>User id:</td><td><input type="text" name="httpd_username" value=""/></td></tr>
+<tr><td>Password:</td><td><input type="password" name="httpd_password" value=""/></td></tr>
+<tr><td><input type="button" class="graybutton" style="font-weight: bold;" onclick="submitFormSignin()" value="Sign in"/></td><td></td></tr>
+</table>
+</p>
+<input type="hidden" name="httpd_location" value="/"/>
+</form>
+
 <form name="openIDSignin" action="/" method="GET">
 <input type="hidden" name="openid_identifier" value=""/>
 </form>
 
-<form name="oauth2Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth2_authorize" value=""/>
-<input type="hidden" name="mod_oauth2_access_token" value=""/>
-<input type="hidden" name="mod_oauth2_client_id" value=""/>
-<input type="hidden" name="mod_oauth2_info" value=""/>
-<input type="hidden" name="mod_oauth2_display" value=""/>
-<input type="hidden" name="mod_oauth2_step" value="authorize"/>
+<form name="oauth2Signin" action="/oauth2/authorize/" method="GET">
+<input type="hidden" name="oauth2_authorize" value=""/>
+<input type="hidden" name="oauth2_access_token" value=""/>
+<input type="hidden" name="oauth2_client_id" value=""/>
+<input type="hidden" name="oauth2_info" value=""/>
+<input type="hidden" name="oauth2_display" value=""/>
+<input type="hidden" name="oauth2_scope" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 <script type="text/javascript">
@@ -85,8 +104,7 @@
  * Signin with OpenID.
  */
 function submitOpenIDSignin(w) {
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     localStorage.removeItem('/r/EditWidget/accounts');
     localStorage.removeItem('/r/EditWidget/dashboards');
     //localStorage.clear();
@@ -95,7 +113,7 @@
     document.openIDSignin.submit();
 }
 
-function withGoogle() {
+function withGoogleOpenID() {
     return 'https://www.google.com/accounts/o8/id';
 }
 
@@ -104,25 +122,40 @@
  */
 function submitOAuth2Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     localStorage.removeItem('/r/EditWidget/accounts');
     localStorage.removeItem('/r/EditWidget/dashboards');
     //localStorage.clear();
-    document.oauth2Signin.mod_oauth2_authorize.value = parms[0];
-    document.oauth2Signin.mod_oauth2_access_token.value = parms[1];
-    document.oauth2Signin.mod_oauth2_client_id.value = parms[2];
-    document.oauth2Signin.mod_oauth2_info.value = parms[3];
-    document.oauth2Signin.mod_oauth2_display.value = parms[4];
-    document.oauth2Signin.action = openauthReferrer();
+    document.oauth2Signin.oauth2_authorize.value = parms[0];
+    document.oauth2Signin.oauth2_access_token.value = parms[1];
+    document.oauth2Signin.oauth2_client_id.value = parms[2];
+    document.oauth2Signin.oauth2_info.value = parms[3];
+    document.oauth2Signin.oauth2_scope.value = parms[4];
+    document.oauth2Signin.oauth2_display.value = parms[5];
+    document.oauth2Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth2Signin.action = '/oauth2/authorize/';
     document.oauth2Signin.submit();
 }
 
 function withFacebook() {
-    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me', ui.isMobile()? 'touch' : 'page'];
+    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me', 'email', ui.isMobile()? 'touch' : 'page'];
     return parms;
 }
 
+function withGoogleOAuth() {
+    var parms = ['https://accounts.google.com/o/oauth2/auth', 'https://accounts.google.com/o/oauth2/token', 'google.com', 'https://www.googleapis.com/oauth2/v1/userinfo', 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile', ''];
+    return parms;
+}
+
+/**
+ * Signin with a userid and password.
+ */
+function submitFormSignin() {
+    clearauthcookie();
+    document.formSignin.httpd_location.value = '/';
+    document.formSignin.submit();
+}
+
 /**
  * Handle orientation change.
  */
diff --git a/hosting/server/htdocs/public/notauth/index.html b/hosting/server/htdocs/public/notauth/index.html
index 21f70f8..0c0435d 100644
--- a/hosting/server/htdocs/public/notauth/index.html
+++ b/hosting/server/htdocs/public/notauth/index.html
@@ -110,7 +110,7 @@
 function showmenu(mdiv) {
     mdiv.innerHTML = ui.menubar(
         mklist(ui.menu('Home', '/', '_view', false)),
-            mklist(ui.signedin()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
+            mklist(hasauthcookie()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
 }
 
 showmenu(mdiv);
@@ -121,8 +121,7 @@
  */
 function logout() {
     // Clear session cookie and user-specific local storage entries
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     localStorage.removeItem('/r/EditWidget/accounts');
     localStorage.removeItem('/r/EditWidget/dashboards');
     //localStorage.clear();
diff --git a/hosting/server/htdocs/public/notfound/index.html b/hosting/server/htdocs/public/notfound/index.html
index 839cc33..0b364b1 100644
--- a/hosting/server/htdocs/public/notfound/index.html
+++ b/hosting/server/htdocs/public/notfound/index.html
@@ -111,7 +111,7 @@
 function showmenu(mdiv) {
     mdiv.innerHTML = ui.menubar(
         mklist(ui.menu('Home', '/', '_view', false)),
-            mklist(ui.signedin()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
+            mklist(hasauthcookie()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
 }
 
 showmenu(mdiv);
@@ -122,8 +122,7 @@
  */
 function logout() {
     // Clear session cookie and user-specific local storage entries
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     localStorage.removeItem('/r/EditWidget/accounts');
     localStorage.removeItem('/r/EditWidget/dashboards');
     //localStorage.clear();
diff --git a/hosting/server/htdocs/public/notyet/index.html b/hosting/server/htdocs/public/notyet/index.html
index c014c72..11d25ee 100644
--- a/hosting/server/htdocs/public/notyet/index.html
+++ b/hosting/server/htdocs/public/notyet/index.html
@@ -111,7 +111,7 @@
 function showmenu(mdiv) {
     mdiv.innerHTML = ui.menubar(
         mklist(ui.menu('Home', '/', '_view', false)),
-            mklist(ui.signedin()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
+            mklist(hasauthcookie()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
 }
 
 showmenu(mdiv);
@@ -122,8 +122,7 @@
  */
 function logout() {
     // Clear session cookie and user-specific local storage entries
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     localStorage.removeItem('/r/EditWidget/accounts');
     localStorage.removeItem('/r/EditWidget/dashboards');
     //localStorage.clear();
diff --git a/hosting/server/htdocs/public/oops/index.html b/hosting/server/htdocs/public/oops/index.html
index 2ef7754..8d27c49 100644
--- a/hosting/server/htdocs/public/oops/index.html
+++ b/hosting/server/htdocs/public/oops/index.html
@@ -110,7 +110,7 @@
 function showmenu(mdiv) {
     mdiv.innerHTML = ui.menubar(
         mklist(ui.menu('Home', '/', '_view', false)),
-            mklist(ui.signedin()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
+            mklist(hasauthcookie()? ui.menufunc('Sign out', 'logout();', false) : ui.menu('Sign in', '/login/', '_self', false)));
 }
 
 showmenu(mdiv);
@@ -121,8 +121,7 @@
  */
 function logout() {
     // Clear session cookie and user-specific local storage entries
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     localStorage.removeItem('/r/EditWidget/accounts');
     localStorage.removeItem('/r/EditWidget/dashboards');
     //localStorage.clear();
diff --git a/hosting/server/ssl-start b/hosting/server/ssl-start
index 4689566..49d2b42 100755
--- a/hosting/server/ssl-start
+++ b/hosting/server/ssl-start
@@ -17,15 +17,15 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
-# For this module to work, add the example.com domain to your /etc/hosts as follows:
-# 127.0.0.1 example.com
+# For this module to work, add the www.example.com domain to your /etc/hosts as follows:
+# 127.0.0.1 www.example.com
 
 here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
 jsprefix=`echo "import os; print os.path.realpath('$here/../../modules/js')" | python`
 
 # Create SSL certificates
-../../modules/http/ssl-ca-conf tmp example.com
-../../modules/http/ssl-cert-conf tmp example.com server
+../../modules/http/ssl-ca-conf tmp www.example.com
+../../modules/http/ssl-cert-conf tmp www.example.com server
 
 # Configure and start logging
 if [ -x ../../components/log/scribe-cat ]; then
@@ -40,31 +40,32 @@
 ../../components/cache/memcached-start tmp 11212
 
 # Configure server
-../../modules/http/httpd-conf tmp example.com 8090 htdocs
+../../modules/http/httpd-conf tmp www.example.com 8090 htdocs
 ../../modules/http/httpd-event-conf tmp
 ../../modules/http/httpd-ssl-conf tmp 8453
 
-# Configure password authentication
-#../../modules/http/open-auth-conf tmp
-#../../modules/http/passwd-auth-conf tmp john john
-#../../modules/http/passwd-auth-conf tmp jane jane
-#../../modules/http/passwd-auth-conf tmp admin admin
-
-# Configure OAuth authentication
-# Configure your OAuth app keys here
-../../modules/oauth/oauth-conf tmp
-../../modules/oauth/oauth-memcached-conf tmp localhost 11212
-../../modules/oauth/oauth2-appkey-conf tmp facebook.com 12345 67890
-
 # Configure OpenID step2 authentication
 ../../modules/openid/openid-conf tmp
 ../../modules/openid/openid-step2-conf tmp
 ../../modules/openid/openid-memcached-conf tmp localhost 11212
 
+# Configure OAuth authentication
+# Configure your OAuth app keys here
+../../modules/oauth/oauth-conf tmp
+../../modules/oauth/oauth-memcached-conf tmp localhost 11212
+../../modules/oauth/oauth2-appkey-conf tmp facebook.com 12345 67890
+../../modules/oauth/oauth2-appkey-conf tmp google.com 12345 67890
+
+# Configure password authentication
+../../modules/http/open-auth-conf tmp
+../../modules/http/passwd-auth-conf tmp john john
+../../modules/http/passwd-auth-conf tmp jane jane
+../../modules/http/passwd-auth-conf tmp admin admin
+
 # Configure authorized users
-#../../modules/http/group-auth-conf tmp john
-#../../modules/http/group-auth-conf tmp jane
-#../../modules/http/group-auth-conf tmp admin
+../../modules/http/group-auth-conf tmp john
+../../modules/http/group-auth-conf tmp jane
+../../modules/http/group-auth-conf tmp admin
 # Configure the email addresses associated with your OpenID and OAuth ids here
 ../../modules/http/group-auth-conf tmp john@example.com
 ../../modules/http/group-auth-conf tmp jane@example.com
@@ -91,11 +92,11 @@
 
 EOF
 
-    cat >tmp/conf/mod-security-log.conf <<EOF
+#    cat >tmp/conf/mod-security-log.conf <<EOF
 # Generated by: ssl-start $*
-SecAuditLog "|$here/../../components/log/scribe-cat secaudit"
-
-EOF
+#SecAuditLog "|$here/../../components/log/scribe-cat secaudit"
+#
+#EOF
 
 else
     cat >tmp/conf/log.conf <<EOF
@@ -111,11 +112,11 @@
 
 EOF
 
-    cat >tmp/conf/mod-security-log.conf <<EOF
+#    cat >tmp/conf/mod-security-log.conf <<EOF
 # Generated by: ssl-start $*
-SecAuditLog $here/tmp/logs/secaudit_log secaudit
-
-EOF
+#SecAuditLog $here/tmp/logs/secaudit_log
+#
+#EOF
 
 fi
 
@@ -180,5 +181,6 @@
 EOF
 
 # Start server
+#../../modules/http/httpd-loglevel-conf tmp debug
 ../../modules/http/httpd-start tmp
 
diff --git a/modules/http/basic-auth-conf b/modules/http/basic-auth-conf
index 77ca054..39dde90 100755
--- a/modules/http/basic-auth-conf
+++ b/modules/http/basic-auth-conf
@@ -25,6 +25,11 @@
 conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
 host=`echo $conf | awk '{ print $6 }'`
 
+loc=$2
+if [ "$loc" = "" ]; then
+    loc="/"
+fi
+
 sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
 if [ "$sslconf" = "" ]; then
     sslsuffix=""
@@ -44,7 +49,7 @@
 # Generated by: basic-auth-conf $*
 # Require clients to present a userid + password for HTTP
 # basic authentication
-<Location />
+<Location $loc>
 AuthType Basic
 AuthName "$host"
 AuthBasicProvider file
diff --git a/modules/http/form-auth-conf b/modules/http/form-auth-conf
index 4ba3bec..2898d9b 100755
--- a/modules/http/form-auth-conf
+++ b/modules/http/form-auth-conf
@@ -25,7 +25,11 @@
 conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
 host=`echo $conf | awk '{ print $6 }'`
 
-pw=`cat $root/cert/ca.key | head -2 | tail -1`
+if [ "$2" = "" ]; then
+    pw=`cat $root/cert/ca.key | head -2 | tail -1`
+else
+    pw="$2"
+fi
 
 sslconf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-ssl-conf"`
 if [ "$sslconf" = "" ]; then
diff --git a/modules/http/htdocs/login/index.html b/modules/http/htdocs/login/index.html
index 99aeb31..5c5286f 100644
--- a/modules/http/htdocs/login/index.html
+++ b/modules/http/htdocs/login/index.html
@@ -32,8 +32,7 @@
 
 <script type="text/javascript">
 function submitFormSignin() {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.formSignin.httpd_location.value = '/';
     document.formSignin.submit();
 }
diff --git a/modules/http/htdocs/logout/index.html b/modules/http/htdocs/logout/index.html
index 4e7df1b..795f8f3 100644
--- a/modules/http/htdocs/logout/index.html
+++ b/modules/http/htdocs/logout/index.html
@@ -33,8 +33,7 @@
 <form name="signout" action="/login" method="GET">
 <script type="text/javascript">
 function submitSignout() {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.signout.submit();
     return true;
 }
diff --git a/modules/http/http.hpp b/modules/http/http.hpp
index 2334c1c..eb7a8d5 100644
--- a/modules/http/http.hpp
+++ b/modules/http/http.hpp
@@ -52,6 +52,11 @@
 namespace http {
 
 /**
+ * Enable CURL verbose debug log.
+ */
+//#define WANT_MAINTAINER_CURL_VERBOSE
+
+/**
  * CURL library runtime, one per process.
  */
 class CURLRuntime {
@@ -243,7 +248,7 @@
     CURL* ch = handle(cs);
     curl_easy_reset(ch);
     curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl/1.0");
-#ifdef WANT_MAINTAINER_MODE
+#ifdef WANT_MAINTAINER_CURL_VERBOSE
     curl_easy_setopt(ch, CURLOPT_VERBOSE, true);
 #endif
 
@@ -454,9 +459,9 @@
 /**
  * Find and return a header.
  */
-const failable<string> header(const char* prefix, const list<string>& h) {
+const maybe<string> header(const char* prefix, const list<string>& h) {
     if (isNil(h))
-        return mkfailure<string>(string("Couldn't find header: ") + prefix);
+        return maybe<string>();
     const string s = car(h);
     if (find(s, prefix) != 0)
         return header(prefix, cdr(h));
@@ -467,8 +472,9 @@
 /**
  * Find and return a location header.
  */
-const failable<string> location(const list<string>& h) {
-    return header("Location: ", h);
+const string location(const list<string>& h) {
+    const maybe<string> l = header("Location: ", h);
+    return hasContent(l)? content(l) : "";
 }
 
 /**
@@ -484,8 +490,9 @@
 /**
  * Find and return a content-type header.
  */
-const failable<string> contentType(const list<string>& h) {
-    return header("Content-Type: ", h);
+const string contentType(const list<string>& h) {
+    const maybe<string> ct = header("Content-Type: ", h);
+    return hasContent(ct)? content(ct) : "";
 }
 
 /**
@@ -516,44 +523,40 @@
 }
 
 /**
- * HTTP GET, return a list of values representing the resource at the given URL.
+ * Convert an HTTP content response to a value.
  */
-const failable<value> get(const string& url, const CURLSession& cs) {
-    debug(url, "http::get::url");
+const failable<value> responseValue(const list<list<string> > res) {
 
-    // Get the contents of the resource at the given URL
-    const failable<list<list<string> > > res = get<list<string> >(rcons<string>, list<string>(), url, cs);
-    if (!hasContent(res))
-        return mkfailure<value>(reason(res));
-    const string ct(content(contentType(car(content(res)))));
-    debug(ct, "http::get::contentType");
+    // Parse the returned content
+    const string ct = contentType(car(res));
+    debug(ct, "http::responseValue::contentType");
 
-    const list<string> ls(reverse(cadr(content(res))));
-    debug(ls, "http::get::content");
+    const list<string> ls(reverse(cadr(res)));
+    debug(ls, "http::responseValue::content");
 
     if (atom::isATOMEntry(ls)) {
         // Read an ATOM entry
         const value val(elementsToValues(content(atom::readATOMEntry(ls))));
-        debug(val, "http::get::result");
+        debug(val, "http::responseValue::result");
         return val;
     }
     if (contains(ct, "application/atom+xml") || atom::isATOMFeed(ls)) {
         // Read an ATOM feed
         const value val(elementsToValues(content(atom::readATOMFeed(ls))));
-        debug(val, "http::get::result");
+        debug(val, "http::responseValue::result");
         return val;
     }
     if (contains(ct, "application/rss+xml") || rss::isRSSFeed(ls)) {
         // Read an RSS feed
         const value val(elementsToValues(content(rss::readRSSFeed(ls))));
-        debug(val, "http::get::result");
+        debug(val, "http::responseValue::result");
         return val;
     }
     if (contains(ct, "text/javascript") || contains(ct, "application/json") || json::isJSON(ls)) {
         // Read a JSON document
         js::JSContext cx;
         const value val(json::jsonValues(content(json::readJSON(ls, cx))));
-        debug(val, "http::get::result");
+        debug(val, "http::responseValue::result");
         return val;
     }
     if (contains(ct, "application/x-javascript")) {
@@ -565,27 +568,42 @@
         const size_t fp = find(s, '(');
         const size_t lp = find_last(s, ')');
         const list<string> jls = mklist<string>(substr(s, fp + 1, lp - (fp + 1)));
-        debug(jls, "http::get::javascript::content");
+        debug(jls, "http::responseValue::javascript::content");
 
         js::JSContext cx;
         const value val(json::jsonValues(content(json::readJSON(jls, cx))));
-        debug(val, "http::get::result");
+        debug(val, "http::responseValue::result");
         return val;
     }
     if (contains(ct, "text/xml") || contains(ct, "application/xml") || isXML(ls)) {
         // Read an XML document
         const value val(elementsToValues(readXML(ls)));
-        debug(val, "http::get::result");
+        debug(val, "http::responseValue::result");
         return val;
     }
 
     // Return the content type and a content list
     const value val(mklist<value>(ct, mkvalues(ls)));
-    debug(val, "http::get::result");
+    debug(val, "http::responseValue::result");
     return val;
 }
 
 /**
+ * HTTP GET, return a list of values representing the resource at the given URL.
+ */
+const failable<value> get(const string& url, const CURLSession& cs) {
+    debug(url, "http::get::url");
+
+    // Get the contents of the resource at the given URL
+    const failable<list<list<string> > > res = get<list<string> >(rcons<string>, list<string>(), url, cs);
+    if (!hasContent(res))
+        return mkfailure<value>(reason(res));
+
+    // Parse the returned content
+    return responseValue(content(res));
+}
+
+/**
  * Form an HTTP content request.
  */
 const failable<list<list<string> > > writeRequest(const failable<list<string> >& ls, const string& ct) {
@@ -686,9 +704,15 @@
         return mkfailure<value>(reason(res));
 
     // Return the new entry id from the HTTP location header, if any
-    const value eid(entryId(location(car(content(res)))));
-    debug(eid, "http::post::result");
-    return eid;
+    const string loc = location(car(content(res)));
+    if (length(loc) != 0) {
+        const value eid(entryId(location(car(content(res)))));
+        debug(eid, "http::post::result");
+        return eid;
+    }
+
+    // Return the returned content
+    return responseValue(content(res));
 }
 
 /**
diff --git a/modules/http/httpd-conf b/modules/http/httpd-conf
index e5e7f27..f940073 100755
--- a/modules/http/httpd-conf
+++ b/modules/http/httpd-conf
@@ -35,6 +35,8 @@
     pportsuffix=":$pport"
 fi
 
+dothost=`echo $host | grep "\."`
+
 mkdir -p $4
 htdocs=`echo "import os; print os.path.realpath('$4')" | python`
 
@@ -85,10 +87,7 @@
 Include conf/log.conf
 
 # Configure tracking
-CookieTracking on
-CookieName TuscanyVisitorId
-CookieStyle Cookie
-CookieExpires 31556926
+Include conf/tracking.conf
 
 # Configure Mime types and default charsets
 TypesConfig $here/conf/mime.types
@@ -96,12 +95,12 @@
 AddCharset utf-8 .html .js .css
 
 # Configure cache control
-SetEnvIf Request_URI "^/app.html$" must-revalidate
-Header onsuccess set Cache-Control "max-age=604800" env=!must-revalidate
-Header set Cache-Control "must-revalidate, max-age=0" env=must-revalidate
-Header set Expires "Tue, 01 Jan 1980 00:00:00 GMT" env=must-revalidate
+<Directory />
+ExpiresActive On
+ExpiresDefault M604800
+</Directory>
 
-# Configuration auth modules
+# Configure auth modules
 Include conf/auth.conf
 
 # Set default document root
@@ -138,6 +137,7 @@
 <Location />
 RewriteEngine on
 RewriteCond %{HTTP_HOST} !^$host [NC]
+RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC]
 RewriteRule .* http://$host$pportsuffix%{REQUEST_URI} [R]
 </Location>
 
@@ -152,6 +152,26 @@
 
 EOF
 
+# Generate tracking configuration
+cat >$root/conf/tracking.conf <<EOF
+# Generated by: httpd-conf $*
+# Configure tracking
+CookieTracking on
+CookieName TuscanyVisitorId
+CookieStyle Cookie
+CookieExpires 31556926
+
+EOF
+
+if [ "$dothost" != "" ]; then
+    cat >>$root/conf/tracking.conf <<EOF
+# Generated by: httpd-conf $*
+CookieDomain .$dothost
+
+EOF
+
+fi
+
 # Configure logging
 cat >$root/conf/log.conf <<EOF
 # Generated by: httpd-conf $*
@@ -206,6 +226,7 @@
 LoadModule dir_module ${modules_prefix}/modules/mod_dir.so
 LoadModule setenvif_module ${modules_prefix}/modules/mod_setenvif.so
 LoadModule env_module ${modules_prefix}/modules/mod_env.so
+LoadModule expires_module ${modules_prefix}/modules/mod_expires.so
 <IfModule !log_config_module>
 LoadModule log_config_module ${modules_prefix}/modules/mod_log_config.so
 </IfModule>
@@ -223,7 +244,6 @@
 LoadModule reqtimeout_module ${modules_prefix}/modules/mod_reqtimeout.so
 
 LoadModule mod_tuscany_ssltunnel $here/libmod_tuscany_ssltunnel$libsuffix
-LoadModule mod_tuscany_openauth $here/libmod_tuscany_openauth$libsuffix
 
 EOF
 
diff --git a/modules/http/httpd-ssl-conf b/modules/http/httpd-ssl-conf
index 9bf9816..cb5ccfb 100755
--- a/modules/http/httpd-ssl-conf
+++ b/modules/http/httpd-ssl-conf
@@ -68,6 +68,7 @@
 <Location />
 RewriteEngine on
 RewriteCond %{HTTP_HOST} !^$host [NC]
+RewriteCond %{HTTP:X-Forwarded-Server} ^$ [NC]
 RewriteRule .* https://$host$sslpportsuffix%{REQUEST_URI} [R]
 </Location>
 
diff --git a/modules/http/httpd.hpp b/modules/http/httpd.hpp
index af075a0..718ed6e 100644
--- a/modules/http/httpd.hpp
+++ b/modules/http/httpd.hpp
@@ -44,14 +44,14 @@
 #include <http_connection.h>
 #include <http_request.h>
 // Ignore conversion warnings in HTTPD 2.3.15 header
-#ifdef WANT_MAINTAINER_MODE
+#ifdef WANT_MAINTAINER_WARNINGS
 #ifndef IS_DARWIN
 #pragma GCC diagnostic ignored "-Wconversion"
 #endif
 #endif
 #include <http_protocol.h>
 // Re-enable conversion warnings
-#ifdef WANT_MAINTAINER_MODE
+#ifdef WANT_MAINTAINER_WARNINGS
 #ifndef IS_DARWIN
 #pragma GCC diagnostic warning "-Wconversion"
 #endif
@@ -259,6 +259,8 @@
  * Convert a URI to an absolute URL.
  */
 const string url(const string& uri, request_rec* r) {
+    if (contains(uri, "://"))
+        return uri;
     ostringstream n;
     const string s = scheme(r);
     const string h = hostName(r, "localhost");
@@ -406,8 +408,8 @@
     const string ob(str(os));
 
     // Make sure browsers come back and check for updated dynamic content
-    // The actual header setup is configured in httpd-conf, based on the must-revalidate env variable
-    apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "must-revalidate"), apr_pstrdup(r->pool, "true"));
+    apr_table_set(r->headers_out, "Cache-Control", "must-revalidate, max-age=0");
+    apr_table_set(r->headers_out, "Expires", "Tue, 01 Jan 1980 00:00:00 GMT");
 
     // Compute and return an Etag for the returned content
     const string etag(ap_md5_binary(r->pool, (const unsigned char*)c_str(ob), (int)length(ob)));
@@ -658,7 +660,7 @@
     return v;
 }
 
-#ifdef WANT_MAINTAINER_MODE
+#ifdef WANT_MAINTAINER_LOG
 
 /**
  * Debug log.
@@ -701,8 +703,6 @@
  * Log a request.
  */
 const bool debugRequest(request_rec* r, const string& msg) {
-    if (!isDebugLog())
-        return true;
     gc_scoped_pool();
     cdebug << msg << ":" << endl;
     cdebug << "  unparsed uri: " << debugOptional(r->unparsed_uri) << endl;
@@ -725,11 +725,11 @@
     return true;
 }
 
-#define httpdDebugRequest(r, msg) httpd::debugRequest(r, msg)
+#define debug_httpdRequest(r, msg) if (debug_islogging()) httpd::debugRequest(r, msg)
 
 #else
 
-#define httpdDebugRequest(r, msg)
+#define debug_httpdRequest(r, msg)
 
 #endif
 
diff --git a/modules/http/mod-openauth.cpp b/modules/http/mod-openauth.cpp
index 09d62bf..b1aabd7 100644
--- a/modules/http/mod-openauth.cpp
+++ b/modules/http/mod-openauth.cpp
@@ -77,7 +77,7 @@
     string login;
 };
 
-#ifdef WANT_MAINTAINER_MODE
+#ifdef WANT_MAINTAINER_LOG
 
 /**
  * Log session entries.
@@ -88,12 +88,16 @@
 }
 
 const bool debugSession(request_rec* r, session_rec* z) {
-    if (!isDebugLog())
-        return true;
     apr_table_do(debugSessionEntry, r, z->entries, NULL);
     return true;
 }
 
+#define debug_authSession(r, z) if (debug_islogging()) openauth::debugSession(r, z)
+
+#else
+
+#define debug_authSession(r, z)
+
 #endif
 
 /**
@@ -110,9 +114,7 @@
     ap_session_load_fn(r, &z);
     if (z == NULL)
         return mkfailure<value>("Couldn't retrieve user session");
-#ifdef WANT_MAINTAINER_MODE
-    debugSession(r, z);
-#endif
+    debug_authSession(r, z);
 
     if (ap_session_get_fn == NULL)
         ap_session_get_fn = APR_RETRIEVE_OPTIONAL_FN(ap_session_get);
@@ -213,10 +215,10 @@
 
     // Create a scoped memory pool
     gc_scoped_pool pool(r->pool);
-    httpdDebugRequest(r, "modopenauth::checkAuthn::input");
+    debug_httpdRequest(r, "modopenauth::checkAuthn::input");
 
     // Get session id from the request
-    const maybe<string> sid = sessionID(r);
+    const maybe<string> sid = sessionID(r, "TuscanyOpenAuth");
     if (hasContent(sid)) {
         // Decline if the session id was not created by this module
         const string stype = substr(content(sid), 0, 7);
@@ -283,12 +285,12 @@
     // Decline if the request is for another authentication provider
     if (!isNil(assoc<value>("openid_identifier", args)))
         return DECLINED;
-    if (!isNil(assoc<value>("mod_oauth1_step", args)))
-        return DECLINED;
-    if (!isNil(assoc<value>("mod_oauth2_step", args)))
-        return DECLINED;
 
-    // Redirect to the login page
+    // Redirect to the login page, unless we have a session id from another module
+    if (hasContent(sessionID(r, "TuscanyOpenIDAuth")) ||
+        hasContent(sessionID(r, "TuscanyOAuth1")) ||
+        hasContent(sessionID(r, "TuscanyOAuth2")))
+        return DECLINED;
     r->ap_auth_type = const_cast<char*>(atype);
     return httpd::reportStatus(login(dc.login, r));
 }
diff --git a/modules/http/open-auth-conf b/modules/http/open-auth-conf
index 9c209b8..5226622 100755
--- a/modules/http/open-auth-conf
+++ b/modules/http/open-auth-conf
@@ -22,6 +22,13 @@
 mkdir -p $1
 root=`echo "import os; print os.path.realpath('$1')" | python`
 
+uname=`uname -s`
+if [ $uname = "Darwin" ]; then
+    libsuffix=".dylib"
+else
+    libsuffix=".so"
+fi
+
 conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"`
 host=`echo $conf | awk '{ print $6 }'`
 
@@ -32,7 +39,19 @@
     sslsuffix="-ssl"
 fi
 
-pw=`cat $root/cert/ca.key | head -2 | tail -1`
+if [ "$2" = "" ]; then
+    pw=`cat $root/cert/ca.key | head -2 | tail -1`
+else
+    pw="$2"
+fi
+
+# Configure HTTPD mod_tuscany_openauth module
+cat >>$root/conf/modules.conf <<EOF
+# Generated by: openauth-conf $*
+# Load support for Open authentication
+LoadModule mod_tuscany_openauth $here/libmod_tuscany_openauth$libsuffix
+
+EOF
 
 # Disallow public access to server resources
 cat >$root/conf/noauth$sslsuffix.conf <<EOF
diff --git a/modules/http/openauth.hpp b/modules/http/openauth.hpp
index e044a74..5d88788 100644
--- a/modules/http/openauth.hpp
+++ b/modules/http/openauth.hpp
@@ -47,7 +47,7 @@
         return cs;
     return cookieName(cs + 1);
 }
-const maybe<string> sessionID(const list<string> c) {
+const maybe<string> sessionID(const list<string>& c, const string& key) {
     if (isNil(c))
         return maybe<string>();
     const string cn = cookieName(c_str(car(c)));
@@ -55,29 +55,29 @@
     if (i < length(cn)) {
         const list<string> kv = mklist<string>(substr(cn, 0, i), substr(cn, i+1));
         if (!isNil(kv) && !isNil(cdr(kv))) {
-            if (car(kv) == "TuscanyOpenAuth")
+            if (car(kv) == key)
                 return cadr(kv);
         }
     }
-    return sessionID(cdr(c));
+    return sessionID(cdr(c), key);
 }
 
-const maybe<string> sessionID(const request_rec* r) {
+const maybe<string> sessionID(const request_rec* r, const string& key) {
     const string c = httpd::cookie(r);
     debug(c, "openauth::sessionid::cookies");
     if (length(c) == 0)
         return maybe<string>();
-    return sessionID(tokenize(";", c));
+    return sessionID(tokenize(";", c), key);
 }
 
 /**
  * Convert a session id to a cookie string.
  */
-const string cookie(const string& sid, const string& domain) {
+const string cookie(const string& key, const string& sid, const string& domain) {
     const time_t t = time(NULL) + 86400;
     char exp[32];
     strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t));
-    const string c = string("TuscanyOpenAuth=") + sid + "; expires=" + string(exp) + "; domain=." + domain + "; path=/";
+    const string c = key + string("=") + sid + "; expires=" + string(exp) + "; domain=." + domain + "; path=/";
     debug(c, "openauth::cookie");
     return c;
 }
diff --git a/modules/http/proxy-conf b/modules/http/proxy-conf
index 76e5b2f..4c445db 100755
--- a/modules/http/proxy-conf
+++ b/modules/http/proxy-conf
@@ -25,6 +25,9 @@
 cat >>$root/conf/vhost.conf <<EOF
 # Generated by: proxy-conf $*
 # Enable load balancing
+ProxyPass /balancer-manager !
+ProxyPass /server-status !
+ProxyPass /server-info !
 ProxyPass / balancer://cluster/
 
 <Proxy balancer://cluster>
@@ -37,5 +40,22 @@
 RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
 </Location>
 
+# Enable balancer manager
+<Location /balancer-manager>
+SetHandler balancer-manager
+HostnameLookups on
+Require user admin
+</Location> 
+
+EOF
+
+cat >>$root/conf/pubauth.conf <<EOF
+# Generated by: proxy-conf $*
+# Allow the server admin to manage the load balancer
+<Location /balancer-manager>
+HostnameLookups on
+Require user admin
+</Location>
+
 EOF
 
diff --git a/modules/http/proxy-ssl-conf b/modules/http/proxy-ssl-conf
index 7e8003d..d87aea6 100755
--- a/modules/http/proxy-ssl-conf
+++ b/modules/http/proxy-ssl-conf
@@ -26,6 +26,8 @@
 # Generated by: proxy-ssl-conf $*
 # Enable load balancing
 ProxyPass /balancer-manager !
+ProxyPass /server-status !
+ProxyPass /server-info !
 ProxyPass / balancer://sslcluster/
 
 <Proxy balancer://sslcluster>
@@ -33,6 +35,11 @@
 ProxySet lbmethod=byrequests
 </Proxy>
 
+<Location />
+RequestHeader set X-Forwarded-HTTPS %{HTTPS}s
+RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
+</Location>
+
 # Enable balancer manager
 <Location /balancer-manager>
 SetHandler balancer-manager
@@ -40,11 +47,6 @@
 Require user admin
 </Location> 
 
-<Location />
-RequestHeader set X-Forwarded-HTTPS %{HTTPS}s
-RequestHeader set X-Forwarded-Port %{SERVER_PORT}s
-</Location>
-
 EOF
 
 cat >>$root/conf/svhost-ssl.conf <<EOF
diff --git a/modules/js/htdocs/ui.js b/modules/js/htdocs/ui.js
index eabf851..dcc1462 100644
--- a/modules/js/htdocs/ui.js
+++ b/modules/js/htdocs/ui.js
@@ -193,13 +193,6 @@
 };
 
 /**
- * Return true if the session cookie contains signin information.
- */
-ui.signedin = function() {
-    return !isNil(document.cookie) && document.cookie.indexOf('TuscanyOpenAuth=') != -1;
-};
-
-/**
  * Convert a CSS position to a numeric position.
  */
 ui.numpos = function(p) {
diff --git a/modules/js/htdocs/util.js b/modules/js/htdocs/util.js
index 60c8a0c..677132d 100644
--- a/modules/js/htdocs/util.js
+++ b/modules/js/htdocs/util.js
@@ -334,6 +334,28 @@
 }
 
 /**
+ * Return true if the document cookie contains auth information.
+ */
+function hasauthcookie() {
+    return !isNil(document.cookie) &&
+        (document.cookie.indexOf('TuscanyOpenAuth=') != -1 ||
+         document.cookie.indexOf('TuscanyOAuth1=') != -1 ||
+         document.cookie.indexOf('TuscanyOAuth2=') != -1 ||
+         document.cookie.indexOf('TuscanyOpenIDAuth=') != -1);
+}
+
+/**
+ * Clear auth information from the document cookie.
+ */
+function clearauthcookie() {
+    document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    return true;
+}
+
+/**
  * Format a string like Python format.
  */
 function format() {
diff --git a/modules/oauth/htdocs/login/index.html b/modules/oauth/htdocs/login/index.html
index d1002f7..0243915 100644
--- a/modules/oauth/htdocs/login/index.html
+++ b/modules/oauth/htdocs/login/index.html
@@ -44,7 +44,7 @@
 function oauthReferrer() {
     r = queryParams()['openauth_referrer'];
     if (typeof(r) == 'undefined')
-        return r;
+        return '/';
     q = r.indexOf('?');
     if (q > 0)
         return r.substring(0, q);
@@ -55,38 +55,48 @@
     document.location = '/';
 }
 
+function clearauthcookie() {
+    document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    return true;
+}
+
 function submitSignin2(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
-    document.signin2.mod_oauth2_authorize.value = parms[0];
-    document.signin2.mod_oauth2_access_token.value = parms[1];
-    document.signin2.mod_oauth2_client_id.value = parms[2];
-    document.signin2.mod_oauth2_info.value = parms[3];
-    document.signin2.action = oauthReferrer();
+    clearauthcookie();
+    document.signin2.oauth2_authorize.value = parms[0];
+    document.signin2.oauth2_access_token.value = parms[1];
+    document.signin2.oauth2_client_id.value = parms[2];
+    document.signin2.oauth2_info.value = parms[3];
+    document.signin2.oauth2_scope.value = parms[4];
+    document.signin2.oauth2_display.value = parms[5];
+    document.signin2.openauth_referrer.value = oauthReferrer();
+    document.signin2.action = '/oauth2/authorize/';
     document.signin2.submit();
 }
 
 function withFacebook() {
-    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me'];
+    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me', 'email', 'page'];
     return parms;
 }
 
 function withGithub() {
-    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show'];
+    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show', 'email', ''];
     return parms;
 }
 
 function submitSignin1(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
-    document.signin1.mod_oauth1_request_token.value = parms[0];
-    document.signin1.mod_oauth1_authorize.value = parms[1];
-    document.signin1.mod_oauth1_access_token.value = parms[2];
-    document.signin1.mod_oauth1_client_id.value = parms[3];
-    document.signin1.mod_oauth1_info.value = parms[4];
-    document.signin1.action = oauthReferrer();
+    clearauthcookie();
+    document.signin1.oauth1_request_token.value = parms[0];
+    document.signin1.oauth1_authorize.value = parms[1];
+    document.signin1.oauth1_access_token.value = parms[2];
+    document.signin1.oauth1_client_id.value = parms[3];
+    document.signin1.oauth1_info.value = parms[4];
+    document.signin1.openauth_referrer.value = oauthReferrer();
+    document.signin1.action = '/oauth1/authorize/';
     document.signin1.submit();
 }
 
@@ -109,20 +119,22 @@
 </form>
 
 <form name="signin2" action="/" method="GET">
-<input type="hidden" name="mod_oauth2_authorize" value=""/>
-<input type="hidden" name="mod_oauth2_access_token" value=""/>
-<input type="hidden" name="mod_oauth2_client_id" value=""/>
-<input type="hidden" name="mod_oauth2_info" value=""/>
-<input type="hidden" name="mod_oauth2_step" value="authorize"/>
+<input type="hidden" name="oauth2_authorize" value=""/>
+<input type="hidden" name="oauth2_access_token" value=""/>
+<input type="hidden" name="oauth2_client_id" value=""/>
+<input type="hidden" name="oauth2_info" value=""/>
+<input type="hidden" name="oauth2_scope" value=""/>
+<input type="hidden" name="oauth2_display" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 <form name="signin1" action="/" method="GET">
-<input type="hidden" name="mod_oauth1_request_token" value=""/>
-<input type="hidden" name="mod_oauth1_authorize" value=""/>
-<input type="hidden" name="mod_oauth1_access_token" value=""/>
-<input type="hidden" name="mod_oauth1_client_id" value=""/>
-<input type="hidden" name="mod_oauth1_info" value=""/>
-<input type="hidden" name="mod_oauth1_step" value="authorize"/>
+<input type="hidden" name="oauth1_request_token" value=""/>
+<input type="hidden" name="oauth1_authorize" value=""/>
+<input type="hidden" name="oauth1_access_token" value=""/>
+<input type="hidden" name="oauth1_client_id" value=""/>
+<input type="hidden" name="oauth1_info" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 </body>
diff --git a/modules/oauth/htdocs/login/mixed.html b/modules/oauth/htdocs/login/mixed.html
index 57484dc..50d70f0 100644
--- a/modules/oauth/htdocs/login/mixed.html
+++ b/modules/oauth/htdocs/login/mixed.html
@@ -29,9 +29,16 @@
 <h1>Sign in with a Form, an OpenID provider or an OAuth provider</h1>
 
 <script type="text/javascript">
+function clearauthcookie() {
+    document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    return true;
+}
+
 function submitFormSignin() {
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     document.formSignin.httpd_location.value = '/';
     document.formSignin.submit();
 }
@@ -50,7 +57,7 @@
 function openauthReferrer() {
     r = queryParams()['openauth_referrer'];
     if (typeof(r) == 'undefined')
-        return r;
+        return '/';
     q = r.indexOf('?');
     if (q > 0)
         return r.substring(0, q);
@@ -62,8 +69,7 @@
 }
 
 function submitOpenIDSignin(w) {
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     document.openIDSignin.openid_identifier.value = w();
     document.openIDSignin.action = openauthReferrer();
     document.openIDSignin.submit();
@@ -111,36 +117,38 @@
 
 function submitOAuth2Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
-    document.oauth2Signin.mod_oauth2_authorize.value = parms[0];
-    document.oauth2Signin.mod_oauth2_access_token.value = parms[1];
-    document.oauth2Signin.mod_oauth2_client_id.value = parms[2];
-    document.oauth2Signin.mod_oauth2_info.value = parms[3];
-    document.oauth2Signin.action = openauthReferrer();
+    clearauthcookie();
+    document.oauth2Signin.oauth2_authorize.value = parms[0];
+    document.oauth2Signin.oauth2_access_token.value = parms[1];
+    document.oauth2Signin.oauth2_client_id.value = parms[2];
+    document.oauth2Signin.oauth2_info.value = parms[3];
+    document.oauth2Signin.oauth2_scope.value = parms[4];
+    document.oauth2Signin.oauth2_display.value = parms[5];
+    document.oauth2Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth2Signin.action = '/oauth2/authorize/';
     document.oauth2Signin.submit();
 }
 
 function withFacebook() {
-    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me'];
+    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me', 'email', 'page'];
     return parms;
 }
 
 function withGithub() {
-    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show'];
+    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show', 'email', ''];
     return parms;
 }
 
 function submitOAuth1Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
-    document.oauth1Signin.mod_oauth1_request_token.value = parms[0];
-    document.oauth1Signin.mod_oauth1_authorize.value = parms[1];
-    document.oauth1Signin.mod_oauth1_access_token.value = parms[2];
-    document.oauth1Signin.mod_oauth1_client_id.value = parms[3];
-    document.oauth1Signin.mod_oauth1_info.value = parms[4];
-    document.oauth1Signin.action = openauthReferrer();
+    clearauthcookie();
+    document.oauth1Signin.oauth1_request_token.value = parms[0];
+    document.oauth1Signin.oauth1_authorize.value = parms[1];
+    document.oauth1Signin.oauth1_access_token.value = parms[2];
+    document.oauth1Signin.oauth1_client_id.value = parms[3];
+    document.oauth1Signin.oauth1_info.value = parms[4];
+    document.oauth1Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth1Signin.action = '/oauth1/authorize/';
     document.oauth1Signin.submit();
 }
 
@@ -205,20 +213,22 @@
 </form>
 
 <form name="oauth2Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth2_authorize" value=""/>
-<input type="hidden" name="mod_oauth2_access_token" value=""/>
-<input type="hidden" name="mod_oauth2_client_id" value=""/>
-<input type="hidden" name="mod_oauth2_info" value=""/>
-<input type="hidden" name="mod_oauth2_step" value="authorize"/>
+<input type="hidden" name="oauth2_authorize" value=""/>
+<input type="hidden" name="oauth2_access_token" value=""/>
+<input type="hidden" name="oauth2_client_id" value=""/>
+<input type="hidden" name="oauth2_info" value=""/>
+<input type="hidden" name="oauth2_scope" value=""/>
+<input type="hidden" name="oauth2_display" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 <form name="oauth1Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth1_request_token" value=""/>
-<input type="hidden" name="mod_oauth1_authorize" value=""/>
-<input type="hidden" name="mod_oauth1_access_token" value=""/>
-<input type="hidden" name="mod_oauth1_client_id" value=""/>
-<input type="hidden" name="mod_oauth1_info" value=""/>
-<input type="hidden" name="mod_oauth1_step" value="authorize"/>
+<input type="hidden" name="oauth1_request_token" value=""/>
+<input type="hidden" name="oauth1_authorize" value=""/>
+<input type="hidden" name="oauth1_access_token" value=""/>
+<input type="hidden" name="oauth1_client_id" value=""/>
+<input type="hidden" name="oauth1_info" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 </body>
diff --git a/modules/oauth/htdocs/logout/index.html b/modules/oauth/htdocs/logout/index.html
index 35172da..a9d2e62 100644
--- a/modules/oauth/htdocs/logout/index.html
+++ b/modules/oauth/htdocs/logout/index.html
@@ -31,9 +31,16 @@
 
 <form name="signout" action="/login" method="GET">
 <script type="text/javascript">
+function clearauthcookie() {
+    document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    return true;
+}
+
 function submitSignout() {
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     document.signout.submit();
     return true;
 }
diff --git a/modules/oauth/mod-oauth1.cpp b/modules/oauth/mod-oauth1.cpp
index 1276ea4..29fe756 100644
--- a/modules/oauth/mod-oauth1.cpp
+++ b/modules/oauth/mod-oauth1.cpp
@@ -89,7 +89,7 @@
  * Return the user info for a session.
  */
 const failable<value> userInfo(const value& sid, const memcache::MemCached& mc) {
-    return memcache::get(mklist<value>("tuscanyOpenAuth", sid), mc);
+    return memcache::get(mklist<value>("tuscanyOAuth1", sid), mc);
 }
 
 /**
@@ -174,25 +174,28 @@
  */
 const failable<int> authorize(const list<list<value> >& args, request_rec* r, const list<list<value> >& appkeys, const memcache::MemCached& mc) {
     // Extract authorize, access_token, client ID and info URIs
-    const list<value> req = assoc<value>("mod_oauth1_request_token", args);
+    const list<value> ref = assoc<value>("openauth_referrer", args);
+    if (isNil(ref) || isNil(cdr(ref)))
+        return mkfailure<int>("Missing openauth_referrer parameter");
+    const list<value> req = assoc<value>("oauth1_request_token", args);
     if (isNil(req) || isNil(cdr(req)))
-        return mkfailure<int>("Missing mod_oauth1_request_token parameter");
-    const list<value> auth = assoc<value>("mod_oauth1_authorize", args);
+        return mkfailure<int>("Missing oauth1_request_token parameter");
+    const list<value> auth = assoc<value>("oauth1_authorize", args);
     if (isNil(auth) || isNil(cdr(auth)))
-        return mkfailure<int>("Missing mod_oauth1_authorize parameter");
-    const list<value> tok = assoc<value>("mod_oauth1_access_token", args);
+        return mkfailure<int>("Missing oauth1_authorize parameter");
+    const list<value> tok = assoc<value>("oauth1_access_token", args);
     if (isNil(tok) || isNil(cdr(tok)))
-        return mkfailure<int>("Missing mod_oauth1_access_token parameter");
-    const list<value> cid = assoc<value>("mod_oauth1_client_id", args);
+        return mkfailure<int>("Missing oauth1_access_token parameter");
+    const list<value> cid = assoc<value>("oauth1_client_id", args);
     if (isNil(cid) || isNil(cdr(cid)))
-        return mkfailure<int>("Missing mod_oauth1_client_id parameter");
-    const list<value> info = assoc<value>("mod_oauth1_info", args);
+        return mkfailure<int>("Missing oauth1_client_id parameter");
+    const list<value> info = assoc<value>("oauth1_info", args);
     if (isNil(info) || isNil(cdr(info)))
-        return mkfailure<int>("Missing mod_oauth1_info parameter");
+        return mkfailure<int>("Missing oauth1_info parameter");
 
     // Build the redirect URI
-    const list<list<value> > redirargs = mklist<list<value> >(mklist<value>("mod_oauth1_step", "access_token"), tok, cid, info);
-    const string redir = httpd::url(r->uri, r) + string("?") + http::queryString(redirargs);
+    const list<list<value> > redirargs = mklist<list<value> >(tok, cid, info, ref);
+    const string redir = httpd::url("/oauth1/access_token/", r) + string("?") + http::queryString(redirargs);
     debug(redir, "modoauth1::authorize::redir");
 
     // Lookup client app configuration
@@ -296,15 +299,18 @@
  */
 const failable<int> accessToken(const list<list<value> >& args, request_rec* r, const list<list<value> >& appkeys, const memcache::MemCached& mc) {
     // Extract access_token URI, client ID and verification code
-    const list<value> tok = assoc<value>("mod_oauth1_access_token", args);
+    const list<value> ref = assoc<value>("openauth_referrer", args);
+    if (isNil(ref) || isNil(cdr(ref)))
+        return mkfailure<int>("Missing openauth_referrer parameter");
+    const list<value> tok = assoc<value>("oauth1_access_token", args);
     if (isNil(tok) || isNil(cdr(tok)))
-        return mkfailure<int>("Missing mod_oauth1_access_token parameter");
-    const list<value> cid = assoc<value>("mod_oauth1_client_id", args);
+        return mkfailure<int>("Missing oauth1_access_token parameter");
+    const list<value> cid = assoc<value>("oauth1_client_id", args);
     if (isNil(cid) || isNil(cdr(cid)))
-        return mkfailure<int>("Missing mod_oauth1_client_id parameter");
-    const list<value> info = assoc<value>("mod_oauth1_info", args);
+        return mkfailure<int>("Missing oauth1_client_id parameter");
+    const list<value> info = assoc<value>("oauth1_info", args);
     if (isNil(info) || isNil(cdr(info)))
-        return mkfailure<int>("Missing mod_oauth1_info parameter");
+        return mkfailure<int>("Missing oauth1_info parameter");
     const list<value> tv = assoc<value>("oauth_token", args);
     if (isNil(tv) || isNil(cdr(tv)))
         return mkfailure<int>("Missing oauth_token parameter");
@@ -372,14 +378,14 @@
 
     // Store user info in memcached keyed by session ID
     const value sid = string("OAuth1_") + mkrand();
-    const failable<bool> prc = memcache::put(mklist<value>("tuscanyOpenAuth", sid), content(iv), mc);
+    const failable<bool> prc = memcache::put(mklist<value>("tuscanyOAuth1", sid), content(iv), mc);
     if (!hasContent(prc))
         return mkfailure<int>(reason(prc));
 
     // Send session ID to the client in a cookie
-    debug(c_str(openauth::cookie(sid, httpd::hostName(r))), "modoauth1::access_token::setcookie");
-    apr_table_set(r->err_headers_out, "Set-Cookie", c_str(openauth::cookie(sid, httpd::hostName(r))));
-    return httpd::externalRedirect(httpd::url(r->uri, r), r);
+    debug(c_str(openauth::cookie("TuscanyOAuth1", sid, httpd::hostName(r))), "modoauth1::access_token::setcookie");
+    apr_table_set(r->err_headers_out, "Set-Cookie", c_str(openauth::cookie("TuscanyOAuth1", sid, httpd::hostName(r))));
+    return httpd::externalRedirect(httpd::url(httpd::unescape(cadr(ref)), r), r);
 }
 
 /**
@@ -391,6 +397,7 @@
     if (!dc.enabled)
         return DECLINED;
     const char* atype = ap_auth_type(r);
+    debug(atype, "modopenauth::checkAuthn::auth_type");
     if (atype == NULL || strcasecmp(atype, "Open"))
         return DECLINED;
 
@@ -398,11 +405,11 @@
     gc_scoped_pool pool(r->pool);
 
     // Get the server configuration
-    httpdDebugRequest(r, "modoauth1::checkAuthn::input");
+    debug_httpdRequest(r, "modoauth1::checkAuthn::input");
     const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth1);
 
     // Get session id from the request
-    const maybe<string> sid = openauth::sessionID(r);
+    const maybe<string> sid = openauth::sessionID(r, "TuscanyOAuth1");
     if (hasContent(sid)) {
         // Decline if the session id was not created by this module
         if (substr(content(sid), 0, 7) != "OAuth1_")
@@ -416,33 +423,25 @@
         }
     }
 
-    // Get the request args
-    const list<list<value> > args = httpd::queryArgs(r);
-
-    // Decline if the request is for another authentication provider
-    if (!isNil(assoc<value>("openid_identifier", args)))
-        return DECLINED;
-    if (!isNil(assoc<value>("mod_oauth2_step", args)))
-        return DECLINED;
-
-    // Determine the OAuth protocol flow step, conveniently passed
-    // around in a request arg
-    const list<value> sl = assoc<value>("mod_oauth1_step", args);
-    const value step = !isNil(sl) && !isNil(cdr(sl))? cadr(sl) : "";
-
     // Handle OAuth authorize request step
-    if (step == "authorize") {
+    if (string(r->uri) == "/oauth1/authorize/") {
         r->ap_auth_type = const_cast<char*>(atype);
-        return httpd::reportStatus(authorize(args, r, sc.appkeys, sc.mc));
+        return httpd::reportStatus(authorize(httpd::queryArgs(r), r, sc.appkeys, sc.mc));
     }
 
     // Handle OAuth access_token request step
-    if (step == "access_token") {
+    if (string(r->uri) == "/oauth1/access_token/") {
         r->ap_auth_type = const_cast<char*>(atype);
-        return httpd::reportStatus(accessToken(args, r, sc.appkeys, sc.mc));
+        return httpd::reportStatus(accessToken(httpd::queryArgs(r), r, sc.appkeys, sc.mc));
     }
 
-    // Redirect to the login page
+    // Redirect to the login page, unless we have a session id from another module
+    if (hasContent(openauth::sessionID(r, "TuscanyOpenIDAuth")) ||
+        hasContent(openauth::sessionID(r, "TuscanyOpenAuth")) ||
+        hasContent(openauth::sessionID(r, "TuscanyOAuth2")))
+        return DECLINED;
+    if ((substr(string(r->uri), 0, 8) == "/oauth2/") || !isNil(assoc<value>("openid_identifier", httpd::queryArgs(r))))
+        return DECLINED;
     r->ap_auth_type = const_cast<char*>(atype);
     return httpd::reportStatus(openauth::login(dc.login, r));
 }
diff --git a/modules/oauth/mod-oauth2.cpp b/modules/oauth/mod-oauth2.cpp
index ace611d..dbede7e 100644
--- a/modules/oauth/mod-oauth2.cpp
+++ b/modules/oauth/mod-oauth2.cpp
@@ -83,7 +83,7 @@
  * Return the user info for a session.
  */
 const failable<value> userInfo(const value& sid, const memcache::MemCached& mc) {
-    return memcache::get(mklist<value>("tuscanyOpenAuth", sid), mc);
+    return memcache::get(mklist<value>("tuscanyOAuth2", sid), mc);
 }
 
 /**
@@ -126,25 +126,35 @@
  */
 const failable<int> authorize(const list<list<value> >& args, request_rec* r, const list<list<value> >& appkeys) {
     // Extract authorize, access_token, client ID and info URIs
-    const list<value> auth = assoc<value>("mod_oauth2_authorize", args);
+    const list<value> ref = assoc<value>("openauth_referrer", args);
+    if (isNil(ref) || isNil(cdr(ref)))
+        return mkfailure<int>("Missing openauth_referrer parameter");
+    const list<value> auth = assoc<value>("oauth2_authorize", args);
     if (isNil(auth) || isNil(cdr(auth)))
-        return mkfailure<int>("Missing mod_oauth2_authorize parameter");
-    const list<value> tok = assoc<value>("mod_oauth2_access_token", args);
+        return mkfailure<int>("Missing oauth2_authorize parameter");
+    const list<value> tok = assoc<value>("oauth2_access_token", args);
     if (isNil(tok) || isNil(cdr(tok)))
-        return mkfailure<int>("Missing mod_oauth2_access_token parameter");
-    const list<value> cid = assoc<value>("mod_oauth2_client_id", args);
+        return mkfailure<int>("Missing oauth2_access_token parameter");
+    const list<value> cid = assoc<value>("oauth2_client_id", args);
     if (isNil(cid) || isNil(cdr(cid)))
-        return mkfailure<int>("Missing mod_oauth2_client_id parameter");
-    const list<value> info = assoc<value>("mod_oauth2_info", args);
+        return mkfailure<int>("Missing oauth2_client_id parameter");
+    const list<value> info = assoc<value>("oauth2_info", args);
     if (isNil(info) || isNil(cdr(info)))
-        return mkfailure<int>("Missing mod_oauth2_info parameter");
-    const list<value> display = assoc<value>("mod_oauth2_display", args);
+        return mkfailure<int>("Missing oauth2_info parameter");
+    const list<value> scope = assoc<value>("oauth2_scope", args);
+    if (isNil(scope) || isNil(cdr(scope)))
+        return mkfailure<int>("Missing oauth2_scope parameter");
+    const list<value> display = assoc<value>("oauth2_display", args);
 
     // Build the redirect URI
-    const list<list<value> > rargs = mklist<list<value> >(mklist<value>("mod_oauth2_step", "access_token"), tok, cid, info);
-    const string redir = httpd::url(r->uri, r) + string("?") + http::queryString(rargs);
+    const string redir = httpd::url("/oauth2/access_token/", r);
     debug(redir, "modoauth2::authorize::redir");
 
+    // Build the state URI
+    const list<list<value> > stargs = mklist<list<value> >(tok, cid, info, ref);
+    const string state = http::queryString(stargs);
+    debug(state, "modoauth2::authorize::state");
+
     // Lookup client app configuration
     const list<value> app = assoc<value>(cadr(cid), appkeys);
     if (isNil(app) || isNil(cdr(app)))
@@ -153,7 +163,7 @@
 
     // Redirect to the authorize URI
     const list<value> adisplay = (isNil(display) || isNil(cdr(display)))? list<value>() : mklist<value>("display", cadr(display));
-    const list<list<value> > aargs = mklist<list<value> >(mklist<value>("client_id", car(appkey)), mklist<value>("scope", "email"), adisplay, mklist<value>("redirect_uri", httpd::escape(redir)));
+    const list<list<value> > aargs = mklist<list<value> >(mklist<value>("response_type", "code"), mklist<value>("client_id", car(appkey)), mklist<value>("scope", cadr(scope)), adisplay, mklist<value>("redirect_uri", httpd::escape(redir)), mklist<value>("state", httpd::escape(state)));
     const string uri = httpd::unescape(cadr(auth)) + string("?") + http::queryString(aargs);
     debug(uri, "modoauth2::authorize::uri");
     return httpd::externalRedirect(uri, r);
@@ -172,16 +182,23 @@
  * Handle an access_token request.
  */
 const failable<int> accessToken(const list<list<value> >& args, request_rec* r, const list<list<value> >& appkeys, const perthread_ptr<http::CURLSession>& cs, const memcache::MemCached& mc) {
-    // Extract access_token URI, client ID and authorization code
-    const list<value> tok = assoc<value>("mod_oauth2_access_token", args);
+    // Extract access_token URI, client ID and authorization code parameters
+    const list<value> state = assoc<value>("state", args);
+    if (isNil(state) || isNil(cdr(state)))
+        return mkfailure<int>("Missing state parameter");
+    const list<list<value> >& stargs = httpd::queryArgs(httpd::unescape(cadr(state)));
+    const list<value> ref = assoc<value>("openauth_referrer", stargs);
+    if (isNil(ref) || isNil(cdr(ref)))
+        return mkfailure<int>("Missing openauth_referrer parameter");
+    const list<value> tok = assoc<value>("oauth2_access_token", stargs);
     if (isNil(tok) || isNil(cdr(tok)))
-        return mkfailure<int>("Missing mod_oauth2_access_token parameter");
-    const list<value> cid = assoc<value>("mod_oauth2_client_id", args);
+        return mkfailure<int>("Missing oauth2_access_token parameter");
+    const list<value> cid = assoc<value>("oauth2_client_id", stargs);
     if (isNil(cid) || isNil(cdr(cid)))
-        return mkfailure<int>("Missing mod_oauth2_client_id parameter");
-    const list<value> info = assoc<value>("mod_oauth2_info", args);
+        return mkfailure<int>("Missing oauth2_client_id parameter");
+    const list<value> info = assoc<value>("oauth2_info", stargs);
     if (isNil(info) || isNil(cdr(info)))
-        return mkfailure<int>("Missing mod_oauth2_info parameter");
+        return mkfailure<int>("Missing oauth2_info parameter");
     const list<value> code = assoc<value>("code", args);
     if (isNil(code) || isNil(cdr(code)))
         return mkfailure<int>("Missing code parameter");
@@ -193,19 +210,26 @@
     list<value> appkey = cadr(app);
 
     // Build the redirect URI
-    const list<list<value> > rargs = mklist<list<value> >(mklist<value>("mod_oauth2_step", "access_token"), tok, cid, info);
-    const string redir = httpd::url(r->uri, r) + string("?") + http::queryString(rargs);
+    const string redir = httpd::url("/oauth2/access_token/", r);
     debug(redir, "modoauth2::access_token::redir");
 
     // Request access token
-    const list<list<value> > targs = mklist<list<value> >(mklist<value>("client_id", car(appkey)), mklist<value>("redirect_uri", httpd::escape(redir)), mklist<value>("client_secret", cadr(appkey)), code);
-    const string turi = httpd::unescape(cadr(tok)) + string("?") + http::queryString(targs);
+    const list<list<value> > targs = mklist<list<value> >(mklist<value>("client_id", car(appkey)), mklist<value>("redirect_uri", httpd::escape(redir)), mklist<value>("client_secret", cadr(appkey)), code, mklist<value>("grant_type", "authorization_code"));
+    const string tqs = http::queryString(targs);
+    debug(tqs, "modoauth2::access_token::tokenqs");
+    const string turi = httpd::unescape(cadr(tok));
     debug(turi, "modoauth2::access_token::tokenuri");
-    const failable<value> tr = http::get(turi, *(cs));
-    if (!hasContent(tr))
-        return mkfailure<int>(reason(tr));
+    const value tval = mklist<value>(string("application/x-www-form-urlencoded;charset=UTF-8"), mklist<value>(tqs));
+    const failable<value> ftr = http::post(tval, turi, *(cs));
+    if (!hasContent(ftr))
+        return mkfailure<int>(reason(ftr));
+    const value tr = content(ftr);
     debug(tr, "modoauth2::access_token::response");
-    const list<value> tv = assoc<value>("access_token", httpd::queryArgs(join("", convertValues<string>(cadr<value>(content(tr))))));
+    if (!isList(tr) || isNil(tr))
+        return mkfailure<int>("Empty access token");
+    const list<value> tv = isString(car<value>(tr)) ?
+        assoc<value>("access_token", httpd::queryArgs(join("", convertValues<string>(cadr<value>(tr))))) :
+        assoc<value>("access_token", tr);
     if (isNil(tv) || isNil(cdr(tv)))
         return mkfailure<int>("Couldn't retrieve access_token");
     debug(tv, "modoauth2::access_token::token");
@@ -227,14 +251,14 @@
 
     // Store user info in memcached keyed by session ID
     const value sid = string("OAuth2_") + mkrand();
-    const failable<bool> prc = memcache::put(mklist<value>("tuscanyOpenAuth", sid), content(iv), mc);
+    const failable<bool> prc = memcache::put(mklist<value>("tuscanyOAuth2", sid), content(iv), mc);
     if (!hasContent(prc))
         return mkfailure<int>(reason(prc));
 
     // Send session ID to the client in a cookie
-    debug(c_str(openauth::cookie(sid, httpd::hostName(r))), "modoauth2::access_token::setcookie");
-    apr_table_set(r->err_headers_out, "Set-Cookie", c_str(openauth::cookie(sid, httpd::hostName(r))));
-    return httpd::externalRedirect(httpd::url(r->uri, r), r);
+    debug(c_str(openauth::cookie("TuscanyOAuth2", sid, httpd::hostName(r))), "modoauth2::access_token::setcookie");
+    apr_table_set(r->err_headers_out, "Set-Cookie", c_str(openauth::cookie("TuscanyOAuth2", sid, httpd::hostName(r))));
+    return httpd::externalRedirect(httpd::url(httpd::unescape(cadr(ref)), r), r);
 }
 
 /**
@@ -246,6 +270,7 @@
     if (!dc.enabled)
         return DECLINED;
     const char* atype = ap_auth_type(r);
+    debug(atype, "modopenauth::checkAuthn::auth_type");
     if (atype == NULL || strcasecmp(atype, "Open"))
         return DECLINED;
 
@@ -253,11 +278,11 @@
     gc_scoped_pool pool(r->pool);
 
     // Get the server configuration
-    httpdDebugRequest(r, "modoauth2::checkAuthn::input");
+    debug_httpdRequest(r, "modoauth2::checkAuthn::input");
     const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth2);
 
     // Get session id from the request
-    const maybe<string> sid = openauth::sessionID(r);
+    const maybe<string> sid = openauth::sessionID(r, "TuscanyOAuth2");
     if (hasContent(sid)) {
         // Decline if the session id was not created by this module
         if (substr(content(sid), 0, 7) != "OAuth2_")
@@ -271,33 +296,25 @@
         }
     }
 
-    // Get the request args
-    const list<list<value> > args = httpd::queryArgs(r);
-
-    // Decline if the request is for another authentication provider
-    if (!isNil(assoc<value>("openid_identifier", args)))
-        return DECLINED;
-    if (!isNil(assoc<value>("mod_oauth1_step", args)))
-        return DECLINED;
-
-    // Determine the OAuth protocol flow step, conveniently passed
-    // around in a request arg
-    const list<value> sl = assoc<value>("mod_oauth2_step", args);
-    const value step = !isNil(sl) && !isNil(cdr(sl))? cadr(sl) : "";
-
     // Handle OAuth authorize request step
-    if (step == "authorize") {
+    if (string(r->uri) == "/oauth2/authorize/") {
         r->ap_auth_type = const_cast<char*>(atype);
-        return httpd::reportStatus(authorize(args, r, sc.appkeys));
+        return httpd::reportStatus(authorize(httpd::queryArgs(r), r, sc.appkeys));
     }
 
     // Handle OAuth access_token request step
-    if (step == "access_token") {
+    if (string(r->uri) == "/oauth2/access_token/") {
         r->ap_auth_type = const_cast<char*>(atype);
-        return httpd::reportStatus(accessToken(args, r, sc.appkeys, sc.cs, sc.mc));
+        return httpd::reportStatus(accessToken(httpd::queryArgs(r), r, sc.appkeys, sc.cs, sc.mc));
     }
 
-    // Redirect to the login page
+    // Redirect to the login page, unless we have a session id from another module
+    if (hasContent(openauth::sessionID(r, "TuscanyOpenIDAuth")) ||
+        hasContent(openauth::sessionID(r, "TuscanyOpenAuth")) ||
+        hasContent(openauth::sessionID(r, "TuscanyOAuth1")))
+        return DECLINED;
+    if ((substr(string(r->uri), 0, 8) == "/oauth1/") || !isNil(assoc<value>("openid_identifier", httpd::queryArgs(r))))
+        return DECLINED;
     r->ap_auth_type = const_cast<char*>(atype);
     return httpd::reportStatus(openauth::login(dc.login, r));
 }
diff --git a/modules/oauth/start-mixed-test b/modules/oauth/start-mixed-test
index d0c1841..11bba42 100755
--- a/modules/oauth/start-mixed-test
+++ b/modules/oauth/start-mixed-test
@@ -17,6 +17,9 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
+# For this module to work, add the www.example.com domain to your /etc/hosts as follows:
+# 127.0.0.1 www.example.com
+
 # Test supporting both OpenID and OAuth in the same app
 here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $here`
 
@@ -24,14 +27,19 @@
 ../../components/cache/memcached-start tmp 11212
 ../../components/cache/memcached-start tmp 11213
 
-../../modules/http/ssl-ca-conf tmp localhost
-../../modules/http/ssl-cert-conf tmp localhost
-../../modules/http/httpd-conf tmp localhost 8090 htdocs
+../../modules/http/ssl-ca-conf tmp www.example.com
+../../modules/http/ssl-cert-conf tmp www.example.com
+../../modules/http/httpd-conf tmp www.example.com 8090 htdocs
 ../../modules/http/httpd-ssl-conf tmp 8453
 
+../openid/openid-conf tmp
+../openid/openid-step2-conf tmp
+../openid/openid-memcached-conf tmp www.example.com 11212
+../openid/openid-memcached-conf tmp www.example.com 11213
+
 ./oauth-conf tmp
-./oauth-memcached-conf tmp localhost 11212
-./oauth-memcached-conf tmp localhost 11213
+./oauth-memcached-conf tmp www.example.com 11212
+./oauth-memcached-conf tmp www.example.com 11213
 
 # Configure your app keys here
 ./oauth1-appkey-conf tmp twitter.com app2345 secret7890
@@ -39,11 +47,6 @@
 ./oauth2-appkey-conf tmp facebook.com app1234 secret6789
 ./oauth2-appkey-conf tmp github.com app5678 secret8901
 
-../openid/openid-conf tmp
-../openid/openid-step2-conf tmp
-../openid/openid-memcached-conf tmp localhost 11212
-../openid/openid-memcached-conf tmp localhost 11213
-
 ../http/open-auth-conf tmp
 ../http/passwd-auth-conf tmp foo foo
 
@@ -51,7 +54,7 @@
 # to the authorized user group
 ../../modules/http/group-auth-conf tmp foo
 ../../modules/http/group-auth-conf tmp 123456
-../../modules/http/group-auth-conf tmp https://www.google.com/accounts/o8/id?id=12345678
+../../modules/http/group-auth-conf tmp jane@example.com
 
 ../../modules/server/server-conf tmp
 ../../modules/server/scheme-conf tmp
diff --git a/modules/oauth/start-test b/modules/oauth/start-test
index d38f8d3..21e1e00 100755
--- a/modules/oauth/start-test
+++ b/modules/oauth/start-test
@@ -17,21 +17,24 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
+# For this module to work, add the www.example.com domain to your /etc/hosts as follows:
+# 127.0.0.1 www.example.com
+
 # Setup
-../../ubuntu/ip-redirect-all 80 8090
-../../ubuntu/ip-redirect-all 443 8453
+#../../ubuntu/ip-redirect-all 80 8090
+#../../ubuntu/ip-redirect-all 443 8453
 
 ../../components/cache/memcached-start tmp 11212
 ../../components/cache/memcached-start tmp 11213
 
-../../modules/http/ssl-ca-conf tmp localhost
-../../modules/http/ssl-cert-conf tmp localhost
-../../modules/http/httpd-conf tmp localhost 8090/80 htdocs
-../../modules/http/httpd-ssl-conf tmp 8453/443
+../../modules/http/ssl-ca-conf tmp www.example.com
+../../modules/http/ssl-cert-conf tmp www.example.com
+../../modules/http/httpd-conf tmp www.example.com 8090 htdocs
+../../modules/http/httpd-ssl-conf tmp 8453
 
 ./oauth-conf tmp
-./oauth-memcached-conf tmp localhost 11212
-./oauth-memcached-conf tmp localhost 11213
+./oauth-memcached-conf tmp www.example.com 11212
+./oauth-memcached-conf tmp www.example.com 11213
 
 # Configure your app keys here
 ./oauth1-appkey-conf tmp twitter.com app2345 secret7890
@@ -41,7 +44,7 @@
 
 # For this test to work you need to add your oauth user id to the
 # authorized user group
-../../modules/http/group-auth-conf tmp 123456
+../../modules/http/group-auth-conf tmp jane@example.com
 
 ../../modules/server/server-conf tmp
 ../../modules/server/scheme-conf tmp
diff --git a/modules/openid/htdocs/login/index.html b/modules/openid/htdocs/login/index.html
index dcb10e1..47fbb73 100644
--- a/modules/openid/htdocs/login/index.html
+++ b/modules/openid/htdocs/login/index.html
@@ -55,9 +55,16 @@
     document.location = '/';
 }
 
+function clearauthcookie() {
+    document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    return true;
+}
+
 function submitSignin(w) {
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     document.signin.openid_identifier.value = w();
     document.signin.action = openidReferrer();
     document.signin.submit();
diff --git a/modules/openid/htdocs/logout/index.html b/modules/openid/htdocs/logout/index.html
index 35172da..a9d2e62 100644
--- a/modules/openid/htdocs/logout/index.html
+++ b/modules/openid/htdocs/logout/index.html
@@ -31,9 +31,16 @@
 
 <form name="signout" action="/login" method="GET">
 <script type="text/javascript">
+function clearauthcookie() {
+    document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/';
+    return true;
+}
+
 function submitSignout() {
-    var reset = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + window.location.hostname + '; path=/';
-    document.cookie = reset;
+    clearauthcookie();
     document.signout.submit();
     return true;
 }
diff --git a/modules/openid/openid-conf b/modules/openid/openid-conf
index f4f715c..d07e4b7 100755
--- a/modules/openid/openid-conf
+++ b/modules/openid/openid-conf
@@ -58,7 +58,7 @@
 Require valid-user
 AuthOpenIDEnabled On
 AuthOpenIDCookiePath /
-AuthOpenIDCookieName TuscanyOpenAuth
+AuthOpenIDCookieName TuscanyOpenIDAuth
 AuthOpenIDLoginPage /login/
 AuthOpenIDAXAdd REMOTE_USER http://axschema.org/contact/email
 AuthOpenIDAXAdd EMAIL http://axschema.org/contact/email
diff --git a/modules/openid/start-test b/modules/openid/start-test
index 195e1c3..5cac1db 100755
--- a/modules/openid/start-test
+++ b/modules/openid/start-test
@@ -31,9 +31,9 @@
 ./openid-memcached-conf tmp localhost 11213
 ./openid-step2-conf tmp
 
-# For this test to work you need to add your openid to the
-# the authorized user group
-../../modules/http/group-auth-conf tmp https://www.google.com/accounts/o8/id?id=1234567
+# For this test to work you need to add your openid (or email address if
+# available from your openid attributes to the the authorized user group
+../../modules/http/group-auth-conf tmp jane@example.com
 
 ../../modules/server/server-conf tmp
 ../../modules/server/scheme-conf tmp
diff --git a/samples/store-cluster/domains/jane/htdocs/login/index.html b/samples/store-cluster/domains/jane/htdocs/login/index.html
index 5bdd113..fcad40b 100644
--- a/samples/store-cluster/domains/jane/htdocs/login/index.html
+++ b/samples/store-cluster/domains/jane/htdocs/login/index.html
@@ -45,7 +45,7 @@
 function openauthReferrer() {
     r = queryParams()['openauth_referrer'];
     if (typeof(r) == 'undefined')
-        return r;
+        return '/';
     q = r.indexOf('?');
     if (q > 0)
         return r.substring(0, q);
@@ -57,8 +57,7 @@
 }
 
 function submitOpenIDSignin(w) {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.openIDSignin.openid_identifier.value = w();
     document.openIDSignin.action = openauthReferrer();
     document.openIDSignin.submit();
@@ -106,46 +105,48 @@
 
 function submitOAuth2Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
-    document.oauth2Signin.mod_oauth2_authorize.value = parms[0];
-    document.oauth2Signin.mod_oauth2_access_token.value = parms[1];
-    document.oauth2Signin.mod_oauth2_client_id.value = parms[2];
-    document.oauth2Signin.mod_oauth2_info.value = parms[3];
-    document.oauth2Signin.action = openauthReferrer();
+    clearauthcookie();
+    document.oauth2Signin.oauth2_authorize.value = parms[0];
+    document.oauth2Signin.oauth2_access_token.value = parms[1];
+    document.oauth2Signin.oauth2_client_id.value = parms[2];
+    document.oauth2Signin.oauth2_info.value = parms[3];
+    document.oauth2Signin.oauth2_scope.value = parms[4];
+    document.oauth2Signin.oauth2_display.value = parms[5];
+    document.oauth2Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth2Signin.action = '/oauth2/authorize/';
     document.oauth2Signin.submit();
 }
 
 function withFacebook() {
-    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'testfacebookapp', 'https://graph.facebook.com/me'];
+    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me', 'email', 'page'];
     return parms;
 }
 
 function withGithub() {
-    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'testgithubapp', 'https://github.com/api/v2/json/user/show'];
+    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show', 'email', ''];
     return parms;
 }
 
 function submitOAuth1Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
-    document.oauth1Signin.mod_oauth1_request_token.value = parms[0];
-    document.oauth1Signin.mod_oauth1_authorize.value = parms[1];
-    document.oauth1Signin.mod_oauth1_access_token.value = parms[2];
-    document.oauth1Signin.mod_oauth1_client_id.value = parms[3];
-    document.oauth1Signin.mod_oauth1_info.value = parms[4];
-    document.oauth1Signin.action = openauthReferrer();
+    clearauthcookie();
+    document.oauth1Signin.oauth1_request_token.value = parms[0];
+    document.oauth1Signin.oauth1_authorize.value = parms[1];
+    document.oauth1Signin.oauth1_access_token.value = parms[2];
+    document.oauth1Signin.oauth1_client_id.value = parms[3];
+    document.oauth1Signin.oauth1_info.value = parms[4];
+    document.oauth1Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth1Signin.action = '/oauth1/authorize/';
     document.oauth1Signin.submit();
 }
 
 function withLinkedin() {
-    var parms = ['https://api.linkedin.com/uas/oauth/requestToken', 'https://www.linkedin.com/uas/oauth/authorize', 'https://api.linkedin.com/uas/oauth/accessToken', 'testlinkedinapp', 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url)'];
+    var parms = ['https://api.linkedin.com/uas/oauth/requestToken', 'https://www.linkedin.com/uas/oauth/authorize', 'https://api.linkedin.com/uas/oauth/accessToken', 'linkedin.com', 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url)'];
     return parms;
 }
 
 function withTwitter() {
-    var parms = ['https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/authorize', 'https://api.twitter.com/oauth/access_token', 'testtwitterapp', 'https://api.twitter.com/1/statuses/user_timeline.json'];
+    var parms = ['https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/authorize', 'https://api.twitter.com/oauth/access_token', 'twitter.com', 'https://api.twitter.com/1/statuses/user_timeline.json'];
     return parms;
 }
 </script>
@@ -189,20 +190,22 @@
 </form>
 
 <form name="oauth2Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth2_authorize" value=""/>
-<input type="hidden" name="mod_oauth2_access_token" value=""/>
-<input type="hidden" name="mod_oauth2_client_id" value=""/>
-<input type="hidden" name="mod_oauth2_info" value=""/>
-<input type="hidden" name="mod_oauth2_step" value="authorize"/>
+<input type="hidden" name="oauth2_authorize" value=""/>
+<input type="hidden" name="oauth2_access_token" value=""/>
+<input type="hidden" name="oauth2_client_id" value=""/>
+<input type="hidden" name="oauth2_info" value=""/>
+<input type="hidden" name="oauth2_scope" value=""/>
+<input type="hidden" name="oauth2_display" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 <form name="oauth1Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth1_request_token" value=""/>
-<input type="hidden" name="mod_oauth1_authorize" value=""/>
-<input type="hidden" name="mod_oauth1_access_token" value=""/>
-<input type="hidden" name="mod_oauth1_client_id" value=""/>
-<input type="hidden" name="mod_oauth1_info" value=""/>
-<input type="hidden" name="mod_oauth1_step" value="authorize"/>
+<input type="hidden" name="oauth1_request_token" value=""/>
+<input type="hidden" name="oauth1_authorize" value=""/>
+<input type="hidden" name="oauth1_access_token" value=""/>
+<input type="hidden" name="oauth1_client_id" value=""/>
+<input type="hidden" name="oauth1_info" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 </body>
diff --git a/samples/store-cluster/domains/jane/htdocs/logout/index.html b/samples/store-cluster/domains/jane/htdocs/logout/index.html
index 56dfa71..91608a6 100644
--- a/samples/store-cluster/domains/jane/htdocs/logout/index.html
+++ b/samples/store-cluster/domains/jane/htdocs/logout/index.html
@@ -33,8 +33,7 @@
 <form name="signout" action="/login" method="GET">
 <script type="text/javascript">
 function submitSignout() {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.signout.submit();
     return true;
 }
diff --git a/samples/store-cluster/domains/joe/htdocs/login/index.html b/samples/store-cluster/domains/joe/htdocs/login/index.html
index 5bdd113..fcad40b 100644
--- a/samples/store-cluster/domains/joe/htdocs/login/index.html
+++ b/samples/store-cluster/domains/joe/htdocs/login/index.html
@@ -45,7 +45,7 @@
 function openauthReferrer() {
     r = queryParams()['openauth_referrer'];
     if (typeof(r) == 'undefined')
-        return r;
+        return '/';
     q = r.indexOf('?');
     if (q > 0)
         return r.substring(0, q);
@@ -57,8 +57,7 @@
 }
 
 function submitOpenIDSignin(w) {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.openIDSignin.openid_identifier.value = w();
     document.openIDSignin.action = openauthReferrer();
     document.openIDSignin.submit();
@@ -106,46 +105,48 @@
 
 function submitOAuth2Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
-    document.oauth2Signin.mod_oauth2_authorize.value = parms[0];
-    document.oauth2Signin.mod_oauth2_access_token.value = parms[1];
-    document.oauth2Signin.mod_oauth2_client_id.value = parms[2];
-    document.oauth2Signin.mod_oauth2_info.value = parms[3];
-    document.oauth2Signin.action = openauthReferrer();
+    clearauthcookie();
+    document.oauth2Signin.oauth2_authorize.value = parms[0];
+    document.oauth2Signin.oauth2_access_token.value = parms[1];
+    document.oauth2Signin.oauth2_client_id.value = parms[2];
+    document.oauth2Signin.oauth2_info.value = parms[3];
+    document.oauth2Signin.oauth2_scope.value = parms[4];
+    document.oauth2Signin.oauth2_display.value = parms[5];
+    document.oauth2Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth2Signin.action = '/oauth2/authorize/';
     document.oauth2Signin.submit();
 }
 
 function withFacebook() {
-    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'testfacebookapp', 'https://graph.facebook.com/me'];
+    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me', 'email', 'page'];
     return parms;
 }
 
 function withGithub() {
-    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'testgithubapp', 'https://github.com/api/v2/json/user/show'];
+    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show', 'email', ''];
     return parms;
 }
 
 function submitOAuth1Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
-    document.oauth1Signin.mod_oauth1_request_token.value = parms[0];
-    document.oauth1Signin.mod_oauth1_authorize.value = parms[1];
-    document.oauth1Signin.mod_oauth1_access_token.value = parms[2];
-    document.oauth1Signin.mod_oauth1_client_id.value = parms[3];
-    document.oauth1Signin.mod_oauth1_info.value = parms[4];
-    document.oauth1Signin.action = openauthReferrer();
+    clearauthcookie();
+    document.oauth1Signin.oauth1_request_token.value = parms[0];
+    document.oauth1Signin.oauth1_authorize.value = parms[1];
+    document.oauth1Signin.oauth1_access_token.value = parms[2];
+    document.oauth1Signin.oauth1_client_id.value = parms[3];
+    document.oauth1Signin.oauth1_info.value = parms[4];
+    document.oauth1Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth1Signin.action = '/oauth1/authorize/';
     document.oauth1Signin.submit();
 }
 
 function withLinkedin() {
-    var parms = ['https://api.linkedin.com/uas/oauth/requestToken', 'https://www.linkedin.com/uas/oauth/authorize', 'https://api.linkedin.com/uas/oauth/accessToken', 'testlinkedinapp', 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url)'];
+    var parms = ['https://api.linkedin.com/uas/oauth/requestToken', 'https://www.linkedin.com/uas/oauth/authorize', 'https://api.linkedin.com/uas/oauth/accessToken', 'linkedin.com', 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url)'];
     return parms;
 }
 
 function withTwitter() {
-    var parms = ['https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/authorize', 'https://api.twitter.com/oauth/access_token', 'testtwitterapp', 'https://api.twitter.com/1/statuses/user_timeline.json'];
+    var parms = ['https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/authorize', 'https://api.twitter.com/oauth/access_token', 'twitter.com', 'https://api.twitter.com/1/statuses/user_timeline.json'];
     return parms;
 }
 </script>
@@ -189,20 +190,22 @@
 </form>
 
 <form name="oauth2Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth2_authorize" value=""/>
-<input type="hidden" name="mod_oauth2_access_token" value=""/>
-<input type="hidden" name="mod_oauth2_client_id" value=""/>
-<input type="hidden" name="mod_oauth2_info" value=""/>
-<input type="hidden" name="mod_oauth2_step" value="authorize"/>
+<input type="hidden" name="oauth2_authorize" value=""/>
+<input type="hidden" name="oauth2_access_token" value=""/>
+<input type="hidden" name="oauth2_client_id" value=""/>
+<input type="hidden" name="oauth2_info" value=""/>
+<input type="hidden" name="oauth2_scope" value=""/>
+<input type="hidden" name="oauth2_display" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 <form name="oauth1Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth1_request_token" value=""/>
-<input type="hidden" name="mod_oauth1_authorize" value=""/>
-<input type="hidden" name="mod_oauth1_access_token" value=""/>
-<input type="hidden" name="mod_oauth1_client_id" value=""/>
-<input type="hidden" name="mod_oauth1_info" value=""/>
-<input type="hidden" name="mod_oauth1_step" value="authorize"/>
+<input type="hidden" name="oauth1_request_token" value=""/>
+<input type="hidden" name="oauth1_authorize" value=""/>
+<input type="hidden" name="oauth1_access_token" value=""/>
+<input type="hidden" name="oauth1_client_id" value=""/>
+<input type="hidden" name="oauth1_info" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 </body>
diff --git a/samples/store-cluster/domains/joe/htdocs/logout/index.html b/samples/store-cluster/domains/joe/htdocs/logout/index.html
index 5f7880d..91608a6 100644
--- a/samples/store-cluster/domains/joe/htdocs/logout/index.html
+++ b/samples/store-cluster/domains/joe/htdocs/logout/index.html
@@ -33,12 +33,12 @@
 <form name="signout" action="/login" method="GET">
 <script type="text/javascript">
 function submitSignout() {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.signout.submit();
     return true;
 }
 </script>
 <input type="button" onclick="submitSignout()" value="Sign out"/>
 </form>
-</body></html>
+</body>
+</html>
diff --git a/samples/store-cluster/htdocs/login/index.html b/samples/store-cluster/htdocs/login/index.html
index 5bdd113..fcad40b 100644
--- a/samples/store-cluster/htdocs/login/index.html
+++ b/samples/store-cluster/htdocs/login/index.html
@@ -45,7 +45,7 @@
 function openauthReferrer() {
     r = queryParams()['openauth_referrer'];
     if (typeof(r) == 'undefined')
-        return r;
+        return '/';
     q = r.indexOf('?');
     if (q > 0)
         return r.substring(0, q);
@@ -57,8 +57,7 @@
 }
 
 function submitOpenIDSignin(w) {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.openIDSignin.openid_identifier.value = w();
     document.openIDSignin.action = openauthReferrer();
     document.openIDSignin.submit();
@@ -106,46 +105,48 @@
 
 function submitOAuth2Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
-    document.oauth2Signin.mod_oauth2_authorize.value = parms[0];
-    document.oauth2Signin.mod_oauth2_access_token.value = parms[1];
-    document.oauth2Signin.mod_oauth2_client_id.value = parms[2];
-    document.oauth2Signin.mod_oauth2_info.value = parms[3];
-    document.oauth2Signin.action = openauthReferrer();
+    clearauthcookie();
+    document.oauth2Signin.oauth2_authorize.value = parms[0];
+    document.oauth2Signin.oauth2_access_token.value = parms[1];
+    document.oauth2Signin.oauth2_client_id.value = parms[2];
+    document.oauth2Signin.oauth2_info.value = parms[3];
+    document.oauth2Signin.oauth2_scope.value = parms[4];
+    document.oauth2Signin.oauth2_display.value = parms[5];
+    document.oauth2Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth2Signin.action = '/oauth2/authorize/';
     document.oauth2Signin.submit();
 }
 
 function withFacebook() {
-    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'testfacebookapp', 'https://graph.facebook.com/me'];
+    var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'facebook.com', 'https://graph.facebook.com/me', 'email', 'page'];
     return parms;
 }
 
 function withGithub() {
-    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'testgithubapp', 'https://github.com/api/v2/json/user/show'];
+    var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show', 'email', ''];
     return parms;
 }
 
 function submitOAuth1Signin(w) {
     parms = w();
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
-    document.oauth1Signin.mod_oauth1_request_token.value = parms[0];
-    document.oauth1Signin.mod_oauth1_authorize.value = parms[1];
-    document.oauth1Signin.mod_oauth1_access_token.value = parms[2];
-    document.oauth1Signin.mod_oauth1_client_id.value = parms[3];
-    document.oauth1Signin.mod_oauth1_info.value = parms[4];
-    document.oauth1Signin.action = openauthReferrer();
+    clearauthcookie();
+    document.oauth1Signin.oauth1_request_token.value = parms[0];
+    document.oauth1Signin.oauth1_authorize.value = parms[1];
+    document.oauth1Signin.oauth1_access_token.value = parms[2];
+    document.oauth1Signin.oauth1_client_id.value = parms[3];
+    document.oauth1Signin.oauth1_info.value = parms[4];
+    document.oauth1Signin.openauth_referrer.value = openauthReferrer();
+    document.oauth1Signin.action = '/oauth1/authorize/';
     document.oauth1Signin.submit();
 }
 
 function withLinkedin() {
-    var parms = ['https://api.linkedin.com/uas/oauth/requestToken', 'https://www.linkedin.com/uas/oauth/authorize', 'https://api.linkedin.com/uas/oauth/accessToken', 'testlinkedinapp', 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url)'];
+    var parms = ['https://api.linkedin.com/uas/oauth/requestToken', 'https://www.linkedin.com/uas/oauth/authorize', 'https://api.linkedin.com/uas/oauth/accessToken', 'linkedin.com', 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url)'];
     return parms;
 }
 
 function withTwitter() {
-    var parms = ['https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/authorize', 'https://api.twitter.com/oauth/access_token', 'testtwitterapp', 'https://api.twitter.com/1/statuses/user_timeline.json'];
+    var parms = ['https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/authorize', 'https://api.twitter.com/oauth/access_token', 'twitter.com', 'https://api.twitter.com/1/statuses/user_timeline.json'];
     return parms;
 }
 </script>
@@ -189,20 +190,22 @@
 </form>
 
 <form name="oauth2Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth2_authorize" value=""/>
-<input type="hidden" name="mod_oauth2_access_token" value=""/>
-<input type="hidden" name="mod_oauth2_client_id" value=""/>
-<input type="hidden" name="mod_oauth2_info" value=""/>
-<input type="hidden" name="mod_oauth2_step" value="authorize"/>
+<input type="hidden" name="oauth2_authorize" value=""/>
+<input type="hidden" name="oauth2_access_token" value=""/>
+<input type="hidden" name="oauth2_client_id" value=""/>
+<input type="hidden" name="oauth2_info" value=""/>
+<input type="hidden" name="oauth2_scope" value=""/>
+<input type="hidden" name="oauth2_display" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 <form name="oauth1Signin" action="/" method="GET">
-<input type="hidden" name="mod_oauth1_request_token" value=""/>
-<input type="hidden" name="mod_oauth1_authorize" value=""/>
-<input type="hidden" name="mod_oauth1_access_token" value=""/>
-<input type="hidden" name="mod_oauth1_client_id" value=""/>
-<input type="hidden" name="mod_oauth1_info" value=""/>
-<input type="hidden" name="mod_oauth1_step" value="authorize"/>
+<input type="hidden" name="oauth1_request_token" value=""/>
+<input type="hidden" name="oauth1_authorize" value=""/>
+<input type="hidden" name="oauth1_access_token" value=""/>
+<input type="hidden" name="oauth1_client_id" value=""/>
+<input type="hidden" name="oauth1_info" value=""/>
+<input type="hidden" name="openauth_referrer" value=""/>
 </form>
 
 </body>
diff --git a/samples/store-cluster/htdocs/logout/index.html b/samples/store-cluster/htdocs/logout/index.html
index 56dfa71..91608a6 100644
--- a/samples/store-cluster/htdocs/logout/index.html
+++ b/samples/store-cluster/htdocs/logout/index.html
@@ -33,8 +33,7 @@
 <form name="signout" action="/login" method="GET">
 <script type="text/javascript">
 function submitSignout() {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.signout.submit();
     return true;
 }
diff --git a/samples/store-python/htdocs/login/index.html b/samples/store-python/htdocs/login/index.html
index 99aeb31..5c5286f 100644
--- a/samples/store-python/htdocs/login/index.html
+++ b/samples/store-python/htdocs/login/index.html
@@ -32,8 +32,7 @@
 
 <script type="text/javascript">
 function submitFormSignin() {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.formSignin.httpd_location.value = '/';
     document.formSignin.submit();
 }
diff --git a/samples/store-python/htdocs/logout/index.html b/samples/store-python/htdocs/logout/index.html
index 5f7880d..452fa73 100644
--- a/samples/store-python/htdocs/logout/index.html
+++ b/samples/store-python/htdocs/logout/index.html
@@ -33,8 +33,7 @@
 <form name="signout" action="/login" method="GET">
 <script type="text/javascript">
 function submitSignout() {
-    var reset = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';domain=.' + domainname(window.location.hostname) + ';path=/;secure=TRUE';
-    document.cookie = reset;
+    clearauthcookie();
     document.signout.submit();
     return true;
 }