I cannot wrap my head around this problem. Read a lot of solutions but cannot seem to find the correct combination that works for me.
I want to initiate a users password reset flow from within my (android/iOS) app.
I think I need django-rest-auth for this to expose an API endpoint something like this:
from rest_auth.views import PasswordResetView
urlpatterns = [
path('password/reset/', PasswordResetView.as_view(), name='rest_password_reset'),
]
Now posting to http://127.0.0.1:8000/password/reset/ with a JSON payload of { "email": "test1#test.com" } gives an error: django.urls.exceptions.NoReverseMatch: Reverse for 'password_reset_confirm' not found.
Now, I'm strugeling with the next part. I found that password_reset_confirm is defined in django.contrib.auth but I do not want to expose the admin-like interface to the user.
I'd like to use the allauth PasswordResetFromKeyView.
So, defining password_reset_confirm as:
path('password/reset/<uidb64>/<token>/',
PasswordResetFromKeyView.as_view(),
name='password_reset_confirm'
),
Works. An email is send containing a reset URL. But now, following that URL I'm getting another error: PasswordResetFromKeyView.dispatch() missing 2 required positional arguments: 'uidb36' and 'key'
Ok, obvious, changed the password_reset_confirm path arguments from <uidb64> and <token> to <uidb36> and <key>.
Than the error moves to password_reset_email.html because of the arguments in
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
Ok, also changed that to uidb32=uid and key=token results in a HTML page displaying "BAD TOKEN".
Now, I'm completely at a loss.
How to configure django-allauth and django-rest-auth so that I can do a rest request to send the email containing a valid URL which the user can use to change his/her password?
UPDATE: I just saw django-allauth is no longer maintained and that you should switch to: dj-rest-auth. Now the process starts all over again...
Ok, the following works, posting for reference because I have lost an awful lot of time on this.
Pipfile:
[packages]
django = "~=3.0"
django-allauth = "0.50.0"
django-rest-auth = "0.9.5"
urls.py:
from django.contrib import admin
from django.urls import path, re_path
# Register
from allauth.account.views import ConfirmEmailView
from rest_auth.registration.views import RegisterView, VerifyEmailView
# Password reset
from rest_auth.views import PasswordResetView, PasswordResetConfirmView
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^confirm-email/(?P<key>[-:\w]+)/$',
ConfirmEmailView.as_view(), name='account_confirm_email'),
path('user/register/',
RegisterView.as_view(),
name='rest_register'
),
path('user/verify-email/',
VerifyEmailView.as_view(),
name='rest_verify_email'
),
# Password reset
path('user/password/reset/',
PasswordResetView.as_view(),
name='rest_password_reset'
),
path('user/password/reset/confirm/<uidb64>/<token>/',
PasswordResetConfirmView.as_view(),
name='password_reset_confirm'),
]
I'm able to post to: http://127.0.0.1:8000/user/password/reset/ with a JSON payload of { "email": "test1#test.com" }.
A Email is generated with an reset URL, clicking this URL brings the user to the browsable API page of Django:
However, this page is not intended to be exposed to the user. So my next question on S.O. is: How to create a custom page for the user to reset his/her password?
Related
I am having a problem with the passeord reset system.
The code is as below. When I enter the respective URL into the browser address directly it shows the expected Django forms/pages.
However if I fill an email address and hit enter/click the link, I get the "Reverse for 'password_reset_confirm' not found. 'password_reset_confirm' is not a valid view function or pattern name." error at line 6 in password_reset_email.html.
But I have included the uid64! and the token!
Also, when I deliberately use an incorrect email address I get the "Reverse for 'password_reset_done' not found. 'password_reset_done' is not a valid view function or pattern name." error.
I cannot see from the django documentation, other similar questions on this site, or various guides, what the obvious simple step is that I must have missed.
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
app_name = 'users'
urlpatterns = [
path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
path('password_reset/confirm/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('password_reset/complete/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]
The problem is that Django does not use a namespace when reversing the password reset urls.py. You can stop the error by removing app_name='users' from your urls.py.
Alternatively, you can configure the password reset view to use the namespace:"
path('password_reset/', auth_views.PasswordResetView.as_view(success_url=reverse_lazy('users:password_reset_done')), name='password_reset'),
This will fix the immediate error, but you'll find that you need to make several other changes to fix similar errors. Removing app_name='users' is more straight forward.
When you define app_name you should specify it also for reverse.
Use users:password_reset_done instead of password_reset_done
Check reversing-namespaced-urls for more details.
I have been trying to setup password reset functionality in DRF using django-rest-auth. Earlier I was getting error TemplateDoesNotExist:registration/password_reset_email.html which I resolved by adding the following code
serializer.py
from rest_auth.serializers import PasswordResetSerializer
from allauth.account.forms import ResetPasswordForm
class PasswordSerializer(PasswordResetSerializer):
password_reset_form_class = ResetPasswordForm
settings.py
REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER': 'api.serializers.PasswordSerializer',
}
However, Now I am getting into another issue - "NoReverseMatch: Reverse for 'account_reset_password_from_key' not found. 'account_reset_password_from_key' is not a valid view function or pattern name.". And haven't found any solution or workaround for this.
Any help would be appreciated.
So, finally I got the password reset functionality working. Here is how it goes -
We just need one URL in our urls.py -
urlpatterns = [
url(r'^account/', include('allauth.urls')),
url(r'^rest-auth/', include('rest_auth.urls')),
# This is the only URL required for BASIC password reset functionality.
# This URL creates the confirmation link which is sent via e-mail. All of the rest
# password reset features get their reverse lookup via django-allauth and django-rest-auth.
url(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', TemplateView.as_view(), name='password_reset_confirm'),
url(r'^rest-auth/registration/account-confirm-email/(?P<key>[-:\w]+)/$', allauthemailconfirmation,
name="account_confirm_email"),
url(r'^rest-auth/registration/', include('rest_auth.registration.urls'), name='account_signup'),
]
Using this URL configuration raised TemplateDoesNotExist at /api/rest-auth/password/reset/ error first. After a lot of debugging, I found that the issue was raised for the template - registration/password_reset_email.html which resides under the Django Admin's template directory. This happened due to another Django app that I was using and it had disabled the django admin app.
So, adding 'django.contrib.admin' under INSTALLED_APPS and removing the serializers resolved the issue.
I hope this resolves issue for others as well.
PS: Debugger is your best friend. ;)
I want to have my custom "/login" page. So in settings.py I did a simple LOGIN_URL = '/login'. But before doing it, I want to develop all other more complex pages. I found a simple but very effective hack like this:
urlpatterns = [
# blabla
url(r'^admin/', include(admin.site.urls)),
url(r'^login/$', RedirectView.as_view(
url=reverse_lazy('admin:login'))),
# blabla
]
This means: when the user is not connected he/she is redirected to /login. In the urls, /login is converted to 'admin:login' which is admin/login. This is a "double" redirect. Everthing works fine except this:
origin URL: "/my_jobs"
redirected to "login?next=/my_jobs"
redirected to "/admin/login"
So my problem is that I want do pass again the "next" parameter in the RedirectView. I found a lot about redirection and custom login, but not something about that on stackoverflow (this is not a duplicate).
You can set query_string to True, so that query strings are appended to the URL.
RedirectView(
url=reverse_lazy('admin:login'),
query_string=True,
# You might want to set permanent=False,
# as it defaults to True for Django < 1.9
permanent=False,
)
Note that Django comes with a built in login view. You can enable it by adding the URL pattern and a simple template, which isn't much more work than your code above.
I only want to allow people to sign up or log in with their social account. I have the social sign up and log in working, but I cant figure out how to disable the local sign up.
I've read the docs and this sounds close to what I want
ACCOUNT_FORMS (={})
Used to override forms, for example: {‘login’: ‘myapp.forms.LoginForm’}
It seems like I can make a new sign up form and only include the social log in link, but I was hoping there is any easier way that I'm overlooking. I'm still new to this all so I tend to miss the obvious a lot still.
I also tried changing the code below to False, but that disabled social sign up as well.
allauth.account.adapter.py
def is_open_for_signup(self, request):
"""
Checks whether or not the site is open for signups.
Next to simply returning True/False you can also intervene the
regular flow by raising an ImmediateHttpResponse
"""
return True
Change templates and urlpatterns
You would have to change both the templates (login, signup, etc.) and urlpatterns provided by allauth by default, which relate to the classic signup/login flow using email.
Changing/reducing the available routes via the urlpatterns ensures that only the routes are available that should be there. HTTP error 404 is then shown for any attempt to hack into existing allauth default functionality (related to email) if you do it right.
Changing the templates can ensure that the user interface does not provide what is related to email-based authentication.
No easy option available
Unfortunately, as of today there is no easy switch or setting to simply disable email-based signup and authentication with django-allauth. More details may be on GitHub in future, see:
Issue #1227 ("Social only: disable all local account handling by means of a simple setting")
Issue #345 ("How to disable form login/signup?")
Sample: urls.py
An urls.py like this will work with the current django-allauth (v0.30.0) on Django 1.10:
from django.conf.urls import include, url
from allauth.account.views import confirm_email, login, logout
from allauth.compat import importlib
from allauth.socialaccount import providers
providers_urlpatterns = []
for provider in providers.registry.get_list():
prov_mod = importlib.import_module(provider.get_package() + '.urls')
providers_urlpatterns += getattr(prov_mod, 'urlpatterns', [])
urlpatterns = [
url(r'^auth/', include(providers_urlpatterns)),
url(r'^confirm-email/(?P<key>[-:\w]+)/$', confirm_email, name='account_confirm_email'),
url(r'^login/$', login, name='account_login'),
url(r'^logout/$', logout, name='account_logout'),
url(r'^signup/$', login, name='account_signup'), # disable email signup
]
The solution wasn't what I originally thought. The much easier way to do this, instead of changing the forms, was to change the template and just remove any other options in that template.
My page now correctly only shows social auth and I am happy.
If anyone has a better or more secure answer I'd be open to it. Being new still, I don't know if this is the best solution, but for now it seems great and will mark as answered.
Ok, here is the thing. If you are not using any social account to link to your users, then it's very simple to finish the task you described by simply only include urls you need. However, if you need to use social account to link your users, then you have to include all urls because most third party application will not certify the request from your app. they only accept request from allauth.
from django.urls import path, re_path
from allauth.account import views as accountviews
urlpatterns = [
path('admin/', admin.site.urls),
# remember to comment out the following line since it will
# include all urls from allauth lib
# path('accounts/', include('allauth.urls'))
]
# assume you only want singup page and login page from allauth
urlpatterns += [path("acc/signup/", accountviews.signup, name="account_signup"),
path("acc/login/", accountviews.login, name="account_login")
]
So I have Users (from django.contrib.auth.models import User) and UserProfiles. in my UserProfile view there is an edit link. This edit link allows a User to change their User settings. In the password section of the form I see help text that says:
"Use '[algo]$[salt]$[hexdigest]' or use the change password form."
The "change password form" is actually a link to http://127.0.0.1:8000/user/1/user_edit/password/, when I click the link I get an error message saying:
ViewDoesNotExist at /user/1/user_edit/password/
Could not import testdb.views.django.contrib.auth.views. Error was: No module named django.contrib.auth.views
I've been following the documentation: https://docs.djangoproject.com/en/dev/topics/auth/
What am I doing wrong? I hear that this should use djangos templates, do I need to copy those over to my apps template folder? if so, where are they?
URLS.PY
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('testdb.views',
url(r'^$', 'index'),
url(r'^^user/(?P<user_id>\d+)/$', 'user_detail'),
url(r'^user/(?P<user_id>\d+)/user_edit/$', 'user_edit'),
url(r'^user/(?P<user_id>\d+)/user_edit/password/$', 'django.contrib.auth.views.password_change', {'template_name': 'password_change_form'}),
)
You have a wrong URL pattern defined: Django tries to find testdb.views.django.contrib.auth.views as you define the password_change view inside patterns('testdb.views',.
Add a second pattern:
urlpatterns += patterns('django.contrib.auth.views',
url(r'^user/(?P<user_id>\d+)/user_edit/password/$', 'password_change')
)
That should resolve your issue.
cfedermann has a solution to your issue, but I'm confused as to why you've defined the password_change URL in the first place. This functionality is built-in to the admin, and - like all the other admin pages - the URL is defined already by the admin code itself.