You are here

auth0.module in Auth0 Single Sign On 7.2

Same filename and directory in other branches
  1. 8.2 auth0.module
  2. 8 auth0.module

File

auth0.module
View source
<?php

use Auth0\SDK\Auth0;
use Auth0\SDK\API\Authentication;
use Auth0\SDK\API\Management;
use Auth0\SDK\JWTVerifier;

// Define some module default settings
define('AUTH0_WIDGET_CDN', 'https://cdn.auth0.com/js/lock/10.3/lock.min.js');
define('AUTH_LOGIN_CSS', "#a0-widget .a0-panel {\n    min-width: 90%;\n    padding: 5%;\n    box-shadow: none;\n    -webkit-box-shadow: none;\n}\n#a0-widget .a0-panel {\n    background-color: #f6f6f2;\n    border-color: #f9f9f9;\n}");

/**
 * Implements hook_menu().
 */
function auth0_menu() {
  $items = array();

  // Add the callback controller.
  $items['auth0/callback'] = array(
    'description' => 'Callback handler from auth0',
    'page callback' => 'auth0_callback',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['auth0/verify_email'] = array(
    'description' => 'Verify email action',
    'page callback' => 'auth0_verify_email_page',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['user/%user/auth0'] = array(
    'title' => 'Auth0',
    'description' => 'Verify email action',
    'page callback' => 'auth0_user_info_page',
    'page arguments' => array(
      1,
    ),
    'access arguments' => array(
      'administer users',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 100,
  );

  // Add an admin configuration page.
  $items['admin/config/people/auth0'] = array(
    'title' => 'Auth0 Login Settings',
    'description' => 'Configure your auth0 account and widget.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'auth0_basic_settings_form',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
  );

  // Basic configuration tab.
  $items['admin/config/people/auth0/basic'] = array(
    'title' => 'Basic',
    'description' => 'Configure your auth0 account and widget.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'auth0_basic_settings_form',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );

  // Advanced configuration tab.
  $items['admin/config/people/auth0/advanced'] = array(
    'title' => 'Advanced',
    'description' => 'Configure your auth0 account and widget.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'auth0_advanced_settings_form',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 10,
  );
  return $items;
}

/**
 * Display auth0 info for the given user.
 */
function auth0_user_info_page($user) {
  drupal_page_is_cacheable(FALSE);
  if (!auth0_check_dependencies()) {
    return drupal_goto();
  }
  if ($object = auth0_get_auth0_object_from_drupal_uid($user->uid)) {
    if (defined('JSON_PRETTY_PRINT')) {
      return '<pre>' . json_encode($object, JSON_PRETTY_PRINT) . '</pre>';
    }
    else {
      return '<pre>' . print_r($object, TRUE) . '</pre>';
    }
  }
  else {
    return t('This user has not authenticated with Auth0');
  }
}

/**
 * Verify email page callback.
 */
function auth0_verify_email_page() {
  drupal_page_is_cacheable(FALSE);
  if (!auth0_enabled('login')) {
    return drupal_goto();
  }
  $token = $_REQUEST['idToken'];

  /**
   * Validate the ID Token
   */
  $domain = variable_get('auth0_domain', '');
  $client_id = variable_get('auth0_client_id', '');
  $client_secret = variable_get('auth0_client_secret', '');
  $secret_base64_encoded = variable_get('auth0_secret_base64_encoded', FALSE);
  $jwt_signature_alg = variable_get('auth0_jwt_signature_alg', "HS256");
  $auth0_domain = 'https://' . $domain . '/';
  $auth0_settings = array();
  $auth0_settings['authorized_iss'] = [
    $auth0_domain,
  ];
  $auth0_settings['supported_algs'] = [
    $jwt_signature_alg,
  ];
  $auth0_settings['valid_audiences'] = [
    $client_id,
  ];
  $auth0_settings['client_secret'] = $client_secret;
  $auth0_settings['secret_base64_encoded'] = $secret_base64_encoded;
  $jwt_verifier = new JWTVerifier($auth0_settings);
  try {
    $user = $jwt_verifier
      ->verifyAndDecode($token);
  } catch (\Exception $e) {
    drupal_set_message(t('There was a problem re-sending the email.'), 'error');
    watchdog('Auth0', "Error validating the token while resending the email: " . $e
      ->getMessage(), WATCHDOG_ERROR);
    return drupal_goto();
  }
  try {
    $userId = $user->sub;
    $url = "https://{$domain}/api/users/{$userId}/send_verification_email";
    $headers = array(
      'Authorization' => "Bearer {$token}",
    );
    $result = drupal_http_request($url, array(
      'headers' => $headers,
      'method' => 'POST',
    ));
    if ($result->code == 200) {
      drupal_set_message(t('A verification message with further instructions has been sent to your e-mail address.'));
    }
    else {
      drupal_set_message(t('Sorry, we could not send a verification e-mail. Please try again later.'), 'error');
    }
  } catch (Exception $e) {
    drupal_set_message(t('Sorry, we could not send a verification e-mail. Please try again later.'), 'error');
  }
  return drupal_goto();
}

/**
 * User login API callback.
 *
 * Checks the parameters passed by redirection from Auth0 and logs or registers
 * the user if the parameters are valid.
 */
function auth0_callback() {
  drupal_page_is_cacheable(FALSE);
  if (!auth0_enabled('login')) {
    return drupal_goto();
  }

  /* Can these come in a post? */
  $query = drupal_get_query_parameters();
  if (isset($query['error']) && $query['error'] == 'login_required') {
    $authorizeUrl = _auth0_generate_authorize_url(FALSE);

    /* Have to deal with this destination parameter or drupal_goto completely ignores your request to go somewhere other than destination! */
    unset($_GET['destination']);
    drupal_static_reset('drupal_get_destination');
    drupal_get_destination();
    return drupal_goto($authorizeUrl, array(
      'external' => TRUE,
      'absolute' => TRUE,
    ));
  }
  $domain = variable_get('auth0_domain', '');
  $client_id = variable_get('auth0_client_id', '');
  $client_secret = variable_get('auth0_client_secret', '');
  $secret_base64_encoded = variable_get('auth0_secret_base64_encoded', FALSE);
  $jwt_signature_alg = variable_get('auth0_jwt_signature_alg', "HS256");
  $auth0 = new Auth0(array(
    'domain' => $domain,
    'client_id' => $client_id,
    'client_secret' => $client_secret,
    'redirect_uri' => url('auth0/callback', array(
      'absolute' => TRUE,
    )),
    'store' => NULL,
    // Set to null so that the store is set to SessionStore.
    'persist_id_token' => FALSE,
    'persist_user' => FALSE,
    'persist_access_token' => FALSE,
    'persist_refresh_token' => FALSE,
  ));
  $user_info = NULL;
  try {
    $user_info = $auth0
      ->getUser();
    $id_token = $auth0
      ->getIdToken();
  } catch (Exception $e) {
    drupal_set_message(t('There was a problem logging you in, sorry for the inconvenience.'), 'error');
    watchdog('Auth0', 'Error occurred while getting the Auth0 user info or ID token: @exception', array(
      '@exception' => print_r($e, TRUE),
    ), WATCHDOG_ERROR);
    return drupal_goto();
  }

  // var_dump($auth0); die;
  // Check the state
  $query = drupal_get_query_parameters();
  if (!isset($query['state']) || !drupal_valid_token($query['state'], 'auth0_state')) {
    drupal_set_message(t('There was a problem logging you in, sorry for the inconvenience.'), 'error');
    watchdog('Auth0', "Could not validate the state", WATCHDOG_ERROR);
    return drupal_goto();
  }

  /**
   * Validate the ID Token
   */
  $auth0_domain = 'https://' . $domain . '/';
  $auth0_settings = array();
  $auth0_settings['authorized_iss'] = [
    $auth0_domain,
  ];
  $auth0_settings['supported_algs'] = [
    $jwt_signature_alg,
  ];
  $auth0_settings['valid_audiences'] = [
    $client_id,
  ];
  $auth0_settings['client_secret'] = $client_secret;
  $auth0_settings['secret_base64_encoded'] = $secret_base64_encoded;
  $jwt_verifier = new JWTVerifier($auth0_settings);
  try {
    $user = $jwt_verifier
      ->verifyAndDecode($id_token);
  } catch (\Exception $e) {
    drupal_set_message(t('There was a problem logging you in, sorry for the inconvenience.'), 'error');
    watchdog('Auth0', "Error validating the token: " . $e
      ->getMessage(), WATCHDOG_ERROR);
    return drupal_goto();
  }
  $success = FALSE;
  if (isset($user_info['sub']) && !isset($user_info['user_id'])) {
    $user_info['user_id'] = $user_info['sub'];
  }
  if ($user_info) {
    $success = auth0_login_auth0_user($user_info, $id_token);
  }
  if (!$success) {
    drupal_set_message(t('There was a problem logging you in, sorry for the inconvenience.'), 'error');
    watchdog('Auth0', "user_info missing", WATCHDOG_ERROR);
  }
  return drupal_goto();
}

/**
 * Display a message and cancel login if the user does not have a verified email.
 */
function auth0_fail_with_verify_email($idToken) {
  $url = url('auth0/verify_email', array());
  $formText = "<form style='display:none' name='auth0VerifyEmail' action=@url method='post'><input type='hidden' value=@token name='idToken'/></form>";
  $linkText = "<a href='javascript:null' onClick='document.forms[\"auth0VerifyEmail\"].submit();'>here</a>";
  $message = t($formText . "Please verify your email and log in again. Click {$linkText} to resend verification email.", array(
    '@url' => $url,
    '@token' => $idToken,
  ));
  drupal_set_message($message, 'warning');
  return drupal_goto();
}

/**
 * Log in an Auth0 authenticated user.
 */
function auth0_login_auth0_user($user_info, $id_token) {
  $requires_email = variable_get('auth0_requires_email', TRUE);
  $requires_verified_email = $requires_email && variable_get('user_email_verification', TRUE);

  // Allow other modules to modify the Auth0 user before processing the login.
  drupal_alter('auth0_user_pre_login', $user_info, $id_token);

  // Check that the user account has an e-mail address if one is required.
  if ($requires_email && empty($user_info['email'])) {
    return drupal_set_message(t('This account does not have an e-mail address associated with it. Please log in with a different provider.'), 'error');
  }

  // Check that the user has a verified e-mail address if that is required.
  if ($requires_verified_email && isset($user_info['email']) && empty($user_info['email_verified'])) {
    return auth0_fail_with_verify_email($id_token);
  }

  // See if there is a user in the auth0_user table with the user info client id
  function_exists('dd') && dd($user_info['user_id'], 'looking up drupal user by auth0 user_id');
  $uid = auth0_find_auth0_user($user_info['user_id']);
  if ($uid) {
    function_exists('dd') && dd($uid, 'uid of existing drupal user found');

    // The user exists. Update the auth0_user with the new userInfo object.
    auth0_update_auth0_object($user_info);

    // Update field and role mappings
    auth0_update_fields_and_roles($user_info, $uid);

    // Log in the user.
    return auth0_authenticate_user($uid);
  }
  else {
    function_exists('dd') && dd('existing drupal user NOT found');

    // If the user doesn't exist we need to either create a new one, or assign
    // him to an existing one.
    $isDatabaseUser = FALSE;

    /* Make sure we have the identities array, if not, fetch it from the user endpoint */
    $hasIdentities = is_object($user_info) && $user_info
      ->has('identities') || is_array($user_info) && array_key_exists('identities', $user_info);
    if (!$hasIdentities) {
      $mgmtClient = new Management($id_token, variable_get('auth0_domain', ''));
      $user = $mgmtClient->users
        ->get($user_info['user_id']);
      $user_info['identities'] = $user['identities'];
    }
    foreach ($user_info['identities'] as $identity) {
      if ($identity['provider'] == "auth0") {
        $isDatabaseUser = TRUE;
      }
    }
    function_exists('dd') && dd($isDatabaseUser, 'isDatabaseUser');
    $joinUser = FALSE;
    if (variable_get('auth0_join_user_by_mail_enabled', FALSE)) {
      function_exists('dd') && dd($user_info['email'], 'join user by mail is enabled, looking up user by email');

      // If the user has a verified email or is a database user try to see if there is
      // a user to join with. The isDatabase is because we don't want to allow database
      // user creation if there is an existing one with no verified email.
      if (!empty($user_info['email_verified']) || $isDatabaseUser) {
        $joinUser = user_load_by_mail($user_info['email']);
      }
    }
    else {
      function_exists('dd') && dd($user_info['email'], 'join user by mail is not enabled, skipping lookup user by email');
    }
    if ($joinUser) {
      function_exists('dd') && dd($joinUser->uid, 'drupal user found by email with uid');

      // If we are here, we have a potential join user.
      // Don't allow creation or assignation of user if the email is not verified, that would
      // be hijacking.
      if (empty($user_info['email_verified'])) {
        return auth0_fail_with_verify_email($id_token);
      }
      $uid = $joinUser->uid;
    }
    else {

      // If we are here, we need to create the user.
      // Check drupal settings to see if new users are allowed to register.
      if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_ADMINISTRATORS_ONLY) {
        return drupal_set_message(t('Only site administrators can create new user accounts.'), 'error');
      }
      else {
        function_exists('dd') && dd('creating new drupal user from auth0 user');
        $uid = auth0_create_user_from_auth0($user_info);
      }
    }
    function_exists('dd') && dd($uid, 'inserting auth0 user with uid');
    auth0_insert_auth0_user($user_info, $uid);

    // Update field and role mappings
    auth0_update_fields_and_roles($user_info, $uid);

    // Log in the user.
    return auth0_authenticate_user($uid);
  }
  return FALSE;
}

/**
 * Update the field mappings and role mappings for a user based on auth0 user data
 */
function auth0_update_fields_and_roles($user_info, $uid) {
  function_exists('dd') && dd($user_info, 'auth0_update_fields_and_roles called');
  $the_user = user_load($uid);
  function_exists('dd') && dd($the_user, 'the_user before updates');
  $edit = array();
  auth0_update_fields($user_info, $uid, $the_user, $edit);
  auth0_update_roles($user_info, $uid, $the_user, $edit);
  function_exists('dd') && dd($edit, 'values to edit');
  user_save($the_user, $edit);

  //cache_clear_all('menu:'. $uid, TRUE);
  function_exists('dd') && dd(user_load($uid), 'the_user after updates');
}

/*
 * Update the $user profile attributes of a user based on the auth0 field mappings
 */
function auth0_update_fields($user_info, $uid, $the_user, &$edit) {
  $auth0_claim_mapping = variable_get('auth0_claim_mapping');
  function_exists('dd') && dd($auth0_claim_mapping, 'auth0_claim_mapping');
  if (isset($auth0_claim_mapping) && !empty($auth0_claim_mapping)) {

    // For each claim mapping, lookup the value, otherwise set to blank
    $mappings = auth0_pipeListToArray($auth0_claim_mapping);
    function_exists('dd') && dd($mappings, 'auth0_claim_mapping as array');

    // Remove mappings handled automatically by the module
    $skip_mappings = array(
      'uid',
      'name',
      'mail',
      'init',
      'is_new',
      'status',
      'pass',
    );
    foreach ($mappings as $mapping) {
      function_exists('dd') && dd($mapping, 'mapping');
      $key = $mapping[1];
      if (in_array($key, $skip_mappings)) {
        function_exists('dd') && dd($mapping, 'skipping mapping handled already by auth0 module');
      }
      else {
        $value = isset($user_info[$mapping[0]]) ? $user_info[$mapping[0]] : '';

        //array()[LANGUAGE_NONE][0]['value'] = 'foo';
        $edit[$key] = array(
          LANGUAGE_NONE => array(
            0 => array(
              'value' => $value,
            ),
          ),
        );
      }
    }
  }
}

/**
 * Updates the $user->roles of a user based on the auth0 role mappings
 */
function auth0_update_roles($user_info, $uid, $the_user, &$edit) {
  $auth0_claim_to_use_for_role = variable_get('auth0_claim_to_use_for_role');
  if (isset($auth0_claim_to_use_for_role) && !empty($auth0_claim_to_use_for_role)) {
    $claim_value = isset($user_info[$auth0_claim_to_use_for_role]) ? $user_info[$auth0_claim_to_use_for_role] : '';
    function_exists('dd') && dd($claim_value, 'claim_value');
    $claim_values = array();
    if (is_array($claim_value)) {
      $claim_values = $claim_value;
    }
    else {
      $claim_values[] = $claim_value;
    }
    function_exists('dd') && dd($claim_values, 'claim_values');
    $auth0_role_mapping = variable_get('auth0_role_mapping');
    $mappings = auth0_pipeListToArray($auth0_role_mapping);
    function_exists('dd') && dd($mappings, 'auth0_role_mapping as array');
    $roles_granted = array();
    $roles_managed_by_mapping = array();
    foreach ($mappings as $mapping) {
      function_exists('dd') && dd($mapping, 'mapping');
      $roles_managed_by_mapping[] = $mapping[1];
      if (in_array($mapping[0], $claim_values)) {
        $roles_granted[] = $mapping[1];
      }
    }
    $roles_granted = array_unique($roles_granted);
    $roles_managed_by_mapping = array_unique($roles_managed_by_mapping);
    function_exists('dd') && dd($roles_granted, 'roles_granted');
    function_exists('dd') && dd($roles_managed_by_mapping, 'roles_managed_by_mapping');
    $not_granted = array_diff($roles_managed_by_mapping, $roles_granted);
    function_exists('dd') && dd($not_granted, 'not_granted');
    $user_roles = $the_user->roles;
    function_exists('dd') && dd($user_roles, 'user_roles');
    $new_user_roles = array_merge(array_diff($user_roles, $not_granted), $roles_granted);
    function_exists('dd') && dd($new_user_roles, 'new_user_roles');
    $tmp = array_diff($new_user_roles, $user_roles);
    if (!empty($tmp)) {
      $new_user_roles_map = array();
      foreach ($new_user_roles as $new_role) {
        $role = user_role_load_by_name($new_role);
        $new_user_roles_map[$role->rid] = $role->name;
      }
      function_exists('dd') && dd($new_user_roles_map, 'changes to roles detected');
      $edit['roles'] = $new_user_roles_map;
      $the_user->roles = $new_user_roles_map;
    }
  }
}
function auth0_mappingsToPipeList($mappings) {
  $result_text = "";
  foreach ($mappings as $map) {
    $result_text .= $map['from'] . '|' . $map['user_entered'] . "\n";
  }
  return $result_text;
}
function auth0_pipeListToArray($mapping_list_txt, $make_item0_lowercase = FALSE) {
  $result_array = array();
  $mappings = preg_split('/[\\n\\r]+/', $mapping_list_txt);
  foreach ($mappings as $line) {
    if (count($mapping = explode('|', trim($line))) == 2) {
      $item_0 = $make_item0_lowercase ? drupal_strtolower(trim($mapping[0])) : trim($mapping[0]);
      $result_array[] = array(
        $item_0,
        trim($mapping[1]),
      );
    }
  }
  return $result_array;
}

/**
 * Authenticate the given user.
 *
 * We use our own login form because user_external_login loads the login form which
 * we are modifying.
 */
function auth0_authenticate_user($uid) {
  $form_state['uid'] = $uid;
  user_login_submit(array(), $form_state);
  return TRUE;
}

/**
 * Implements hook_user().
 */
function auth0_user($op, &$edit, &$account, $category = NULL) {
  if ($op == 'delete') {
    return auth0_user_delete($account);
  }
}

/**
 * Implements hook_user_delete().
 *
 * Removes the user from the auth0_user table.
 */
function auth0_user_delete($account) {
  db_delete('auth0_user')
    ->condition('drupal_id', $account->uid, '=')
    ->execute();
}

/**
 * Return the uid of the user with the given Auth0 id.
 */
function auth0_find_auth0_user($id) {
  $rs = db_select('auth0_user', 'a')
    ->fields('a', array(
    'drupal_id',
  ))
    ->condition('auth0_id', $id, '=')
    ->execute()
    ->fetchAssoc();
  return empty($rs) ? FALSE : $rs['drupal_id'];
}

/**
 * Return the uid of the user with the given Auth0 id.
 */
function auth0_get_auth0_object_from_drupal_uid($uid) {
  $rs = db_select('auth0_user', 'a')
    ->fields('a')
    ->condition('drupal_id', $uid, '=')
    ->execute()
    ->fetch();
  if (!empty($rs)) {
    $rs = drupal_unpack($rs, 'auth0_object');
    unset($rs->auth0_object);
    return $rs;
  }
  return FALSE;
}

/**
 * Save changes to the local cache of an Auth0 user object.
 */
function auth0_update_auth0_object($user_info) {
  db_update('auth0_user')
    ->fields(array(
    'auth0_object' => serialize($user_info),
  ))
    ->condition('auth0_id', $user_info['user_id'], '=')
    ->execute();
}

/**
 * Create a local cached Auth0 user object.
 */
function auth0_insert_auth0_user($user_info, $uid) {
  $auth0_user = array(
    'auth0_id' => $user_info['user_id'],
    'drupal_id' => $uid,
    'auth0_object' => serialize($user_info),
  );
  drupal_write_record('auth0_user', $auth0_user);
}

/**
 * Create a new Drupal user for an authenticated Auth0 user.
 */
function auth0_create_user_from_auth0($user_info) {
  $user = new stdClass();
  if (isset($user_info['email']) && !empty($user_info['email'])) {
    $email = $user_info['email'];
  }
  else {
    $email = "";
  }
  $user->mail = $email;
  $user->init = $email;

  // If the username already exists, create a new random one.
  $username = $user_info['nickname'];
  function_exists('dd') && dd($username, 'checking if drupal user already exists with auth0 nickname');
  if (user_load_by_name($username)) {
    $username .= time();
    function_exists('dd') && dd($username, 'existing drupal user found, using new random name');
  }
  $user->name = $username;
  $user->is_new = TRUE;

  // If auto_register from auth0 is enabled, they are active immediately, otherwise check the site registration settings
  $auth0_auto_register = variable_get('auth0_auto_register', FALSE);
  if ($auth0_auto_register) {
    $user->status = TRUE;
  }
  else {
    $user->status = variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS;
  }
  $user->pass = user_password();
  function_exists('dd') && dd($user, 'saving new drupal user');
  $new_user = user_save($user);
  if ($user) {
    watchdog('Auth0', 'Account created for %name', array(
      '%name' => $user->name,
    ), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $user->uid . '/edit'));
  }

  // Notify the user if they must have approval.
  if (!$user->status) {
    drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'));
  }
  return $new_user->uid;
}

/**
 * Implements hook_theme().
 *
 * Define the template to use for the /user action.
 */
function auth0_theme() {
  return array(
    'auth0_lock' => array(
      'variables' => array(
        'mode' => 'signin',
      ),
      'template' => 'auth0-lock',
    ),
  );
}

/**
 * The Auth0 basic configuration settings form callback.
 */
function auth0_basic_settings_form($form, &$form_state) {
  if (!auth0_check_dependencies()) {

    // Set message.
    auth0_missing_dependencies_message();
  }
  $form['auth0_domain'] = array(
    '#type' => 'textfield',
    '#title' => t('Domain'),
    '#default_value' => variable_get('auth0_domain', ''),
    '#description' => t('Your Auth0 domain, you can see it in the auth0 dashboard.'),
    '#required' => TRUE,
  );
  $form['auth0_client_id'] = array(
    '#type' => 'textfield',
    '#title' => t('Client id'),
    '#default_value' => variable_get('auth0_client_id', ''),
    '#description' => t('Application id, copy from the auth0 dashboard.'),
    '#required' => TRUE,
  );
  $form['auth0_client_secret'] = array(
    '#type' => 'textfield',
    '#title' => t('Client secret'),
    '#default_value' => variable_get('auth0_client_secret', ''),
    '#description' => t('Application secret, copy from the auth0 dashboard.'),
    '#required' => TRUE,
  );
  $form['auth0_secret_base64_encoded'] = array(
    '#type' => 'checkbox',
    '#title' => t('Client Secret is Base64 Encoded'),
    '#default_value' => variable_get('auth0_secret_base64_encoded', FALSE),
    '#description' => t('This is stated below the client secret in your Auth0 Dashboard for the client.  If your client was created after September 2016, this should be false.'),
  );
  $form['auth0_jwt_signature_alg'] = array(
    '#type' => 'select',
    '#title' => t('JWT Signature Algorithm'),
    '#options' => [
      'HS256' => t('HS256'),
      'RS256' => t('RS256'),
    ],
    '#default_value' => variable_get('auth0_jwt_signature_alg', 'HS256'),
    '#description' => t('Your JWT Signing Algorithm for the ID token.  RS256 is recommended, but must be set in the advanced settings under oauth for this client.'),
    '#required' => TRUE,
  );
  return system_settings_form($form);
}

/**
 * The Auth0 advanced configuration settings form callback.
 */
function auth0_advanced_settings_form($form, &$form_state) {
  if (!auth0_check_dependencies()) {

    // Set message.
    auth0_missing_dependencies_message();
  }
  $form['auth0_replace_forms'] = array(
    '#type' => 'checkbox',
    '#title' => t('Replace default Drupal login, registration, and password reset forms'),
    '#default_value' => variable_get('auth0_replace_forms', TRUE),
    '#description' => t('Uncheck this box to disable replacement of the default Drupal login, registration, and password reset forms with the Auth0 Lock login widget. This allows maintaining the option to login with a Drupal username and password.'),
  );

  // Text field for the e-mail subject.
  $form['auth0_form_title'] = array(
    '#type' => 'textfield',
    '#title' => t('Form title'),
    '#default_value' => variable_get('auth0_form_title', 'Sign In'),
    '#description' => t('This is the title for the login widget.'),
  );
  $form['auth0_allow_signup'] = array(
    '#type' => 'checkbox',
    '#title' => t('Allow user signup'),
    '#default_value' => variable_get('auth0_allow_signup', TRUE),
    '#description' => t('If you have database connection you can allow users to signup using the Auth0 widget.'),
  );
  $form['auth0_widget_cdn'] = array(
    '#type' => 'textfield',
    '#title' => t('Widget CDN'),
    '#default_value' => variable_get('auth0_widget_cdn', AUTH0_WIDGET_CDN),
    '#description' => t('Point this to the latest widget available in the CDN.'),
  );
  $form['auth0_requires_email'] = array(
    '#type' => 'checkbox',
    '#title' => t('Require an e-mail account'),
    '#default_value' => variable_get('auth0_requires_email', TRUE),
    '#description' => t('Require the user to have an e-mail address to login.'),
  );
  $form['auth0_join_user_by_mail_enabled'] = array(
    '#type' => 'checkbox',
    '#title' => t('Link auth0 logins to drupal users by email address'),
    '#default_value' => variable_get('auth0_join_user_by_mail_enabled', FALSE),
    '#description' => t('If enabled, when a user logs into Drupal for the first time, the system will use the email
address of the Auth0 user to search for a drupal user with the same email address and setup a link to that
Drupal user account.
<br/>If not enabled, then a new Drupal user will be created even if a Drupal user with the same email address already exists.
'),
  );
  $form['auth0_sso'] = array(
    '#type' => 'checkbox',
    '#title' => t('SSO enabled'),
    '#default_value' => variable_get('auth0_sso', FALSE),
    '#description' => t('Enable Auth0 <a href="@url">Single Sign On</a> for this site.', array(
      '@url' => 'https://auth0.com/docs/sso/single-sign-on',
    )),
  );
  $form['auth0_login_css'] = array(
    '#type' => 'textarea',
    '#title' => t('Login widget CSS'),
    '#default_value' => variable_get('auth0_login_css', AUTH_LOGIN_CSS),
    '#description' => t('This CSS controls the widget look and feel.'),
  );

  // Add option to have the logout url at the application level or account level
  $form['auth0_returnTo_app'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use returnTo URLs at the App Level'),
    '#default_value' => variable_get('auth0_returnTo_app', FALSE),
    '#description' => t('Check this box to use the <a href="@url">returnTo URLs</a> at the Account Level', array(
      '@url' => 'https://auth0.com/docs/logout#redirecting-users-after-logout',
    )),
  );
  $form['auth0_lock_extra_settings'] = array(
    '#type' => 'textarea',
    '#title' => t('Lock extra setting'),
    '#default_value' => variable_get('auth0_lock_extra_settings'),
    '#description' => t('This should be a valid JSON file. This entire object will be passed to the lock options parameter.'),
  );
  $form['auth0_auto_register'] = array(
    '#type' => 'checkbox',
    '#title' => t('Auto Register Auth0 users (ignore site registration settings)'),
    '#default_value' => variable_get('auth0_auto_register', FALSE),
    '#description' => t('Enable this option if you want new auth0 users to automatically be activated within Drupal regardless of the global site visitor registration settings (e.g. requiring admin approval).'),
  );

  // Enhancement to support mapping claims to user attributes and to roles
  $form['auth0_claim_mapping'] = array(
    '#type' => 'textarea',
    '#title' => t('Mapping of Claims to Profile Fields (one per line):'),
    '#cols' => 50,
    '#rows' => 5,
    '#default_value' => variable_get('auth0_claim_mapping'),
    '#description' => t('Enter claim mappings here in the format &lt;claim_name>|&lt;profile_field_name> (one per line), e.g:
<br/>
<br/>given_name|field_first_name
<br/>family_name|field_last_name
<br/>
<br/>NOTE: the following Drupal fields are handled automatically and will be ignored if specified above:
<br/>    uid, name, mail, init, is_new, status, pass
'),
  );
  $form['auth0_claim_to_use_for_role'] = array(
    '#type' => 'textfield',
    '#title' => t('Claim for Role Mapping:'),
    '#default_value' => variable_get('auth0_claim_to_use_for_role'),
    '#description' => t('Name of the claim to use to map to Drupal roles, e.g. roles.  If the claim contains a list of values, all values will be used in the mappings below.'),
  );
  $form['auth0_role_mapping'] = array(
    '#type' => 'textarea',
    '#title' => t('Mapping of Claim Role Values to Drupal Roles (one per line)'),
    '#default_value' => variable_get('auth0_role_mapping'),
    '#description' => t('Enter role mappings here in the format &lt;auth0 claim value>|&lt;drupal role name> (one per line), e.g.:
<br/>
<br/>admin|administrator
<br/>poweruser|power users
<br/>
<br/>NOTE: for any drupal role in the mapping, if a user is not mapped to the role, the role will be removed from their profile.
Drupal roles not listed above will not be changed by this module.
'),
  );
  return system_settings_form($form);
}

/**
 * Implements hook_user_logout().
 *
 * Logs the user out of Auth0 if SSO is in use.
 */
function auth0_user_logout($account) {

  // If Single Sign On is enabled then log the user out from Auth0.
  if (variable_get("auth0_sso", FALSE)) {
    session_destroy();
    $domain = check_plain(variable_get("auth0_domain", ''));
    if (variable_get("auth0_returnTo_app", FALSE)) {
      $client = check_plain(variable_get("auth0_client_id", ''));
      drupal_goto("https://{$domain}/v2/logout?returnTo=" . urlencode(url('<front>', array(
        'absolute' => TRUE,
      ))) . "&client_id={$client}");
    }
    else {
      drupal_goto("https://{$domain}/v2/logout?returnTo=" . urlencode(url('<front>', array(
        'absolute' => TRUE,
      ))));
    }
  }
}

/**
 * Implements hook_form_alter().
 *
 * Replace the user login forms with the Auth0 login widget.
 */
function auth0_form_alter(&$form, $form_state, $form_id) {

  // If replacing the forms is disabled, then skip making alterations.
  if (!variable_get('auth0_replace_forms', TRUE)) {
    return;
  }
  if (($form_id == 'user_login_block' || $form_id == 'user_login') && auth0_enabled('login')) {
    _auth0_form_replace_with_lock($form, 'signin');
  }

  // If Auth0 controls the user database.
  if ($form_id == 'user_register_form' && auth0_enabled('signup')) {
    _auth0_form_replace_with_lock($form, 'signup');
  }
  if ($form_id == 'user_pass' && auth0_enabled('reset')) {
    _auth0_form_replace_with_lock($form, 'reset');
  }

  // If the settings say to remove the signup altogether.
  if (!variable_get('auth0_allow_signup', '')) {
    if ($form_id == 'user_register_form' || $form_id == 'user_pass') {

      // @TODO: Remove the user signup option.
      drupal_goto('user/login');
    }
  }
}

/**
 * Implements hook_user_form_user_profile_form_alter().
 *
 * Disable email and password fields for users who have logged in with Auth0.
 */
function auth0_form_user_profile_form_alter(&$form, $form_state) {
  $user = $form_state['user'];
  if ($object = auth0_get_auth0_object_from_drupal_uid($user->uid)) {

    // If the user has an Auth0 profile then we simply disable the password/email fields.
    // If this account was created without an email address hide the field,
    // otherwise show it but disable it.
    if ($user->mail) {
      $form['account']['mail']['#disabled'] = TRUE;
    }
    else {
      $form['account']['mail']['#access'] = FALSE;
    }

    // Remove the password field.
    $form['account']['pass']['#access'] = FALSE;

    // If there is no way to edit the mail/pass then we don't need the current pass f
    if (isset($form['account']['current_pass'])) {
      $form['account']['current_pass']['#access'] = FALSE;
    }

    // @TODO: Reenable the password/email editing ability if the connection providor is Auth0
    // This will require using the API to update the info in Auth0
  }
}
function _auth0_generate_authorize_url($prompt) {
  $query = array(
    'redirect_uri' => url('auth0/callback', array(
      'absolute' => TRUE,
      'query' => drupal_get_destination(),
    )),
    'connection' => null,
    'response_type' => 'code',
  );

  /* Overwrite values if needed */
  if (is_array($prompt)) {
    $query = array_merge($query, $prompt);
  }

  /* Set the state, if not passed in */
  if (!isset($_SESSION['auth0_session_started'])) {
    $_SESSION['auth0_session_started'] = TRUE;
    drupal_session_start();
  }
  $state = isset($query['state']) ? $query['state'] : drupal_get_token('auth0_state');
  $response_type = $query['response_type'];
  $redirect_uri = $query['redirect_uri'];
  $connection = $query['connection'];
  unset($query['state']);
  unset($query['response_type']);
  unset($query['redirect_uri']);
  unset($query['connection']);
  $additional_params = [];
  $additional_params['scope'] = 'openid profile email';
  $additional_params = array_merge($additional_params, $query);
  $domain = check_plain(variable_get("auth0_domain", ''));
  $client_id = check_plain(variable_get("auth0_client_id", ''));
  $auth0Api = new Authentication($domain, $client_id);
  return $auth0Api
    ->get_authorize_link($response_type, $redirect_uri, $connection, $state, $additional_params);
}

/**
 * Replace a form with the lock widget.
 */
function _auth0_form_replace_with_lock(&$form, $mode = 'signin') {

  // Remove the old form elements.
  foreach (element_children($form) as $child) {
    unset($form[$child]);
  }

  // Add an Auth0 Lock widget.
  $form['auth0'] = array(
    '#type' => 'markup',
    '#markup' => theme('auth0_lock', array(
      'mode' => $mode,
    )),
  );
}

/**
 * Preprocess the login widget.
 */
function template_preprocess_auth0_lock(&$vars) {
  $vars['sso_enabled'] = (bool) variable_get("auth0_sso", FALSE);
  $vars['domain'] = check_plain(variable_get("auth0_domain", ''));
  $vars['client_id'] = check_plain(variable_get("auth0_client_id", ''));
  $vars['lock_extra_settings'] = json_decode(variable_get("auth0_lock_extra_settings", ''), true);
  if ($vars['lock_extra_settings'] === null) {
    $vars['lock_extra_settings'] = array();
  }
  $vars['params'] = $vars['lock_extra_settings'];
  $vars['params']['container'] = isset($vars['params']['container']) ? $vars['params']['container'] : 'auth0-login-form';
  $vars['params']['sso'] = isset($vars['sso_enabled']) ? $vars['sso_enabled'] : false;
  $vars['params']['auth'] = isset($vars['params']['auth']) ? $vars['params']['auth'] : array();
  $vars['params']['auth']['redirectUrl'] = isset($vars['params']['auth']['redirectUrl']) ? $vars['params']['auth']['redirectUrl'] : url('auth0/callback', array(
    'absolute' => TRUE,
    'query' => drupal_get_destination(),
  ));
  $vars['params']['auth']['responseType'] = isset($vars['params']['auth']['responseType']) ? $vars['params']['auth']['responseType'] : 'code';
  $vars['params']['auth']['params'] = isset($vars['params']['auth']['params']) ? $vars['params']['auth']['params'] : array();
  $vars['params']['auth']['params']['scope'] = isset($vars['params']['auth']['params']['scope']) ? $vars['params']['auth']['params']['scope'] : 'openid email';
  if (!isset($_SESSION['auth0_session_started'])) {
    $_SESSION['auth0_session_started'] = TRUE;
    drupal_session_start();
  }
  $vars['params']['auth']['params']['state'] = isset($vars['params']['auth']['params']['state']) ? $vars['params']['auth']['params']['state'] : drupal_get_token('auth0_state');
  if (auth0_enabled('signup')) {
    $vars['params']['allowSignUp'] = TRUE;
  }
  else {
    $vars['params']['allowSignUp'] = FALSE;
  }
  if (auth0_enabled('reset')) {
    $vars['params']['disableResetAction'] = TRUE;
  }
  else {
    $vars['params']['disableResetAction'] = FALSE;
  }
  drupal_alter('auth0_params', $vars['params']);

  // Generate a link to a login form to be displayed as a no-js fallback.
  if (isset($vars['params']['auth']['params']['state'])) {
    $query = array(
      'redirect_uri' => $vars['params']['auth']['redirectUrl'],
      'state' => $vars['params']['auth']['params']['state'],
    );
  }
  else {
    $query = array(
      'redirect_uri' => $vars['params']['auth']['redirectUrl'],
    );
  }

  // If auth0_sso is enabled, redirect to the hosted login page
  if (variable_get("auth0_sso", FALSE)) {
    $query['prompt'] = 'none';
    $vars['login_link'] = l(t('Log in'), _auth0_generate_authorize_url($query));

    // Bail early so the login link is all that is shown.
    return;
  }
  else {
    $vars['login_link'] = l(t('Log in'), _auth0_generate_authorize_url($query));
  }

  // Add the custom css if specified.
  if ($css = variable_get("auth0_login_css", AUTH_LOGIN_CSS)) {
    drupal_add_css($css, array(
      'type' => 'inline',
    ));
  }

  // Add the lock.js library from the specified CDN.
  drupal_add_js(filter_var(variable_get('auth0_widget_cdn', AUTH0_WIDGET_CDN), FILTER_VALIDATE_URL), 'external');

  // Add the auth0 js settings.
  drupal_add_js(array(
    'auth0' => array(
      'client_id' => $vars['client_id'],
      'domain' => $vars['domain'],
      'options' => $vars['params'],
    ),
  ), 'setting');

  // Add the Drupal behavior to initialize the widget.
  drupal_add_js(drupal_get_path('module', 'auth0') . '/auth0.lock.js');
}

/**
 * Determine if Auth0 is enabled and can be used.
 */
function auth0_enabled($operation = '') {
  if (!auth0_check_dependencies()) {
    return FALSE;
  }
  $out = FALSE;

  // Check that the module has been configured.
  if (variable_get("auth0_domain", '') && variable_get("auth0_client_id", '') && variable_get('auth0_client_secret', '')) {

    // Default to on if the module is configured.
    $out = TRUE;

    // See if our settings allow us to override the registration form.
    if ($operation == 'signup' || $operation == 'reset') {
      $out = (bool) variable_get('auth0_allow_signup', '');
    }
  }

  // Allow other modules to override the status.
  drupal_alter('auth0_enabled', $out, $operation);
  return $out;
}

/**
 * Check that the dependencies were autoloaded.
 */
function auth0_check_dependencies() {
  if (class_exists('\\Auth0SDK\\Auth0')) {
    return TRUE;
  }
  if (file_exists(DRUPAL_ROOT . '/' . drupal_get_path('module', 'auth0') . '/vendor/autoload.php')) {
    require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'auth0') . '/vendor/autoload.php';
    return TRUE;
  }
  return FALSE;
}

/**
 * Set a message explaining how to install the dependencies.
 */
function auth0_missing_dependencies_message() {
  drupal_set_message(t('Auth0 is not fully installed. See the module\'s INSTALL.txt file for installation instructions.', array(
    '!url' => 'https://www.drupal.org/project/composer_manager',
  )), 'warning');
}

/**
 * Implements hook_block_info().
 *
 * Define a block with the Auth0 Lock widget.
 */
function auth0_block_info() {
  $blocks['auth0_lock'] = array(
    'info' => t('Auth0 Lock widget'),
    'cache' => DRUPAL_CACHE_GLOBAL,
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 *
 * Provide output for the Auth0 Lock block.
 */
function auth0_block_view($delta = '') {
  global $user;
  $block = array();
  switch ($delta) {
    case 'auth0_lock':
      if (!$user->uid) {
        $block['subject'] = '';
        $block['content'] = array(
          '#type' => 'markup',
          '#markup' => theme('auth0_lock', array(
            'mode' => 'signin',
          )),
        );
      }
      break;
  }
  return $block;
}

Functions

Namesort descending Description
auth0_advanced_settings_form The Auth0 advanced configuration settings form callback.
auth0_authenticate_user Authenticate the given user.
auth0_basic_settings_form The Auth0 basic configuration settings form callback.
auth0_block_info Implements hook_block_info().
auth0_block_view Implements hook_block_view().
auth0_callback User login API callback.
auth0_check_dependencies Check that the dependencies were autoloaded.
auth0_create_user_from_auth0 Create a new Drupal user for an authenticated Auth0 user.
auth0_enabled Determine if Auth0 is enabled and can be used.
auth0_fail_with_verify_email Display a message and cancel login if the user does not have a verified email.
auth0_find_auth0_user Return the uid of the user with the given Auth0 id.
auth0_form_alter Implements hook_form_alter().
auth0_form_user_profile_form_alter Implements hook_user_form_user_profile_form_alter().
auth0_get_auth0_object_from_drupal_uid Return the uid of the user with the given Auth0 id.
auth0_insert_auth0_user Create a local cached Auth0 user object.
auth0_login_auth0_user Log in an Auth0 authenticated user.
auth0_mappingsToPipeList
auth0_menu Implements hook_menu().
auth0_missing_dependencies_message Set a message explaining how to install the dependencies.
auth0_pipeListToArray
auth0_theme Implements hook_theme().
auth0_update_auth0_object Save changes to the local cache of an Auth0 user object.
auth0_update_fields
auth0_update_fields_and_roles Update the field mappings and role mappings for a user based on auth0 user data
auth0_update_roles Updates the $user->roles of a user based on the auth0 role mappings
auth0_user Implements hook_user().
auth0_user_delete Implements hook_user_delete().
auth0_user_info_page Display auth0 info for the given user.
auth0_user_logout Implements hook_user_logout().
auth0_verify_email_page Verify email page callback.
template_preprocess_auth0_lock Preprocess the login widget.
_auth0_form_replace_with_lock Replace a form with the lock widget.
_auth0_generate_authorize_url

Constants