djoser icon indicating copy to clipboard operation
djoser copied to clipboard

URLs for emails can't be configured for other domains

Open LyleScott opened this issue 4 years ago • 9 comments

It looks like djoser is restricted to using the current site's object for building URLs (ie, ACTIVATE_URL) which leaves you with the protocol and domain of the django server. This is a scenario that needs to be configurable, imho, to account for a frontend hosted on some other domain.

For example, I typically have a Django-based API on the api domain (api.foobar.com) and web files served up on the www domain (www.foobar.com) via some static-file-only CDN.

Evidence:

The djoser Activation Email object that produces an evaluated template: https://github.com/sunscrapers/djoser/blob/84f2ca4e7ce3dd05ce9b145fcc5353e77ebffc05/djoser/email.py#L8

The email template using protocol/domain: https://github.com/sunscrapers/djoser/blob/84f2ca4e7ce3dd05ce9b145fcc5353e77ebffc05/djoser/templates/email/activation.html#L22

The django-templated-email handler that fills in the template: (you could set DOMAIN, sure, but this seems like a hack and likely will be disruptive to another module) https://github.com/sunscrapers/django-templated-mail/blob/65e5a34f69d50d4d1b6acdd337f9efcc44b32fae/templated_mail/mail.py#L33

LyleScott avatar Jun 02 '20 03:06 LyleScott

Would something like this fly? This puts it in the context so it's an override:

diff --git a/djoser/email.py b/djoser/email.py
index 8c01925..0ee0551 100644
--- a/djoser/email.py
+++ b/djoser/email.py
@@ -15,7 +15,15 @@ class ActivationEmail(BaseEmailMessage):
         user = context.get("user")
         context["uid"] = utils.encode_uid(user.pk)
         context["token"] = default_token_generator.make_token(user)
+
+        # Use the [possibly] user supplied values to build links within emails.
+        keys = ("protocol", "domain")
+        for key in keys:
+            settings_value = getattr(settings, key.upper(), None)
+            if settings_value:
+                context[key] = settings_value
         context["url"] = settings.ACTIVATION_URL.format(**context)
+
         return context
 
 
diff --git a/docs/source/base_endpoints.rst b/docs/source/base_endpoints.rst
index ac357e0..a4aaf4e 100644
--- a/docs/source/base_endpoints.rst
+++ b/docs/source/base_endpoints.rst
@@ -38,9 +38,9 @@ User Activate
 
 Use this endpoint to activate user account. This endpoint is not a URL which
 will be directly exposed to your users - you should provide site in your
-frontend application (configured by ``ACTIVATION_URL``) which will send ``POST``
-request to activate endpoint. ``HTTP_403_FORBIDDEN`` will be raised if user is already
-active when calling this endpoint (this will happen if you call it more than once).
+frontend application (configured by ``DOMAIN``, ``PROTOCOL``, and ``ACTIVATION_URL``) which will
+send a ``POST`` request to the activate endpoint. ``HTTP_403_FORBIDDEN`` will be raised if user is
+already active when calling this endpoint (this will happen if you call it more than once).
 
 **Default URL**: ``/users/activation/``

LyleScott avatar Jun 02 '20 03:06 LyleScott

Had the same problem, solved by overriding email classes. It would be nice to working as @LyleScott suggests.

lfx avatar Jun 07 '20 00:06 lfx

@LyleScott Did you create a pull request already?

ndrbrt avatar Jul 13 '20 11:07 ndrbrt

@LyleScott This is handy, I want the URL to redirect on the frontend. Because my frontend is separate from the backend.

malikfaiq avatar Dec 17 '20 07:12 malikfaiq

@LyleScott I have created the pull request for this issue. https://github.com/sunscrapers/djoser/pull/566

malikfaiq avatar Dec 18 '20 09:12 malikfaiq

I have the same issue, it would be great to implement this feature.

carloskiki avatar Apr 19 '21 02:04 carloskiki

For those who want a fix until then, here is what I did for my app:

  • Add an emails.py file to your app
  • Add this code to your emails.py file:
from templated_mail.mail import BaseEmailMessage
from django.contrib.auth.tokens import default_token_generator
from djoser import utils
from djoser.conf import settings

class ActivationEmail(BaseEmailMessage):
    template_name = "email/activation.html"

    def get_context_data(self):
        # ActivationEmail can be deleted
        context = super().get_context_data()

        user = context.get("user")
        context["uid"] = utils.encode_uid(user.pk)
        context["token"] = default_token_generator.make_token(user)
        context["url"] = settings.ACTIVATION_URL.format(**context)
        context["domain"] = "site_domain" # Your site domain
        context["protocol"] = "site_protocol" # Your site protocol e.g. ("http", "https")
        return context
  • Add this code to your settings.py file:
DJOSER = {
    #... your other settings
    "EMAIL": {
        "activation": "app.emails.ActivationEmail" # app being your app's name
    }
}

Everything should now work properly.

carloskiki avatar Apr 19 '21 15:04 carloskiki

How does djoser decide which protocol to use?

snnbotchway avatar Feb 09 '23 01:02 snnbotchway

Whoa, sorry, y'all. I just saw this notification. Tales from the crypt. I don't use any of this stuff anymore, but I can perhaps revisit in the near future to see what needs to be done to get this across this finishline. I'm happy to contribute if time allows, cheers.

LyleScott avatar Feb 24 '23 17:02 LyleScott