*/
/**
+ * Status codes for BrowserID verification.
+ */
+define('BROWSERID_REGISTER', 1);
+define('BROWSERID_LOGIN', 2);
+define('BROWSERID_ACCESS_DENIED', 3);
+
+/**
* Implements hook_menu().
*/
function browserid_menu() {
'email' => $GLOBALS['user']->mail
)), 'setting');
}
+ else {
+ drupal_add_js(array('browserid' => array(
+ 'token' => browserid_get_token('browserid-login-csrf'),
+ )), 'setting');
+ }
}
/**
$options = array(
//'text' => l(t('Sign in with BrowserID'), $_REQUEST['q']),
//'button' => t('Standard button'),
- 'red' => theme('image', $vars + array('path' => $path .'/images/sign_in_red.png')),
- 'blue' => theme('image', $vars + array('path' => $path .'/images/sign_in_blue.png')),
- 'orange' => theme('image', $vars + array('path' => $path .'/images/sign_in_orange.png')),
- 'green' => theme('image', $vars + array('path' => $path .'/images/sign_in_green.png')),
- 'grey' => theme('image', $vars + array('path' => $path .'/images/sign_in_grey.png')),
+ 'red' => theme('image', $vars + array('path' => $path . '/images/sign_in_red.png')),
+ 'blue' => theme('image', $vars + array('path' => $path . '/images/sign_in_blue.png')),
+ 'orange' => theme('image', $vars + array('path' => $path . '/images/sign_in_orange.png')),
+ 'green' => theme('image', $vars + array('path' => $path . '/images/sign_in_green.png')),
+ 'grey' => theme('image', $vars + array('path' => $path . '/images/sign_in_grey.png')),
);
return $all ? $options : $options[$type];
}
* Implements hook_form_FORM_ID_alter().
*/
function browserid_form_user_register_form_alter(&$form, $form_state) {
- if (isset($_REQUEST['assertion']) && isset($_REQUEST['audience'])) {
- $response = drupal_http_request(
- 'https://browserid.org/verify?assertion=' . urlencode($_REQUEST['assertion']) . '&audience=' . urlencode($_REQUEST['audience']),
- array('method' => 'POST')
- );
- if ($response->code == 200) {
- $data = json_decode($response->data);
- $account = user_load_by_mail($data->email);
- if (!empty($account) && !empty($account->uid)) {
- $form_state = array('uid' => $account->uid);
- user_login_submit(array(), $form_state);
- drupal_goto('user/'. $account->uid);
- }
- else {
- $error = user_validate_mail($data->email);
- if (!$error) {
- drupal_set_message(
- t('The email address %mail is not registered on @site.', array('%mail' => $data->email, '@site' => variable_get('site_name', 'Drupal'))) .' '.
- t('You can register below, or try again with a different email address: !browserid', array('!browserid' => browserid_login_button()))
- );
- $form['account']['mail'] = array(
- '#type' => 'value',
- '#value' => $data->email,
- );
- $form['account']['mail_info'] = array(
- '#type' => 'item',
- '#title' => t('E-mail address'),
- '#markup' => '<p>'. check_plain($data->email) .'</p>',
- '#required' => TRUE,
- '#weight' => -1,
- );
- }
- }
+ $result = browserid_verify_request();
+ if ($result['code'] == BROWSERID_ACCESS_DENIED) {
+ return;
+ }
+ elseif ($result['code'] == BROWSERID_LOGIN) {
+ drupal_goto('user/' . $result['account']->uid);
+ }
+ elseif ($result['code'] == BROWSERID_REGISTER) {
+ $error = user_validate_mail($result['email']);
+ if (!$error) {
+ drupal_set_message(
+ t('The email address %mail is not registered on @site.', array(
+ '%mail' => $result['email'],
+ '@site' => variable_get('site_name', 'Drupal'),
+ )) . ' ' .
+ t('You can register below, or try again with a different email address: !browserid', array(
+ '!browserid' => browserid_login_button(),
+ ))
+ );
+ $form['account']['mail'] = array(
+ '#type' => 'value',
+ '#value' => $result['email'],
+ );
+ $form['account']['mail_info'] = array(
+ '#type' => 'item',
+ '#title' => t('E-mail address'),
+ '#markup' => '<p>' . check_plain($result['email']) . '</p>',
+ '#required' => TRUE,
+ '#weight' => -1,
+ );
}
}
}
* Verifies assertions.
*/
function browserid_verify() {
- if ($GLOBALS['user']->uid || !isset($_REQUEST['assertion']) || !isset($_REQUEST['audience'])) {
+ $result = browserid_verify_request();
+ if ($result['code'] == BROWSERID_ACCESS_DENIED) {
return MENU_ACCESS_DENIED;
}
+ elseif ($result['code'] == BROWSERID_LOGIN) {
+ drupal_json_output((object) array('reload' => TRUE));
+ }
+ elseif ($result['code'] == BROWSERID_REGISTER) {
+ drupal_json_output((object) array(
+ 'destination' => url('user/register', array('query' => array(
+ 'assertion' => $result['assertion'],
+ 'audience' => $result['audience'],
+ 'token' => $result['token'],
+ ))),
+ ));
+ }
+}
+
+/**
+ * Verifies assertion and token.
+ */
+function browserid_verify_request() {
+ if ($GLOBALS['user']->uid ||
+ !isset($_REQUEST['assertion']) ||
+ !isset($_REQUEST['token']) ||
+ $_REQUEST['token'] != browserid_get_token('browserid-login-csrf')
+ ) {
+ return array('code' => BROWSERID_ACCESS_DENIED);
+ }
+ // The audience (hostname+port) should also be in $_SERVER['HTTP_HOST'] but that is not safe.
+ $audience = $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'];
$response = drupal_http_request(
- 'https://browserid.org/verify?assertion=' . urlencode($_REQUEST['assertion']) . '&audience=' . urlencode($_REQUEST['audience']),
- array('method' => 'POST')
+ 'https://browserid.org/verify',
+ array(
+ 'method' => 'POST',
+ 'data' => 'assertion=' . drupal_encode_path($_REQUEST['assertion']) . '&audience=' . drupal_encode_path($audience),
+ )
);
if ($response->code == 200) {
$data = json_decode($response->data);
if (!empty($account) && !empty($account->uid)) {
$form_state = array('uid' => $account->uid);
user_login_submit(array(), $form_state);
- drupal_json_output((object) array('reload' => TRUE));
+ return array(
+ 'code' => BROWSERID_LOGIN,
+ 'account' => $account,
+ 'assertion' => $_REQUEST['assertion'],
+ 'audience' => $audience,
+ 'token' => $_REQUEST['token'], // no need to regenerate it, we've already checked it's safe
+ );
}
else {
- drupal_json_output((object) array(
- 'destination' => url('user/register', array('query' => array('assertion' => $_REQUEST['assertion'], 'audience' => $_REQUEST['audience']))),
- ));
+ return array(
+ 'code' => BROWSERID_REGISTER,
+ 'email' => $data->email,
+ 'assertion' => $_REQUEST['assertion'],
+ 'audience' => $audience,
+ 'token' => $_REQUEST['token'], // no need to regenerate it, we've already checked it's safe
+ );
}
}
}
+
+/**
+ * Generates a token based on $value, the user session, and the private key.
+ *
+ * Works for anonymous users as well as authenticated users.
+ *
+ * @param $value
+ * An additional value to base the token on.
+ * @see drupal_get_token()
+ */
+function browserid_get_token($value = '') {
+ if (user_is_logged_in()) {
+ return drupal_get_token($value);
+ }
+ return drupal_hmac_base64($value, session_api_get_sid() . drupal_get_private_key() . drupal_get_hash_salt());
+}