is devise_invitable supported? (solvable with workaround) #155

Closed
salmanasiddiqui opened this Issue Feb 17, 2015 · 37 comments

Comments

Projects
None yet
10 participants
@salmanasiddiqui
Contributor

salmanasiddiqui commented Feb 17, 2015

I am trying to use devise_invitable(1.4.0) gem with devise_token_auth(0.1.31)/devise(3.4.1) but having an issue. When I make a post request to auth/invitation the following error occurs

ArgumentError in Devise::InvitationsController#create
wrong number of arguments (1 for 0)

Extracted source (around line #102):
101        class_eval <<-METHODS, __FILE__, __LINE__ + 1
102          def authenticate_#{mapping}!
103            unless current_#{mapping}
104              return render json: {
105                errors: ["Authorized users only."]

Trace:
devise_token_auth (0.1.31) lib/devise_token_auth/controllers/helpers.rb:102:in `authenticate_user!'
devise_invitable (1.4.0) lib/devise_invitable/controllers/helpers.rb:18:in `authenticate_inviter!'
devise_invitable (1.4.0) app/controllers/devise/invitations_controller.rb:69:in `current_inviter'
devise_invitable (1.4.0) app/controllers/devise/invitations_controller.rb:73:in `has_invitations_left?'

First I setup devise_token_auth and is working perfectly. Now I am adding devise_invitable as follows:

  1. Passed parameter :invitable in user model devise method
  2. Executed migraton to add devise_invitable columns

that's it, running rake routes shows me that devise_token_auth is creating routes for invitation. But they are showing me above posted error.

Is devise_invitable is supported by devise_token_auth? Is there a way to use them together?

@lynndylanhurley

This comment has been minimized.

Show comment
Hide comment
@lynndylanhurley

lynndylanhurley Feb 17, 2015

Owner

@salmanasiddiqui - I haven't looked into this yet, and I'm not sure what devise_invitable support will entail. I'll look into this as soon as I get a chance.

Owner

lynndylanhurley commented Feb 17, 2015

@salmanasiddiqui - I haven't looked into this yet, and I'm not sure what devise_invitable support will entail. I'll look into this as soon as I get a chance.

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Apr 9, 2015

Contributor

+1

I've been looking at the code for a while and I've identified a few things that I think will need to be updated in order for this to run. I've been able to replicate @salmanasiddiqui's issue but I haven't yet figured out why it's happening. One thing to keep in mind is that devise_invitable does
prepend_before_filter :authenticate_inviter!, :only => [:new, :create]
which means that authenticate_inviter will have to be locally overridden to call set_user_by_token and return the authenticated user, which is expected by the invitable controller (check https://github.com/scambra/devise_invitable -> Controller Filter section for more info).

I can work on a pull request if I manage to figure out the issue.

edit -> I followed the "1 for 0" issue to DeviseController's resource_class, which is a proxy to the map class. It seems DeviseController's resource_class is being called because of proxy delegation fallback. I just don't understand why the introduction of devise_invitable triggers this.

Contributor

mchavarriagam commented Apr 9, 2015

+1

I've been looking at the code for a while and I've identified a few things that I think will need to be updated in order for this to run. I've been able to replicate @salmanasiddiqui's issue but I haven't yet figured out why it's happening. One thing to keep in mind is that devise_invitable does
prepend_before_filter :authenticate_inviter!, :only => [:new, :create]
which means that authenticate_inviter will have to be locally overridden to call set_user_by_token and return the authenticated user, which is expected by the invitable controller (check https://github.com/scambra/devise_invitable -> Controller Filter section for more info).

I can work on a pull request if I manage to figure out the issue.

edit -> I followed the "1 for 0" issue to DeviseController's resource_class, which is a proxy to the map class. It seems DeviseController's resource_class is being called because of proxy delegation fallback. I just don't understand why the introduction of devise_invitable triggers this.

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Apr 9, 2015

Contributor

I forgot to add version info:

DTA -> v0.1.32.beta9
Devise -> v3.4.1
devise_invitable -> v1.4.2

Contributor

mchavarriagam commented Apr 9, 2015

I forgot to add version info:

DTA -> v0.1.32.beta9
Devise -> v3.4.1
devise_invitable -> v1.4.2

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Apr 9, 2015

Contributor

I think the reason why this is happening is not really because of proxy delegation (the "proxy" on DeviseController's resource_class threw me off track) but simply because the scope of the call is set to InvitationController which inherits from DeviseController, so when resource_class is called from set_user_by_token (itself being called from InvitationController through a before_filter defined in ApplicationController), the scope determines that it will be DeviseController's resource_class that gets called instead.

Contributor

mchavarriagam commented Apr 9, 2015

I think the reason why this is happening is not really because of proxy delegation (the "proxy" on DeviseController's resource_class threw me off track) but simply because the scope of the call is set to InvitationController which inherits from DeviseController, so when resource_class is called from set_user_by_token (itself being called from InvitationController through a before_filter defined in ApplicationController), the scope determines that it will be DeviseController's resource_class that gets called instead.

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Apr 9, 2015

Contributor

As it turns out, moving include DeviseTokenAuth::Concerns::SetUserByToken to a controller that inherits from Devise::InvitationsController (instead of having it on ApplicationController) solves the issue (though it raises the issue of having to repeat the SetUserByToken inclusion in all controllers throughout the site; messy). You guys probably know why but here's what I found, just in case:

  1. Devise::InvitationsController inherits from DeviseController, which has a resource_class method (no arguments) -> here.
  2. Devise::InvitationsController forces authenticate_inviter as a before_filter, which has to be overriden with a call to SetUserByToken instead here.
  3. The way that this is recommended (in devise_invitable's documentation, here go to "Controller Filter" section) is to override the method in ApplicationController.
  4. If done this way, SetUserByToken Concern will end up being inserted after ApplicationController in the inheritance chain, and DeviseController's resource_class will end up being evaluated before SetUserByToken's, which results in the error we were experiencing.

Thoughts?

Contributor

mchavarriagam commented Apr 9, 2015

As it turns out, moving include DeviseTokenAuth::Concerns::SetUserByToken to a controller that inherits from Devise::InvitationsController (instead of having it on ApplicationController) solves the issue (though it raises the issue of having to repeat the SetUserByToken inclusion in all controllers throughout the site; messy). You guys probably know why but here's what I found, just in case:

  1. Devise::InvitationsController inherits from DeviseController, which has a resource_class method (no arguments) -> here.
  2. Devise::InvitationsController forces authenticate_inviter as a before_filter, which has to be overriden with a call to SetUserByToken instead here.
  3. The way that this is recommended (in devise_invitable's documentation, here go to "Controller Filter" section) is to override the method in ApplicationController.
  4. If done this way, SetUserByToken Concern will end up being inserted after ApplicationController in the inheritance chain, and DeviseController's resource_class will end up being evaluated before SetUserByToken's, which results in the error we were experiencing.

Thoughts?

@lynndylanhurley

This comment has been minimized.

Show comment
Hide comment
@lynndylanhurley

lynndylanhurley Apr 9, 2015

Owner

@mchavarriagam - thanks for taking the time to look into this.

As you've found out by now, this one is tricky. I'll have some time early next week to dig in. If you can solve it in the meantime, please send me a PR (with tests!!).

Owner

lynndylanhurley commented Apr 9, 2015

@mchavarriagam - thanks for taking the time to look into this.

As you've found out by now, this one is tricky. I'll have some time early next week to dig in. If you can solve it in the meantime, please send me a PR (with tests!!).

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Apr 9, 2015

Contributor

Sure thing! I'll work on a PR (with tests!!) later today and should have something ready before end of week. Thanks for the gem, it's been great working it with so far. :D

Contributor

mchavarriagam commented Apr 9, 2015

Sure thing! I'll work on a PR (with tests!!) later today and should have something ready before end of week. Thanks for the gem, it's been great working it with so far. :D

@salmanasiddiqui

This comment has been minimized.

Show comment
Hide comment
@salmanasiddiqui

salmanasiddiqui Apr 10, 2015

Contributor

@mchavarriagam

actually authenticate_inviter! calls the authenticate_user!(authenticate_#{mapping}!) method. The authenticate_#{mapping}! method defined in devise_token_auth doesnt take any parameters.. while in devise it does take parameter opts={} -> devise_token_auth vs devise

so just by adding the parameter in devise_token_auth, will resolve this issue..
DeviseInvitation calls authenticate_inviter! which calls authenticate_user! of devise_token_auth, and that calls current_user, which calls set_user_by_token(:user) itself. -> here

but even after this I faced several other issues, dont remember each of them now though..
here is my Invitation Controller in the app.
and here are the modifications I did in Devise Token Auth and using this in my app.

the above is surely missing many things of devise token auth.. as I m still getting my head around it.. and I was really short on time when I did it..

Contributor

salmanasiddiqui commented Apr 10, 2015

@mchavarriagam

actually authenticate_inviter! calls the authenticate_user!(authenticate_#{mapping}!) method. The authenticate_#{mapping}! method defined in devise_token_auth doesnt take any parameters.. while in devise it does take parameter opts={} -> devise_token_auth vs devise

so just by adding the parameter in devise_token_auth, will resolve this issue..
DeviseInvitation calls authenticate_inviter! which calls authenticate_user! of devise_token_auth, and that calls current_user, which calls set_user_by_token(:user) itself. -> here

but even after this I faced several other issues, dont remember each of them now though..
here is my Invitation Controller in the app.
and here are the modifications I did in Devise Token Auth and using this in my app.

the above is surely missing many things of devise token auth.. as I m still getting my head around it.. and I was really short on time when I did it..

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Apr 10, 2015

Contributor

Nice! Thanks for going over that, it makes things a lot clearer now.

I see that you also updated routes.rb by adding invitations_ctrl. The approach that I want to try is to do a merge of the opts[:controllers] into controllers right before going through the "skip" part (here). That way any controller that's included in opts[:controller] will make it into the final list of controllers (without having to hard-code invitations_ctrl in there).

Thanks again for sharing that info, it'll make it easier for me to work on the PR.

Contributor

mchavarriagam commented Apr 10, 2015

Nice! Thanks for going over that, it makes things a lot clearer now.

I see that you also updated routes.rb by adding invitations_ctrl. The approach that I want to try is to do a merge of the opts[:controllers] into controllers right before going through the "skip" part (here). That way any controller that's included in opts[:controller] will make it into the final list of controllers (without having to hard-code invitations_ctrl in there).

Thanks again for sharing that info, it'll make it easier for me to work on the PR.

@salmanasiddiqui

This comment has been minimized.

Show comment
Hide comment
@salmanasiddiqui

salmanasiddiqui Apr 13, 2015

Contributor

actually what I tried, I simply defaulted the routes for invitation controller to devise Invitation controller and then overridden that controller in my app in a way similar to confirmation controller and password controller in devise token auth.

the devise token auth way to do this would be, to rewrite this controller in devise token auth, and then routes should by defaulted to that.

Im also kind of confused by the way devise token auth works and written. Why routes are defaulted to devise token auth? Is it not possible to achieve this token based authentication by simply overriding the devise controllers by inheriting from it?

devise token auth inherits from the base devise controller and then defines his own child controllers.. Rails community is quite familiar with the devise, so imo it might be better to override those controllers by inheriting from those controllers directly, use the same filters as devise uses but override them as needed? or skip few of them if not needed?

just putting out my thoughts, @lynndylanhurley what do you think?

Also, I think each version of devise token auth should mention the compatible ng token auth version and vice versa? or their version should be kept same?

Contributor

salmanasiddiqui commented Apr 13, 2015

actually what I tried, I simply defaulted the routes for invitation controller to devise Invitation controller and then overridden that controller in my app in a way similar to confirmation controller and password controller in devise token auth.

the devise token auth way to do this would be, to rewrite this controller in devise token auth, and then routes should by defaulted to that.

Im also kind of confused by the way devise token auth works and written. Why routes are defaulted to devise token auth? Is it not possible to achieve this token based authentication by simply overriding the devise controllers by inheriting from it?

devise token auth inherits from the base devise controller and then defines his own child controllers.. Rails community is quite familiar with the devise, so imo it might be better to override those controllers by inheriting from those controllers directly, use the same filters as devise uses but override them as needed? or skip few of them if not needed?

just putting out my thoughts, @lynndylanhurley what do you think?

Also, I think each version of devise token auth should mention the compatible ng token auth version and vice versa? or their version should be kept same?

@fertingoff

This comment has been minimized.

Show comment
Hide comment
@fertingoff

fertingoff Jun 15, 2015

Contributor

@mchavarriagam, any hopes for pr? :)

Contributor

fertingoff commented Jun 15, 2015

@mchavarriagam, any hopes for pr? :)

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Jun 15, 2015

Contributor

@fertingoff sorry, been really busy lately so I haven't been able to get back to this. Will probably get some time next week but I can't promise anything right now. Apologies for that.

Contributor

mchavarriagam commented Jun 15, 2015

@fertingoff sorry, been really busy lately so I haven't been able to get back to this. Will probably get some time next week but I can't promise anything right now. Apologies for that.

@fertingoff

This comment has been minimized.

Show comment
Hide comment
@fertingoff

fertingoff Jun 15, 2015

Contributor

@mchavarriagam, awesome, thank's a lot! Take your time.

P.S. You just helped me to have a good sleep tonight 🌝

Contributor

fertingoff commented Jun 15, 2015

@mchavarriagam, awesome, thank's a lot! Take your time.

P.S. You just helped me to have a good sleep tonight 🌝

@fertingoff

This comment has been minimized.

Show comment
Hide comment
@fertingoff

fertingoff Jul 3, 2015

Contributor

@mchavarriagam, please, let me know if you change your mind :)

Contributor

fertingoff commented Jul 3, 2015

@mchavarriagam, please, let me know if you change your mind :)

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Jul 3, 2015

Contributor

:D I'm planning on working on it this weekend, since I finally have some time off. I may ask you guys a few questions over here so we can bounce ideas off each other.

Contributor

mchavarriagam commented Jul 3, 2015

:D I'm planning on working on it this weekend, since I finally have some time off. I may ask you guys a few questions over here so we can bounce ideas off each other.

@fertingoff

This comment has been minimized.

Show comment
Hide comment
@fertingoff

fertingoff Jul 18, 2015

Contributor

hi @mchavarriagam!
I can`t figure out this by myself so far. What do you think, is it still real or it will take much more time?
Thanks again for your possible help.

Contributor

fertingoff commented Jul 18, 2015

hi @mchavarriagam!
I can`t figure out this by myself so far. What do you think, is it still real or it will take much more time?
Thanks again for your possible help.

@gabrielhilal

This comment has been minimized.

Show comment
Hide comment
@gabrielhilal

gabrielhilal Aug 11, 2015

I'm facing the same problem... any news on that?

I'm facing the same problem... any news on that?

@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Aug 11, 2015

Contributor

I haven't had any luck in making my approach work so far and it's taking me longer than I anticipated, definitely. I don't think a solution is coming any time soon. Sorry guys.

Edit: Have you guys tried the workaround mentioned earlier in the thread? -> #155 (comment)

Contributor

mchavarriagam commented Aug 11, 2015

I haven't had any luck in making my approach work so far and it's taking me longer than I anticipated, definitely. I don't think a solution is coming any time soon. Sorry guys.

Edit: Have you guys tried the workaround mentioned earlier in the thread? -> #155 (comment)

@gabrielhilal

This comment has been minimized.

Show comment
Hide comment
@gabrielhilal

gabrielhilal Aug 11, 2015

My workaround in case someone else is facing the issue:

My solution was to override two methods to match the devise_auth_token methods: resource_class and authenticate_user.

routes.rb:

namespace :api do
  mount_devise_token_auth_for 'User', at: 'auth', skip: [:invitations],
    controllers: { registrations: 'api/registrations' }
  devise_for :users, path: "auth", only: [:invitations],
    controllers: { invitations: 'api/invitations' }
end

invitations_controller:

module Api
  class InvitationsController < Devise::InvitationsController

    private

    def authenticate_inviter!
      authenticate_user!
    end

    def authenticate_user!
      unless current_user
        return render json: {
          errors: ["Authorized users only."]
        }, status: 401
      end
    end

    def resource_class(m=nil)
      if m
        mapping = Devise.mappings[m]
      else
        mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
      end
      mapping.to
    end

  end
end

My workaround in case someone else is facing the issue:

My solution was to override two methods to match the devise_auth_token methods: resource_class and authenticate_user.

routes.rb:

namespace :api do
  mount_devise_token_auth_for 'User', at: 'auth', skip: [:invitations],
    controllers: { registrations: 'api/registrations' }
  devise_for :users, path: "auth", only: [:invitations],
    controllers: { invitations: 'api/invitations' }
end

invitations_controller:

module Api
  class InvitationsController < Devise::InvitationsController

    private

    def authenticate_inviter!
      authenticate_user!
    end

    def authenticate_user!
      unless current_user
        return render json: {
          errors: ["Authorized users only."]
        }, status: 401
      end
    end

    def resource_class(m=nil)
      if m
        mapping = Devise.mappings[m]
      else
        mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
      end
      mapping.to
    end

  end
end
@mchavarriagam

This comment has been minimized.

Show comment
Hide comment
@mchavarriagam

mchavarriagam Aug 11, 2015

Contributor

sweet!

Contributor

mchavarriagam commented Aug 11, 2015

sweet!

@fertingoff

This comment has been minimized.

Show comment
Hide comment
@fertingoff

fertingoff Aug 28, 2015

Contributor

@gabrielhilal thanks for posting this workaround. I'm trying to implement it, but have several questions and would appreciate your help.

  1. As far as I understand you use your custom registrations_controller (controllers: { registrations: 'api/registrations' }). Why you use it instead of registrations_controller from devise_token_auth and what is the difference between them in your case?
  2. How does you your invitation link look like? Devise_invitable by default generates something like this: http://localhost:3000/api/v1/auth/invitation/accept?invitation_token=tQ9ss2T-dcD6rNNcFTLc. Should it have redirect_url or other parameters and if yes what method does it in your case?
  3. By default a link from email is processed by devise_invitable's InvitationsController#edit:
 # GET /resource/invitation/accept?invitation_token=abcdef
  def edit
    resource.invitation_token = params[:invitation_token]
    render :edit
  end

However, it doesn't work, since it is for solid rails app, right? I assume that either I need to change this method or I need to redirect to other method. From a gist you posted above I see that you changed nothing except three private methods. So, how come your app works? :)
4. All in all I hope to find myself at a page of setting new password in my angular app. My 'educated guess' is that the route should be http://localhost:9000/#/reset_password (for localhost) with confirmed token by devise_invitable. What method and when does this in your case if my guess is correct?

Thank you very much.

Contributor

fertingoff commented Aug 28, 2015

@gabrielhilal thanks for posting this workaround. I'm trying to implement it, but have several questions and would appreciate your help.

  1. As far as I understand you use your custom registrations_controller (controllers: { registrations: 'api/registrations' }). Why you use it instead of registrations_controller from devise_token_auth and what is the difference between them in your case?
  2. How does you your invitation link look like? Devise_invitable by default generates something like this: http://localhost:3000/api/v1/auth/invitation/accept?invitation_token=tQ9ss2T-dcD6rNNcFTLc. Should it have redirect_url or other parameters and if yes what method does it in your case?
  3. By default a link from email is processed by devise_invitable's InvitationsController#edit:
 # GET /resource/invitation/accept?invitation_token=abcdef
  def edit
    resource.invitation_token = params[:invitation_token]
    render :edit
  end

However, it doesn't work, since it is for solid rails app, right? I assume that either I need to change this method or I need to redirect to other method. From a gist you posted above I see that you changed nothing except three private methods. So, how come your app works? :)
4. All in all I hope to find myself at a page of setting new password in my angular app. My 'educated guess' is that the route should be http://localhost:9000/#/reset_password (for localhost) with confirmed token by devise_invitable. What method and when does this in your case if my guess is correct?

Thank you very much.

@gabrielhilal

This comment has been minimized.

Show comment
Hide comment
@gabrielhilal

gabrielhilal Aug 28, 2015

Hi @fertingoff, let's see if I can help answering your questions:

1- The registrations_controller has nothing to do with this invitable issue. I have overridden some actions because of my requirements... sorry for the copy and paste of my routes... do no worry about that ;)

2 and 3- My invitation link is http://localhost:3000/api/auth/invitation/accept?invitation_token=xxxx, so if you have the v1 scope your link looks fine... I have not included the redirect_url because in my case I will always redirect to the same place. So, I'm doing the redirect in the edit action:

# api/invitations_controller (devise override)
def edit
  redirect_to "#{my_url}?invitation_token=#{params[:invitation_token]}"
end

You will have to change some more methods if you want to send the redirect_url to the mailer...

4- My edit action redirects to the set password form (angular), sending the invitation_token in the param.
So, your angular has to PUT/PATCH the invitation_token, as well as the password and password_confirmation to /api/auth/invitation. In your case /api/auth/v1/invitation.

I really hope it helps :)

In case I forgot something, please note the controller example below ;)

class InvitationsController < Devise::InvitationsController
  before_action :resource_from_invitation_token, only: :update
  before_action :authenticate_user!,  only: :create

  def create
    @user = User.invite!(invite_params, current_user)
    render json: { success: ['User created.'] }, status: :created
  end

  def edit
    redirect_to "#{my_url}?invitation_token=#{params[:invitation_token]}"
  end

  def update
    @user = User.accept_invitation!(accept_invitation_params)
    if @user.errors.empty?
      render json: { success: ['User updated.'] }, status: :accepted
    else
      render json: { errors: @user.errors.full_messages },
             status: :unprocessable_entity
    end
  end

  private

  def invite_params
    params.permit(user:
      [:first_name, :last_name, :job_title, :mobile_number,
       :email, :invitation_token, :provider, :skip_invitation])[:user]
  end

  def accept_invitation_params
    params.permit(:password, :password_confirmation, :invitation_token)
  end

  def authenticate_inviter!
    # authentication in before_action
  end

  def authenticate_user!
    return if current_user
    render json: {
      errors: ['Authorized users only.']
    }, status: :unauthorized
  end

  def resource_class(m = nil)
    if m
      mapping = Devise.mappings[m]
    else
      mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
    end
    mapping.to
  end

  def resource_from_invitation_token
    return if params[:invitation_token] &&
              User.find_by_invitation_token(params[:invitation_token], true)
    render json: { errors: ['Invalid token.'] }, status: :not_acceptable
  end
end

Hi @fertingoff, let's see if I can help answering your questions:

1- The registrations_controller has nothing to do with this invitable issue. I have overridden some actions because of my requirements... sorry for the copy and paste of my routes... do no worry about that ;)

2 and 3- My invitation link is http://localhost:3000/api/auth/invitation/accept?invitation_token=xxxx, so if you have the v1 scope your link looks fine... I have not included the redirect_url because in my case I will always redirect to the same place. So, I'm doing the redirect in the edit action:

# api/invitations_controller (devise override)
def edit
  redirect_to "#{my_url}?invitation_token=#{params[:invitation_token]}"
end

You will have to change some more methods if you want to send the redirect_url to the mailer...

4- My edit action redirects to the set password form (angular), sending the invitation_token in the param.
So, your angular has to PUT/PATCH the invitation_token, as well as the password and password_confirmation to /api/auth/invitation. In your case /api/auth/v1/invitation.

I really hope it helps :)

In case I forgot something, please note the controller example below ;)

class InvitationsController < Devise::InvitationsController
  before_action :resource_from_invitation_token, only: :update
  before_action :authenticate_user!,  only: :create

  def create
    @user = User.invite!(invite_params, current_user)
    render json: { success: ['User created.'] }, status: :created
  end

  def edit
    redirect_to "#{my_url}?invitation_token=#{params[:invitation_token]}"
  end

  def update
    @user = User.accept_invitation!(accept_invitation_params)
    if @user.errors.empty?
      render json: { success: ['User updated.'] }, status: :accepted
    else
      render json: { errors: @user.errors.full_messages },
             status: :unprocessable_entity
    end
  end

  private

  def invite_params
    params.permit(user:
      [:first_name, :last_name, :job_title, :mobile_number,
       :email, :invitation_token, :provider, :skip_invitation])[:user]
  end

  def accept_invitation_params
    params.permit(:password, :password_confirmation, :invitation_token)
  end

  def authenticate_inviter!
    # authentication in before_action
  end

  def authenticate_user!
    return if current_user
    render json: {
      errors: ['Authorized users only.']
    }, status: :unauthorized
  end

  def resource_class(m = nil)
    if m
      mapping = Devise.mappings[m]
    else
      mapping = Devise.mappings[resource_name] || Devise.mappings.values.first
    end
    mapping.to
  end

  def resource_from_invitation_token
    return if params[:invitation_token] &&
              User.find_by_invitation_token(params[:invitation_token], true)
    render json: { errors: ['Invalid token.'] }, status: :not_acceptable
  end
end
@fertingoff

This comment has been minimized.

Show comment
Hide comment
@fertingoff

fertingoff Aug 28, 2015

Contributor

Hi @gabrielhilal, thank you for your prompt and comprehensive reply. It helped a lot.
After successfully firing InvitationsController#update I have a user with updated :encrypted_password field (the only sign of success for me alongside with render json: { success: ['User updated.'] }, status: :accepted). The next step as far as I see is just to sign_in in order to get token. But for some reason I have bad credentials error. Is my way of thinking correct?
Thx again!

Contributor

fertingoff commented Aug 28, 2015

Hi @gabrielhilal, thank you for your prompt and comprehensive reply. It helped a lot.
After successfully firing InvitationsController#update I have a user with updated :encrypted_password field (the only sign of success for me alongside with render json: { success: ['User updated.'] }, status: :accepted). The next step as far as I see is just to sign_in in order to get token. But for some reason I have bad credentials error. Is my way of thinking correct?
Thx again!

@gabrielhilal

This comment has been minimized.

Show comment
Hide comment
@gabrielhilal

gabrielhilal Aug 28, 2015

Hi @fertingoff, after the update action you should be able to sign in...
Make sure you have the following fields in the users table:

## Invitable
t.string   :invitation_token
t.datetime :invitation_created_at
t.datetime :invitation_sent_at
t.datetime :invitation_accepted_at
t.integer  :invitation_limit
t.integer  :invited_by_id
t.string   :invited_by_type

Also, make sure the invitation_accepted_at is updated as well (after the update action).

And just one more thing, make sure you are using the raw_invitation_token in the email view:

<p><%= link_to 'Accept invitation', accept_api_v1_user_invitation_url(invitation_token: @resource.raw_invitation_token) %></p>

I hope it helps ;)

Hi @fertingoff, after the update action you should be able to sign in...
Make sure you have the following fields in the users table:

## Invitable
t.string   :invitation_token
t.datetime :invitation_created_at
t.datetime :invitation_sent_at
t.datetime :invitation_accepted_at
t.integer  :invitation_limit
t.integer  :invited_by_id
t.string   :invited_by_type

Also, make sure the invitation_accepted_at is updated as well (after the update action).

And just one more thing, make sure you are using the raw_invitation_token in the email view:

<p><%= link_to 'Accept invitation', accept_api_v1_user_invitation_url(invitation_token: @resource.raw_invitation_token) %></p>

I hope it helps ;)

@fertingoff

This comment has been minimized.

Show comment
Hide comment
@fertingoff

fertingoff Aug 31, 2015

Contributor

Thank you @gabrielhilal. You empowered me.
Eventually it works.

Just for the records. In my case the problem was that instance of a user need to have uid (email) and provider('email'). @salmanasiddiqui may be it would be helpful to reflect in the name of the issue that it's solvable with workaround?

Contributor

fertingoff commented Aug 31, 2015

Thank you @gabrielhilal. You empowered me.
Eventually it works.

Just for the records. In my case the problem was that instance of a user need to have uid (email) and provider('email'). @salmanasiddiqui may be it would be helpful to reflect in the name of the issue that it's solvable with workaround?

@salmanasiddiqui salmanasiddiqui changed the title from is devise_invitable supported? to is devise_invitable supported? (solvable with workaround) Sep 2, 2015

@klm88

This comment has been minimized.

Show comment
Hide comment
@klm88

klm88 Dec 1, 2015

Will devise_invitable support be added to this gem anytime soon or do we need to implement the workaround?

klm88 commented Dec 1, 2015

Will devise_invitable support be added to this gem anytime soon or do we need to implement the workaround?

@gabrielhilal

This comment has been minimized.

Show comment
Hide comment
@gabrielhilal

gabrielhilal Dec 1, 2015

I wrote a post about the issue and my solution (workaround)... it might be useful to someone, so will leave the link here: http://gabrielhilal.com/2015/11/07/integrating-devise_invitable-into-devise_token_auth/

I wrote a post about the issue and my solution (workaround)... it might be useful to someone, so will leave the link here: http://gabrielhilal.com/2015/11/07/integrating-devise_invitable-into-devise_token_auth/

@ashwinputhige

This comment has been minimized.

Show comment
Hide comment
@ashwinputhige

ashwinputhige Feb 2, 2016

This worked for me.
To invite an use :
user = User.invite!({:email => "users_email_here"})

I edited the URL in the invitation email to include the token after "#"
http://localhost:3000/#/accept/V396a4Kz1aiGdnVwR

Added an angular route to handle that URL and accepted the invitation in my controller.

user = User.accept_invitation!(:password => password,
       :password_confirmation => password_confirmation,
       :invitation_token => invitation_token)

This worked for me.
To invite an use :
user = User.invite!({:email => "users_email_here"})

I edited the URL in the invitation email to include the token after "#"
http://localhost:3000/#/accept/V396a4Kz1aiGdnVwR

Added an angular route to handle that URL and accepted the invitation in my controller.

user = User.accept_invitation!(:password => password,
       :password_confirmation => password_confirmation,
       :invitation_token => invitation_token)
@zachfeldman

This comment has been minimized.

Show comment
Hide comment
@zachfeldman

zachfeldman Oct 6, 2017

Collaborator

Hi there @salmanasiddiqui ,

In an effort to cleanup this project and prioritize a bit, we're marking issues that haven't had any activity in a while with a "close-in-7-days" label. If we don't hear from you in about a week, we'll be closing this issue. Obviously feel free to re-open it at any time if it's the right time or this was done in error!

If you are still having the issue (especially if it's a bug report) please refer to our new Issue Template to provide some more details to help us solve it.

Hope all is well.

Collaborator

zachfeldman commented Oct 6, 2017

Hi there @salmanasiddiqui ,

In an effort to cleanup this project and prioritize a bit, we're marking issues that haven't had any activity in a while with a "close-in-7-days" label. If we don't hear from you in about a week, we'll be closing this issue. Obviously feel free to re-open it at any time if it's the right time or this was done in error!

If you are still having the issue (especially if it's a bug report) please refer to our new Issue Template to provide some more details to help us solve it.

Hope all is well.

@salmanasiddiqui

This comment has been minimized.

Show comment
Hide comment
@salmanasiddiqui

salmanasiddiqui Oct 6, 2017

Contributor

@zachfeldman ,

Before this issue is closed, I would propose to make a simple change in devise_token_auth

Fix the signature of authenticate_#{mapping}! method.
In Devise authenticate_#{mapping}! method accepts 1 argument opts. As devise_token_auth 's intention is to override this method hence the signature should be same.

Let me know if you agree, I can make the PR

Contributor

salmanasiddiqui commented Oct 6, 2017

@zachfeldman ,

Before this issue is closed, I would propose to make a simple change in devise_token_auth

Fix the signature of authenticate_#{mapping}! method.
In Devise authenticate_#{mapping}! method accepts 1 argument opts. As devise_token_auth 's intention is to override this method hence the signature should be same.

Let me know if you agree, I can make the PR

@zachfeldman

This comment has been minimized.

Show comment
Hide comment
@zachfeldman

zachfeldman Oct 14, 2017

Collaborator

Let's see what @MaicolBen and/or @lynndylanhurley think!

Collaborator

zachfeldman commented Oct 14, 2017

Let's see what @MaicolBen and/or @lynndylanhurley think!

@zachfeldman

This comment has been minimized.

Show comment
Hide comment
@zachfeldman

zachfeldman Oct 14, 2017

Collaborator

I don't have an objection to it.

Collaborator

zachfeldman commented Oct 14, 2017

I don't have an objection to it.

@lynndylanhurley

This comment has been minimized.

Show comment
Hide comment
@lynndylanhurley

lynndylanhurley Oct 14, 2017

Owner

Fix the signature of authenticate_#{mapping}! method.
In Devise authenticate_#{mapping}! method accepts 1 argument opts. As devise_token_auth 's intention is to override this method hence the signature should be same.

Let me know if you agree, I can make the PR

@salmanasiddiqui please send a PR and we will review ASAP. Thanks!

Owner

lynndylanhurley commented Oct 14, 2017

Fix the signature of authenticate_#{mapping}! method.
In Devise authenticate_#{mapping}! method accepts 1 argument opts. As devise_token_auth 's intention is to override this method hence the signature should be same.

Let me know if you agree, I can make the PR

@salmanasiddiqui please send a PR and we will review ASAP. Thanks!

@ilhanadiyaman

This comment has been minimized.

Show comment
Hide comment
@ilhanadiyaman

ilhanadiyaman Nov 21, 2017

I have the same issue. @salmanasiddiqui any progress for the PR?

I have the same issue. @salmanasiddiqui any progress for the PR?

@salmanasiddiqui

This comment has been minimized.

Show comment
Hide comment
@salmanasiddiqui

salmanasiddiqui Nov 28, 2017

Contributor

Made the PR. I haven't added any tests, though I would suggest that all the tests for this specific helper in Devise should be copied over. I have moved to python for the past few months and don't have ruby configured. 🙁

Contributor

salmanasiddiqui commented Nov 28, 2017

Made the PR. I haven't added any tests, though I would suggest that all the tests for this specific helper in Devise should be copied over. I have moved to python for the past few months and don't have ruby configured. 🙁

@zachfeldman

This comment has been minimized.

Show comment
Hide comment
@zachfeldman

zachfeldman Nov 28, 2017

Collaborator

This PR doesn't appear that it would break anything because the method has a default argument, so I merged it.

Collaborator

zachfeldman commented Nov 28, 2017

This PR doesn't appear that it would break anything because the method has a default argument, so I merged it.

@MaicolBen

This comment has been minimized.

Show comment
Hide comment
@MaicolBen

MaicolBen Nov 28, 2017

Collaborator

Can this be closed?

Collaborator

MaicolBen commented Nov 28, 2017

Can this be closed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment