I'm in the process of writing a custom user registration module for Drupal 7. I currently have my own custom form which calls the function user_save within it's submit handler to create the new user account. The account is initially created as "disabled", and will only be activated once the user verified their account via a link in a custom welcome email.

I have generated a token to append to the URL query string (contained in the email generated by hook_mail() using drupal_get_token()):

$myToken = drupal_get_token('email_verify_'.$params->uid);

Unfortunately I'm always getting an Access Denied message when following the registration link within my custom email. I've checked both values storing the UID within the hook_mail and page callback and they're indeed correct.

Is there anything I'm missing here? I considered using user_pass_reset_url() instead, but we capture the user password on registration so we don't want them to have to re-enter their password twice.

Thanks in advance!

EDIT #2: adding more code for Pontus

hook_menu implementation:

//callback to verify user via email link
$items['custom_user/signup/verify/%'] = array(
  'title' => 'Activate your Account',
  'page callback' => 'customer_user_signup_verify_email_page',
  'page arguments' => array(3),
  'file' => 'includes/custom_user_signup.verify.inc',
  'access arguments' => array('access content'),
  'type' => MENU_CALLBACK,

Check if valid token within customer_user_signup_verify_email_page() callback function drupal_valid_token():

if (empty($_GET['tok']) || !drupal_valid_token($_GET['tok'], 'email_verify_' . $user_id)) {
    //enable account now that we know you're a valid user

URL printed in email:

  • What's the URL to your page callback? Could you post more code? Commented Jul 30, 2013 at 8:17
  • Thanks for the reply. Please see my editted post for the callback URL and email URL. I just double checked to make sure that the UID arg is coming through correctly in hook_mail and my page callback, and they're both matching. Commented Jul 30, 2013 at 10:09
  • I've had a closer look at the function drupal_valid_token(), and it seems to use the SESSION ID to see whether a token is valid or not. Would opening the email containing the link create a completely new session? If it does this might be why the tokens are mis matching. Commented Jul 30, 2013 at 10:13
  • Could you post the whole implementation of your hook_menu? What does your access argument / access callback say? Commented Jul 30, 2013 at 10:53
  • added the full hook_menu implementation for you. The access arguments are 'access content' to allow any anonymous user to use this route (tested and working). I then check if the token is valid within the callback to make sure the link is being visited via the email link. thanks again Commented Jul 30, 2013 at 11:03

1 Answer 1


The reason this is failing is because drupal_get_token() and drupal_valid_token() use the value of session_id() to generate and validate the token.

The problem is that session_id() will return a different value for anonymous users on each page load. So the token generated by drupal_valid_token() is different from the one that is generated in drupal_get_token() because the input (sessoin_id()) is different.

For form submits where you want to compare a token on the next page load for anonymous users, you can call drupal_session_start() on the form page which will start a session and ensure that the results of session_id() are the same on the form page and the result page. But in your case, where the user is clicking on a link back to the site from an email, the value of session_id() will only be the same if it's the exact next page load. My tests indicate that Drupal destroys the session created by drupal_session_start() on the next page load.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.