Index: google_auth.module =================================================================== RCS file: /cvs/drupal/contributions/modules/google_auth/google_auth.module,v retrieving revision 1.1.2.1 diff -u -r1.1.2.1 google_auth.module --- google_auth.module 12 Apr 2009 12:38:57 -0000 1.1.2.1 +++ google_auth.module 17 Apr 2009 08:30:41 -0000 @@ -9,7 +9,18 @@ * http://code.google.com/apis/accounts/docs/AuthForWebApps.html * * This implementation of Google authentication uses AuthStub which is designed - * for non-registered web applications. http://code.google.com/apis/accounts/docs/AuthSub.html + * for both registered and non-registered web applications. + * http://code.google.com/apis/accounts/docs/AuthSub.html + * + * Registered web applications have to provide google with a valid return Target Path. + * This path is used by Google to return authentication tokens back to the + * registered domain. + * see http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html + * + * In this implementation the relative path /gauthsub is used for registered web + * applications. Site administrators of registered domains should go to + * https://www.google.com/accounts/ManageDomains and set the Target Path of their domain. + * For example: http://www.example.com/gauth. */ /** @@ -22,9 +33,6 @@ * token enables access to the specified service(s) only. To specify more * than one scope, list each one separated with a space (encodes as "%20"). * example: http://picasaweb.google.com/data/ - * - boolean $variables->secure (optional) Boolean flag indicating whether the authentication - * transaction should issue a secure token (1) or a non-secure token (0). Secure - * tokens are available to registered applications only. * - boolean (optional) Boolean flag indicating whether the one-time-use token * may be exchanged for a session token (1) or not (0). * - string $variables->redirect (optional) An optional page to redirect to defaulting to @@ -34,38 +42,57 @@ $backtrace = debug_backtrace(); $error = $backtrace[0]['file']; $error .= ' '. t('line') .' '. $backtrace[0]['line']; - drupal_set_message(t('You must provide an object containing a scope when calling google_auth_required: %error', array('%error' => $error)), 'error'); + drupal_set_message(t('You must provide an object containing a scope when + calling google_auth_required: %error', + array('%error' => $error)), 'error'); drupal_goto(''); } - if (empty($variables->secure)) { - $variables->secure = 0; - } if (empty($variables->session)) { $variables->session = 1; } - if (empty($variables->redirect)) { - $variables->redirect = ''; - } - if (!isset($_SESSION['google_auth_token'])) { + $data['registered'] = variable_get('google_auth_registered', 0); $data['scope'] = $variables->scope; $data['url'] = 'https://www.google.com/accounts/AuthSubRequest'; - $data["secure"] = $variables->secure; - $data["session"] = $variables->session; - $data["next"] = _google_auth_next(); - $data["redirect"] = $variables->redirect; + $data['session'] = $variables->session; + $data['next'] = _google_auth_get_url($data['registered'], 1); + $data['redirect'] = _google_auth_get_url($data['registered'], 0); + + //Only handle security if domain is registered + if ($data['registered'] == 1){ + $data['secure'] = variable_get('google_auth_secure', 0); + $data['sig'] = variable_get('google_auth_sign', ''); + $data['sigalg'] = variable_get('google_auth_sigalg', 'rsa_sha1'); + + //Create a session variable to allow google_auth_handoff() to work correctly. + if (!isset($_SESSION['google_auth_data'])){ + $_SESSION['google_auth_data'] = $data; + } + } _google_auth_sub_request($data); } } /** - * Get the next url to pass to Google - * + * Get either the next url to pass to Google or the current url to + * pass to google_auth_handoff(). + * + * @param boolean $registered + * Boolean flag indicating whether the domain is registered with Google + * or not-registered. + * @param boolean $next (optional) + * Boolean flag indicating whether the url should be the next url + * to pass to Google or the url to pass to google_auth_handoff(). * @return $url - * A string representing the current url + * A string representing an urlsecure */ -function _google_auth_next() { - $uri = request_uri(); +function _google_auth_get_url($registered, $next) { + if ($registered == 1 && $next == 1){ + $uri = '/gauth'; + } + else { + $uri = request_uri(); + } if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') { $p = 'https://'; } @@ -96,29 +123,185 @@ $url .= '?next='. drupal_urlencode($data['next']); $url .= '&scope='. drupal_urlencode($data['scope']); $url .= "&session=". $data['session']; - + if($data['registered']== 1 && $data['secure']==1){ + $url .= "&secure=". $data['secure']; + } + if (!isset($_GET["token"])) { $url = str_replace( "http%3A/%252F", "http%3A//", $url); drupal_goto($url); } else { - // exchange the single use token for a session token from Google - $headers['Content-Type'] = 'application/x-www-form-urlencoded'; - $headers['Authorization'] = 'AuthSub token="'. $_GET["token"] .'"'; - $session_request = str_replace( "AuthSubRequest", "AuthSubSessionToken", $url); - $session_exchange = drupal_http_request($session_request, $headers); - if ($session_exchange->code == 200) { - $token = str_replace('Token=', '', $session_exchange->data); - $_SESSION['google_auth_token'] = trim($token); - $msg = t("You have authenticated with Google."); - drupal_set_message($msg); - } - else { - $msg = t('Authentication with Google has failed.') .' '. $session_exchange->error; - watchdog('google_auth', 'google authentication error - '. print_r($session_exchange, TRUE), array(), WATCHDOG_ERROR); - drupal_set_message($msg, 'error'); - drupal_goto($data['redirect']); + _google_auth_exchange($data); + } +} + +/** + * Exchanges a single use token for a session token from Google + * + * @param $data array + * an array containing url and session information + */ +function _google_auth_exchange($data){ + $headers['Content-Type'] = 'application/x-www-form-urlencoded'; + $headers['Authorization'] = 'AuthSub token="'. $_GET["token"] .'"'; + /*if ($data['registered' == 1 && $data['secure'] == 1]){ + $http_request_url = 'GET https://www.google.com/accounts/AuthSubSessionToken'; + $timestamp = time(); + $nonce = 0; //need to figure out how to generate a 64 bit random number? + $sig = $data['sig']; + $sigalg = $data['sigalg']; + $headers['Authorization'] .= ' data="'.$http_request_url.' '.$timestamp.' '.$nonce.'" '; + $headers['Authorization'] .= 'sig="'.$sig.'" sigalg="'.$sigalg.'"'; + }*/ + $session_request = str_replace( "AuthSubRequest", "AuthSubSessionToken", $url); + $session_exchange = drupal_http_request($session_request, $headers); + if ($session_exchange->code == 200) { + $token = str_replace('Token=', '', $session_exchange->data); + $_SESSION['google_auth_token'] = trim($token); + $msg = t("You have authenticated with Google."); + drupal_set_message($msg); + } + else { + $msg = t('Authentication with Google has failed.') .' '. $session_exchange->error; + watchdog('google_auth', 'google authentication error - '. print_r($session_exchange, TRUE), array(), WATCHDOG_ERROR); + drupal_set_message($msg, 'error'); + drupal_goto($data['redirect']); + } +} + +/** + * Requests a session token from Google if required and handover to the + * originating web application. + * + * This function should only be called by drupals menu system in response to + * Google returning an authentication token. Unexpected entry to the relative path + * /gauth is handled by displaying a Google Authentication Error page. + * + */ +function _google_auth_handoff(){ + foreach($_SESSION['google_auth_data'] as $key=>$value){ + $data[$key] = $value; + } + if (isset($data['redirect']) && isset($data['scope'])){ + $redirect_url = $data['redirect']; + if ($data['session'] == 1){ + google_auth_exchange($data); } + //Token received so no need to keep session variables for redirect + unset($_SESSION['google_auth_data']); + + drupal_goto($redirect_url); + } + else { + $msg = t("Authentication with Google has failed. Please try again."); + watchdog('google_auth', 'google authentication error - '. print_r($session_exchange, TRUE)); + drupal_set_message($msg); + $page_content = t('You are reading this page because:
    +
  • An error occured during the Authentication process with Google.
  • +
  • The page was reached either manually or from a web site other than Google
  • +

If this error continues to occur please contact your site + administrator

'); + return $page_content; + } +} + +// probably need to implement hook_help() at some future point in time + +/** + * Implementation of hook_admin(). + */ +function google_auth_admin(){ + $form['google_auth_registered'] = array( + '#title' => t('Site Registered with Google'), + '#type' => 'checkbox', + '#description' => t('Tick this box if you have registered + your site as a web app with Google
+ You must set your Google target path to http://' + .$_SERVER['HTTP_HOST'].'/gauth or + https://'.$_SERVER['HTTP_HOST'].'/gauth'), + '#default_value' => variable_get('google_auth_registered', 0) + ); + $form['google_auth_secure'] = array( + '#title' => t('Site uses secure request transaction'), + '#type' => 'checkbox', + '#description' => t('Tick this box of you have uploaded a RSA SHA1 + security certificate to Google'), + '#default_value' => variable_get('google_auth_secure', 0), + ); + $form['google_auth_sign'] = array( + '#title' => t('Secure Signature'), + '#type' => 'textfield', + '#description' => t('Enter your domain signature made by the private key + corresponding to the certificate you provided during + Google registration. It must be encoded in BASE64, + and must use the algorithm specified below.'), + '#default_value' => variable_get('google_auth_sign', ''), + ); + $form['google_auth_sigalg'] = array( + '#title' => t('Signature Algorith'), + '#type' => 'select', + '#default_value' => variable_get('google_auth_sigalg', 'rsa_sha1'), + '#options' => array( + 'rsa_sha1' => t('RSA-SHA1'), + ), + '#description' => t('Select the security signature algorithm used to + create the above signature and the certificate you + provided to Google'), + ); + return system_settings_form($form); +} +/** + * Implementation of hook_admin_validate() + */ +function google_auth_admin_validate($form, &$form_state){ + $admin_registered = $form_state['values']['google_auth_registered']; + $admin_secure = $form_state['values']['google_auth_secure']; + $admin_sig = $form_state['values']['google_auth_sign']; + if ($admin_registered == 0 && $admin_secure == 1){ + form_set_error('google_auth_registered', t('Your site must be registered with + Google to use secure request + transactions.')); } + if ($admin_secure == 1 && $admin_sig == ''){ + form_set_error('google_auth_sign', t('You must provide a valid signature + to use secure request transactions.')); + } + // Possibly need more error checking re: signature?. +} +/** + * Implementation of hook_menu() + * + * This implementation uses the drupal menu system to provides a valid relative path + * at /gauth for registered web applications. When Google returns to this URL + * drupal invokes a menu_callback to _google_auth_handover(). + */ +function google_auth_menu(){ + $items = array(); + $items['admin/settings/google_auth'] = array( + 'title' => 'Google Authentication', + 'description' => 'Sets your site configuration to use Google + Authentication API', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('google_auth_admin'), + 'access arguments' => array('access administration pages'), + 'type' => MENU_NORMAL_ITEM, + ); + + // callback link to _google_auth_handoff() + $items['gauth'] = array( + 'title' => 'Google Authentication Error', + 'page callback' => '_google_auth_handoff', + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK + ); + return $items; } +function google_auth_uninstall(){ + + variable_del('google_auth_registered'); + variable_del('google_auth_secure'); + variable_del('google_auth_sign'); + variable_del('google_auth_sigalg'); +}