djangopython-social-authdjango-socialauth

How to specify explicit host name in Django settings?


I'm working on a Django project that runs via several Docker containers and the Django site itself is a middleware that an Nginx container forwards requests to. And for testing the port 80 of the Nginx container is mapped to 8080. Thus I go to http://localhost:8080/ and see the Django website.

I'm having the issue that a redirect URL (constructed by social-auth-app-django package for Google Login) uses http://localhost as a base rather than http://localhost:8080.

The setting LOGIN_REDIRECT_URL isn't what I'm looking for because it occurs after authentication is successful.

I tried SOCIAL_AUTH_LOGIN_REDIRECT_URL which was mentioned somewhere but that was for an old version and no longer appears to do anything.

Since Django uses build_absolute_uri for all absolute URLs, there must be a way to override the base URL and/or host?


Solution

  • TL;DR = Nginx has no concept of Docker-mapped ports and needs to have them hard-coded.

    Similar Issue: https://serverfault.com/questions/577370/how-can-i-use-environment-variables-in-nginx-conf

    Django requires that the HTTP request headers contain the information it needs for the host (code snippet below). In this case it needs to be passed in from Nginx:

    location / {
        proxy_pass http://web:7000;
        proxy_set_header Host $host:8080;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    

    Note the unfortunate hard-coding of the port: $host:8080. The "outer" problem is that the port of the Nginx container is mapped using Docker to 8080 (-p 80:8080) and thus it has no idea that it's actually running on port 8080; Nginx detects itself as running on port 80.

    There is a Django setting for SOCIAL_AUTH_LOGIN_REDIRECT_URL but specifying it like so:

    SOCIAL_AUTH_LOGIN_REDIRECT_URL = 'http://localhost:8080/complete/google-oauth2/'
    

    Causes this to happen when trying to authenticate through Google:

    AuthMissingParameter at /complete/google-oauth2/

    Missing needed parameter state

    Request Method: GET

    Request URL: http://localhost:8080/complete/google-oauth2/

    Django Version: 2.1.5

    Exception Type: AuthMissingParameter

    Exception Value:

    Missing needed parameter state

    Exception Location: /usr/local/lib/python3.6/site-packages/social_core/backends/oauth.py in validate_state, line 88

    Python Executable: /usr/local/bin/python

    Python Version: 3.6.10

    Thus the only solution I have at this point is to bake the port into the Nginx configuration when building the Docker image.

    Django version 2.1.5 code for getting host:

    def _get_raw_host(self):
        """
        Return the HTTP host using the environment or request headers. Skip
        allowed hosts protection, so may return an insecure host.
        """
        # We try three options, in order of decreasing preference.
        if settings.USE_X_FORWARDED_HOST and (
                'HTTP_X_FORWARDED_HOST' in self.META):
            host = self.META['HTTP_X_FORWARDED_HOST']
        elif 'HTTP_HOST' in self.META:
            host = self.META['HTTP_HOST']
        else:
            # Reconstruct the host using the algorithm from PEP 333.
            host = self.META['SERVER_NAME']
            server_port = self.get_port()
            if server_port != ('443' if self.is_secure() else '80'):
                host = '%s:%s' % (host, server_port)
        return host