问题
I'm trying to implement password reset functionality in my React app with django rest framework. I am using rest_auth.
Issue: Once I tried to reset my password from website, It sends password reset link to email. But on clicking that link it redirects to DRF default password reset page, not to my website's desired password reset page. It is working fine when I tried it from localhost. But once deployed to Heroku it is not working.
Some observations: consider the link below which I received on email
https://mysite.herokuapp.com/password/reset/confirm/MQ/5a1-f42302b192f38cf29a3/
If I forcefully removed trailing slash ( / ) in URL then it redirects to my website password reset page and then it works as expected. I tried removing trailing slash from axios request from React but its not working.
Do I have to configure anything on Heroku?
Here is my React route for password reset page
<Route path='/password/reset/confirm/:uid/:token/' component={PasswordRestConfirm} />
this action below fires upon submitting the form.
export const setNewPassword = (new_password1, new_password2, uid, token) => (dispatch, getState) => {
dispatch({ type: SET_NEW_PASSWORD_START });
var bodyFormData = new FormData();
bodyFormData.append('new_password1', new_password1);
bodyFormData.append('new_password2', new_password2);
bodyFormData.append('uid', uid);
bodyFormData.append('token', token);
axios
.post(`https://mysite.herokuapp.com/password/reset/confirm/${uid}/${token}/`, bodyFormData)
.then(res => {
dispatch(createMessage({ passwordChangeSuccess: 'Your password was set successfully' }));
dispatch({
type: SET_NEW_PASSWORD,
payload: res.data
});
})
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status));
dispatch({
type: SET_NEW_PASSWORD_FAIL
});
});
};
settings.py
ALLOWED_HOSTS = ['mysite.herokuapp.com']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'corsheaders',
'rest_auth',
'rest_auth.registration',
'rest_framework',
'rest_framework.authtoken',
# Some other app
]
SITE_ID = 1
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
}
REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER': 'accounts.api.serializers.PasswordResetSerializer',
'PASSWORD_RESET_CONFIRM_SERIALIZER ': 'accounts.api.serializers.PasswordResetConfirmSerializer',
}
REST_SESSION_LOGIN = True
CORS_ORIGIN_ALLOW_ALL = True
ACCOUNT_EMAIL_REQUIRED = False
ACCOUNT_EMAIL_VERIFICATION = 'none'
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_AUTHENTICATION_METHOD = 'username'
# ***********Email settings **************
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = os.environ['EMAIL_USER']
EMAIL_HOST_PASSWORD = os.environ['EMAIL_PASS']
urls.py
urlpatterns = [
path('password/reset/confirm/<uidb64>/<token>/', PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
]
view.py
class PasswordResetConfirmView(GenericAPIView):
serializer_class = PasswordResetConfirmSerializer
permission_classes = (AllowAny,)
@sensitive_post_parameters_m
def dispatch(self, *args, **kwargs):
return super(PasswordResetConfirmView, self).dispatch(*args, **kwargs)
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(
{"detail": _("Password has been reset with the new password.")}
)
serializers.py
class PasswordResetConfirmSerializer(serializers.Serializer):
"""
Serializer for requesting a password reset e-mail.
"""
new_password1 = serializers.CharField(max_length=128)
new_password2 = serializers.CharField(max_length=128)
uid = serializers.CharField()
token = serializers.CharField()
set_password_form_class = SetPasswordForm
def custom_validation(self, attrs):
pass
def validate(self, attrs):
self._errors = {}
# Decode the uidb64 to uid to get User object
try:
uid = force_text(uid_decoder(attrs['uid']))
print(uid)
self.user = UserModel._default_manager.get(pk=uid)
print(self.user)
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
raise ValidationError({'uid': ['Invalid value']})
self.custom_validation(attrs)
# Construct SetPasswordForm instance
self.set_password_form = self.set_password_form_class(
user=self.user, data=attrs
)
if not self.set_password_form.is_valid():
raise serializers.ValidationError(self.set_password_form.errors)
if not default_token_generator.check_token(self.user, attrs['token']):
raise ValidationError({'token': ['Invalid value']})
return attrs
def save(self):
return self.set_password_form.save()
来源:https://stackoverflow.com/questions/58093916/password-reset-link-not-redirecting-to-website