5
votes

I have been following Ryan Boland's excellent Rails multitenancy tutorial, but have run into a snag with devise_invitable. I am using...

Rails 4.1.5 
devise 3.3.0  
devise_invitable 1.3.6
Postgresql

I create a new account and user/account owner on a chosen subdomain (mysubdomain.lvh.me:3000), from which I can send a user invitation just fine. I open the invitation link in an incognito Chrome session to ensure I am not logged in or have any current session. Upon clicking on the invitation link, I am redirected to the sign in page (mysubdomain.lvh.me:3000/users/sign_in) and see a flash notice: "The invitation token provided is not valid!"

I am using a very simple mailer view (app/views/devise/mailer/invitation_instructions.html.erb)...

<%= link_to 'Accept invitation', accept_invitation_url(@resource, :invitation_token => @token) %>

As you can see, I ensured the use of @token, as described here.

Upon creating the invitation, I have confirmed the invitation token is saved to the database (in this case for [email protected] - d1801fd8df78bd8cd125d5d8091fdc6a72c8f8faf4136cb282d497ec612195e9). I have confirmed this matches the token on invitation lookup upon acceptance request (see below traces). Still, it redirects to user sign in page rather than completing sign up, and also displays in the trace log "Filter chain halted as :resource_from_invitation_token rendered or redirected". The user remains uncomfirmed in the end after this transaction.

Any ideas on what might be going wrong for me here? I am including logs, my application controller, and my devise config below...

Here is the trace log for the invitation creation:

Started POST "/users/invitation" for 127.0.0.1 at 2014-09-07 01:28:33 +0800
Processing by Devise::InvitationsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"BiIQ95wwdQz3CJ0+OoLOE9xHHvxhloHsRHrxsqf1D2Q=", "user"=>{"email"=>"[email protected]"}, "commit"=>"Invite User"}
  User Load (4.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = 1  ORDER BY "users"."id" ASC LIMIT 1
  Account Load (0.4ms)  SELECT  "public"."accounts".* FROM "public"."accounts"  WHERE "public"."accounts"."subdomain" = 'mysubdomain' LIMIT 1
  User Load (0.7ms)  SELECT  "users".* FROM "users"  WHERE "users"."email" = '[email protected]'  ORDER BY "users"."id" ASC LIMIT 1
  User Load (0.7ms)  SELECT  "users".* FROM "users"  WHERE "users"."invitation_token" = 'd1801fd8df78bd8cd125d5d8091fdc6a72c8f8faf4136cb282d497ec612195e9'  ORDER BY "users"."id" ASC LIMIT 1
   (0.1ms)  BEGIN
  SQL (0.5ms)  INSERT INTO "users" ("created_at", "email", "invitation_created_at", "invitation_sent_at", "invitation_token", "invited_by_id", "invited_by_type", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"  [["created_at", "2014-09-06 17:28:34.296123"], ["email", "[email protected]"], ["invitation_created_at", "2014-09-06 17:28:34.294987"], ["invitation_sent_at", "2014-09-06 17:28:34.294987"], ["invitation_token", "d1801fd8df78bd8cd125d5d8091fdc6a72c8f8faf4136cb282d497ec612195e9"], ["invited_by_id", 1], ["invited_by_type", "User"], ["updated_at", "2014-09-06 17:28:34.296123"]]
   (2.2ms)  COMMIT
  Rendered devise/mailer/invitation_instructions.html.erb (1.3ms)

Devise::Mailer#invitation_instructions: processed outbound mail in 23.5ms

Sent mail to [email protected] (26.0ms)
Date: Sun, 07 Sep 2014 01:28:34 +0800
From: [email protected]
Reply-To: [email protected]
To: [email protected]
Message-ID: <...>
Subject: Invitation instructions
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

<a href="http://mysubdomain.lvh.me:3000/users/invitation/accept?invitation_token=3GXDmi7NntDRdhvo57q5">Accept invitation</a>
Redirected to http://mysubdomain.lvh.me:3000/users
Completed 302 Found in 888ms (ActiveRecord: 10.0ms)

Here is the trace upon following the invitation link...

Started GET "/users/invitation/accept?invitation_token=3GXDmi7NntDRdhvo57q5" for 127.0.0.1 at 2014-09-07 01:28:38 +0800
Processing by Devise::InvitationsController#edit as HTML
  Parameters: {"invitation_token"=>"3GXDmi7NntDRdhvo57q5"}
  User Load (0.6ms)  SELECT  "users".* FROM "users"  WHERE "users"."invitation_token" = 'd1801fd8df78bd8cd125d5d8091fdc6a72c8f8faf4136cb282d497ec612195e9'  ORDER BY "users"."id" ASC LIMIT 1
Redirected to http://mysubdomain.lvh.me:3000/users/sign_in
Filter chain halted as :resource_from_invitation_token rendered or redirected
Completed 302 Found in 5ms (ActiveRecord: 0.6ms)


Started GET "/users/sign_in" for 127.0.0.1 at 2014-09-07 01:28:38 +0800
Processing by Devise::SessionsController#new as HTML
  Account Load (0.4ms)  SELECT  "public"."accounts".* FROM "public"."accounts"  WHERE "public"."accounts"."subdomain" = 'mysubdomain' LIMIT 1
  Rendered devise/shared/_links.erb (0.7ms)
  Rendered devise/sessions/new.html.erb within layouts/application (4.4ms)
Completed 200 OK in 21ms (Views: 16.6ms | ActiveRecord: 1.3ms)

Here is my application_controller for good measure...

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception
  before_filter :load_schema, :authenticate_user!, :set_mailer_host
  before_filter :configure_permitted_parameters, if: :devise_controller?


  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:first_name, :last_name, :company, :email, :password, :password_confirmation, :remember_me, :image, :image_cache)}
    devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:first_name, :last_name, :company, :email, :password_confirmation, :current_password, :image, :image_cache)}
  end

private
  def load_schema
    Apartment::Database.switch('public')
    return unless request.subdomain.present?

    if current_account
      Apartment::Database.switch(current_account.subdomain)
    else
      redirect_to root_url(subdomain: false)
    end
  end  

  def current_account
    @current_account ||= Account.find_by(subdomain: request.subdomain)
  end
  helper_method :current_account

  def set_mailer_host
    subdomain = current_account ? "#{current_account.subdomain}." : ""
    ActionMailer::Base.default_url_options[:host] = "#{subdomain}lvh.me:3000"
  end

  def after_sign_out_path_for(resource_or_scope)
    new_user_session_path
  end

  def after_invite_path_for(resource)
    users_path
  end

end

Here is my Devise initializer (config/initializers/devise.rb), I have added the line "config.allow_insecure_token_lookup = true" to see if this helps, but to no avail...

Devise.setup do |config|

  config.mailer_sender = '[email protected]'

  require 'devise/orm/active_record'

  config.case_insensitive_keys = [ :email ]

  config.strip_whitespace_keys = [ :email ]

  config.skip_session_storage = [:http_auth]

  config.stretches = Rails.env.test? ? 1 : 10

  config.reconfirmable = true

  config.expire_all_remember_me_on_sign_out = true

  config.password_length = 8..128

  config.sign_out_via = :delete

  config.allow_insecure_token_lookup = true
end
1

1 Answers

1
votes

I'd prefer to comment but I have only 36 points and am not allowed so here's an incomplete answer:

this is the code from devise_invitable InvitationsController which is redirecting your request

def resource_from_invitation_token
  unless params[:invitation_token] && self.resource = resource_class.find_by_invitation_token(params[:invitation_token], true)
    set_flash_message(:alert, :invitation_token_invalid)
    redirect_to after_sign_out_path_for(resource_name)
  end
end

in your rails console try running:

token = '3GXDmi7NntDRdhvo57q5' #the token sent in the invitation email
User.find_by_invitation_token(token, true)

and see if that returns your User. It probably won't but maybe this will bring you closer to an answer. I hope so.