1

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?

Neil C. Obremski
  • 18,696
  • 24
  • 83
  • 112
  • Is the redirection happening at the Nginx level or the django level? – Matt Seymour Jan 22 '20 at 22:17
  • @MattSeymour - At the Django level as part of the `social-auth-app-django` package. I'm going to dig into the source code when I have a moment. I'm sure this is a common issue that I'm asking about incorrectly – Neil C. Obremski Jan 22 '20 at 23:56
  • Possible duplicate of https://stackoverflow.com/questions/29436271/django-social-auth-redirect-to-url-with-port-number? – solarissmoke Jan 23 '20 at 01:57
  • @solarissmoke - Similar issue but with the reverse problem: I want to _add_ the port (8080) not strip it out. However, there may be something I can do in Nginx to pass along the port number received. Thanks for the link; I'll try some of this out. – Neil C. Obremski Jan 23 '20 at 17:16

1 Answers1

1

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
Neil C. Obremski
  • 18,696
  • 24
  • 83
  • 112
  • Running Nginx within ELB for HTTPS has a similar issue. I found a solution to that here: https://tecadmin.net/force-redirect-https-behind-aws-elb/ – Neil C. Obremski Jan 27 '20 at 20:21