diff options
Diffstat (limited to 'sca-cpp/trunk/modules/oauth')
-rw-r--r-- | sca-cpp/trunk/modules/oauth/htdocs/login/index.html | 70 | ||||
-rw-r--r-- | sca-cpp/trunk/modules/oauth/htdocs/login/mixed.html | 76 | ||||
-rw-r--r-- | sca-cpp/trunk/modules/oauth/htdocs/logout/index.html | 11 | ||||
-rw-r--r-- | sca-cpp/trunk/modules/oauth/mod-oauth1.cpp | 87 | ||||
-rw-r--r-- | sca-cpp/trunk/modules/oauth/mod-oauth2.cpp | 123 | ||||
-rwxr-xr-x | sca-cpp/trunk/modules/oauth/start-mixed-test | 25 | ||||
-rwxr-xr-x | sca-cpp/trunk/modules/oauth/start-test | 21 |
7 files changed, 232 insertions, 181 deletions
diff --git a/sca-cpp/trunk/modules/oauth/htdocs/login/index.html b/sca-cpp/trunk/modules/oauth/htdocs/login/index.html index d1002f79ec..02439154e7 100644 --- a/sca-cpp/trunk/modules/oauth/htdocs/login/index.html +++ b/sca-cpp/trunk/modules/oauth/htdocs/login/index.html @@ -44,7 +44,7 @@ function queryParams() { 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 @@ if (typeof(oauthReferrer()) == 'undefined') { 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 @@ function withTwitter() { </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/sca-cpp/trunk/modules/oauth/htdocs/login/mixed.html b/sca-cpp/trunk/modules/oauth/htdocs/login/mixed.html index 57484dc479..50d70f04e4 100644 --- a/sca-cpp/trunk/modules/oauth/htdocs/login/mixed.html +++ b/sca-cpp/trunk/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 queryParams() { 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 @@ if (typeof(openauthReferrer()) == 'undefined') { } 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 withXRDSEndpoint() { 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 @@ function withTwitter() { </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/sca-cpp/trunk/modules/oauth/htdocs/logout/index.html b/sca-cpp/trunk/modules/oauth/htdocs/logout/index.html index 35172da07f..a9d2e628d7 100644 --- a/sca-cpp/trunk/modules/oauth/htdocs/logout/index.html +++ b/sca-cpp/trunk/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/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp b/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp index 1276ea4855..29fe756178 100644 --- a/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp +++ b/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp @@ -89,7 +89,7 @@ public: * 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 list<string> sign(const string& verb, const string& uri, const list<value> */ 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<list<value> > profileUserInfo(const value& cid, const string& inf */ 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 @@ const failable<int> accessToken(const list<list<value> >& args, request_rec* r, // 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 @@ static int checkAuthn(request_rec *r) { 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 @@ static int checkAuthn(request_rec *r) { 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 @@ static int checkAuthn(request_rec *r) { } } - // 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/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp b/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp index ace611d3dc..dbede7ed8b 100644 --- a/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp +++ b/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp @@ -83,7 +83,7 @@ public: * 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> authenticated(const list<list<value> >& attrs, const list<li */ 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 @@ const failable<int> authorize(const list<list<value> >& args, request_rec* r, co // 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 @@ const failable<list<value> > profileUserInfo(const value& cid, const list<value> * 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 @@ const failable<int> accessToken(const list<list<value> >& args, request_rec* r, 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 @@ const failable<int> accessToken(const list<list<value> >& args, request_rec* r, // 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 @@ static int checkAuthn(request_rec *r) { 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 @@ static int checkAuthn(request_rec *r) { 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 @@ static int checkAuthn(request_rec *r) { } } - // 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/sca-cpp/trunk/modules/oauth/start-mixed-test b/sca-cpp/trunk/modules/oauth/start-mixed-test index d0c184144b..11bba42ef0 100755 --- a/sca-cpp/trunk/modules/oauth/start-mixed-test +++ b/sca-cpp/trunk/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 @@ here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $h ../../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 @@ here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $h ./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 @@ here=`echo "import os; print os.path.realpath('$0')" | python`; here=`dirname $h # 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/sca-cpp/trunk/modules/oauth/start-test b/sca-cpp/trunk/modules/oauth/start-test index d38f8d3d1a..21e1e005bd 100755 --- a/sca-cpp/trunk/modules/oauth/start-test +++ b/sca-cpp/trunk/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 |