-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow user send new password with reset pasword token without auth headers #1072
Allow user send new password with reset pasword token without auth headers #1072
Conversation
Thanks @MaicolBen!! I'll review this today. |
@MaicolBen - just to clarify, how does the client get the My understanding was that with this approach, the password reset link contained in the confirmation email would lead to the client, and the But the Do we need to do something with the |
@lynndylanhurley You're right, I missed that, there are 2 ways:
The first one is the harmless in the gem, it's just adding a new parameter to the url. What do you think? |
Cool I think I follow. Just to clarify with option
Is that correct? If so, then we will need a way for the password edit controller action to know when to pass headers back to the client (old behavior) and when to pass the
Then at the top of the password # app/controllers/devise_token_auth/passwords_controller.rb
module DeviseTokenAuth
class PasswordsController < DeviseTokenAuth::ApplicationController
# ...
def edit
# check if reset_password_token is required from client
if (
# if we're using initializers
DeviseTokenAuth.require_client_password_reset_token ||
# or if we're using the email querystring param
resource_params[:require_client_password_reset_token]
)
callback = URI.parse(params[:redirect_url])
# add password_reset_token while preserving existing query params
callback.query_values = (callback.query_values || {}).merge(
reset_password_token: resource_params[:reset_password_token]
)
# redirect to client
return redirect_to callback.to_s
end
# ... otherwise use old behavior
end
# ...
end
end What do you think @MaicolBen? |
Sounds good! I think that we can keep both ways without harming each other, but if you prefer it be configurable, I can make it. Also, this is an alternative behavior, not a new one, I don't want this replace the original behavior that most of the people uses, I want an alternative way that is helpful for mobile apps with a reset password through deep/universal linking. |
@MaicolBen @lynndylanhurley hey guys just wondering what is the status here? Happy to work on my own fork but just wondering if this is still being worked on? |
@fameoflight Sorry, I've been busy, I expect to finish this by this week |
bdf55cb
to
ae309fa
Compare
For now, I found a good hack but would love to remove the hack in future. For anyone else in the same boat, here is my solution. Override passwords controller in api/passwords_controller.rb
In routes.rb
|
4f2e323
to
dde1df6
Compare
|
||
# give redirect value from params priority | ||
@redirect_url = params[:redirect_url] | ||
helper_method :redirect_url |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MaicolBen Thanks for this!
Just a suggestion: maybe you could wrap the helper_method
call in respond_to?(:helper_method)
. helper_method
is defined in AbstractController::Helpers
, which in turn is included in ActionController::Base
, but it is not included in ActionController::Metal
. Check heartcombo/devise/pull/3732 for details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need, I removed it, I only needed the method in the controller
dde1df6
to
5f9442a
Compare
@MaicolBen I think the current approach might fail when This is because Perhaps changing that line will fix it. For the folks skipping the
so that |
@ruimiguelsantos Thanks for noticing that, wouldn't it better change the edit action: def edit
# if a user is not found, return nil
@resource = with_reset_password_token(resource_params[:reset_password_token])
if @resource && @resource.reset_password_period_valid?
if DeviseTokenAuth.require_client_password_reset_token
update_allow_password_change
callback = URI.parse(params[:redirect_url])
# add password_reset_token while preserving existing query params
callback.query_values = (callback.query_values || {}).merge(
reset_password_token: resource_params[:reset_password_token]
)
redirect_to callback.to_s
else
client_id, token = @resource.create_token
update_allow_password_change
redirect_header_options = {reset_password: true}
redirect_headers = build_redirect_headers(token,
client_id,
redirect_header_options)
redirect_to(@resource.build_auth_url(params[:redirect_url],
redirect_headers))
end
else
render_edit_error
end
end
def update_allow_password_change
# ensure that user is confirmed
@resource.skip_confirmation! if confirmable_enabled? && !@resource.confirmed_at
# allow user to change password once without current_password
@resource.allow_password_change = true if recoverable_enabled?
@resource.save!
yield @resource if block_given?
end |
5f9442a
to
6e39b02
Compare
@MaicolBen You're right, that's indeed better. Looks good to me! In that case I would just take the def edit &block
# if a user is not found, return nil
@resource = with_reset_password_token(resource_params[:reset_password_token])
if @resource && @resource.reset_password_period_valid?
if DeviseTokenAuth.require_client_password_reset_token
update_allow_password_change &block
callback = URI.parse(params[:redirect_url])
# add password_reset_token while preserving existing query params
callback.query_values = (callback.query_values || {}).merge(
reset_password_token: resource_params[:reset_password_token]
)
redirect_to callback.to_s
else
client_id, token = @resource.create_token
update_allow_password_change &block
redirect_header_options = {reset_password: true}
redirect_headers = build_redirect_headers(token,
client_id,
redirect_header_options)
redirect_to(@resource.build_auth_url(params[:redirect_url],
redirect_headers))
end
else
render_edit_error
end
end
def update_allow_password_change
# ensure that user is confirmed
@resource.skip_confirmation! if confirmable_enabled? && !@resource.confirmed_at
# allow user to change password once without current_password
@resource.allow_password_change = true if recoverable_enabled?
@resource.save!
yield @resource if block_given?
end |
b44acd2
to
f871a15
Compare
f871a15
to
0df590b
Compare
@lynndylanhurley @ruimiguelsantos Done! I won't fix the code climate issue now, because this is an engine, and adding more concerns could confuse people when they have to override, I will add a Codeclimate config when #1125 is merged. Maybe in the future we can come up with better refactors. Also, I added doc for this flow, since I was writing the doc and it was huge as a FAQ, I moved it to its own page. |
0df590b
to
98e4edc
Compare
98e4edc
to
a8875f5
Compare
Will this be merged ? |
@sofianegargouri we don't have enough contributors in this repo to review, @dks17 can review it but this will go to 1.1.0 or 1.2.0 |
a8875f5
to
00de9b8
Compare
00de9b8
to
ad97567
Compare
ad97567
to
ecf02e4
Compare
What else needs to be done here @MaicolBen @lynndylanhurley? I don't want this PR to die! I found one bug that I fixed locally—if the token isn't valid and the user isn't found, the code tries to call #create_token on nil and throws a |
Made that update, fixed some tests, and rebased against master bringing this branch up to date with the rest of the project. Feel free to pull in those changes to this branch @MaicolBen, or if you give me access to this branch I can do it. |
This allows a new flow (thanks to @lynndylanhurley):
reset_password_token
reset_password_token
I will update the section with the form submitting in the wiki:
PUT /auth/password
with thepassword
andpassword_confirmation
parameters. There are 2 ways to identify the user who is resetting the password:config/initializers/devise_token_auth.rb
; at this time of writing it is:uid
,client
andaccess-token
.-Ensure that the
uid
sent in the headers is not URL-escaped. e.g. it should be bob@example.com, not bob%40example.com-