iMerica / dj-rest-auth

Authentication for Django Rest Framework
https://dj-rest-auth.readthedocs.io/en/latest/index.html
MIT License
1.65k stars 306 forks source link

Make Domain for Auth related E-Mails changable #356

Open oesah opened 2 years ago

oesah commented 2 years ago

Hey everyone, I spent couple hours today to find out how to change the domain of the reset URL you get via E-Mail. It was always pointing to the domain where my backend ran. But I wanted it to point to my frontend URL, which is a different subdomain. My solution looks like this:

from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.urls.base import reverse
from allauth.account import app_settings
from allauth.account.adapter import get_adapter
from allauth.account.utils import user_pk_to_url_str, user_username
from allauth.utils import build_absolute_uri
from dj_rest_auth.forms import AllAuthPasswordResetForm
from dj_rest_auth.serializers import PasswordResetSerializer

class CustomAllAuthPasswordResetForm(AllAuthPasswordResetForm):
    def save(self, request, **kwargs):
        current_site = get_current_site(request)
        email = self.cleaned_data['email']
        token_generator = kwargs.get('token_generator',
                                     default_token_generator)

        for user in self.users:

            temp_key = token_generator.make_token(user)

            # save it to the password reset model
            # password_reset = PasswordReset(user=user, temp_key=temp_key)
            # password_reset.save()

            # send the password reset email
            path = reverse(
                'password_reset_confirm',
                args=[user_pk_to_url_str(user), temp_key],
            )
            url = build_absolute_uri(None, path) # PASS NONE INSTEAD OF REQUEST

            context = {
                'current_site': current_site,
                'user': user,
                'password_reset_url': url,
                'request': request,
            }
            if app_settings.AUTHENTICATION_METHOD != app_settings.AuthenticationMethod.EMAIL:
                context['username'] = user_username(user)
            get_adapter(request).send_mail('account/email/password_reset_key',
                                           email, context)
        return self.cleaned_data['email']

class CustomPasswordResetSerializer(PasswordResetSerializer):
    @property
    def password_reset_form_class(self):
        return CustomAllAuthPasswordResetForm

# settings.py
REST_AUTH_SERIALIZERS = {
    'PASSWORD_RESET_SERIALIZER':
    'api.users.api.serializers.CustomPasswordResetSerializer',
}

However, this to me seems like an overkill. So much code for basically 1 line I needed to change so that the e-mails have the domain of the frontend.

Basically, I changed one line: build_absolute_uri(request, path) --> build_absolute_uri(None, path). Now it takes the value defined in django.contrib.sites module in the admin.

Am I missing something? I looked into other related issues but it always looked like a lot of code changes for something I thaught should be one line in my settings.py.

If this is the case, can we implement a feature where we can change the domain of ALL URLs related to authentication?

oesah commented 2 years ago

Related #357