Django get sub domain from which the request was sent from - django

i have a standalone front ends serving on different sub domains, and a single backend responding to all requests through API(s)
Say i have following subdomains
first.example.com
second.example.com
third.example.com
and my backend is at
backend.example.com
in my backend views i want to know which subdomain sent me request
i tried
self.request.get_host().split('.')[0]
but this gave me "backend" as result from every sub-domain
what i want to have :
if request was sent from first.example.com > in my
backend.example.com view i want to have "first"
if request was sent from second.example.com > in my
backend.example.com view i want to have "second"
and so on
Technologies information:
Subdomains using React
Backend using Django
Server Nginx
other server Gunicorn

You can get subdomain name from HTTP_REFERER
from urllib.parse import urlparse
urlparse(request.META.get('HTTP_REFERER')).hostname.split('.')[0]

Related

flask sessions not persistent between requests from cross domains

I have a frontend vue site hosted on google's firebase with the url (https://front-end.web.com) , while my flask backend is hosted on heroku with the url (https://back-end.heroku.com). This makes my session not to persist across requests, I tried fixing this by implementing CORS on my backend, but for some reason it's not working , below are snippets of my code to show my implementation
config_class.py
class ConfigClass():
CORS_ALLOW_HEADERS = ['Content-Type']
CORS_ORIGINS = ['https://front-end.web.com']
SECRET_KEY = os.environ.get("APP_SECRET_KEY")
SESSION_TYPE = 'redis'
_init.py
from flask import Flask, session
from flask_session import Session
from flask_cors import CORS
from root_folder.config import ConfigClass
db = SQLAlchemy()
migrate = Migrate()
ma = Marshmallow()
sess = Session()
def create_app(ConfigClass):
# initiate the flask app and assign the configurations #
app = Flask(__name__)
app.config.from_object(config_options[config_class])
sess.init_app(app)
from root_folder.clients import clients_app
# register all the blueprints in this application
app.register_blueprint(clients_app)
CORS(app, supports_credentials=True)
# return the app object to be executed
return app
app.py
from root_folder import create_app
app = create_app()
Procfile:
web: gunicorn -w 1 app:app
axios front end request
let formData = new FormData();
formData.append("email", email);
formData.append("password", password);
axios.post(
backendUrl+'create_client_account',
formData,
{
withCredentials: true,
headers:{
"Content-Type": "multipart/form-data"
}
}
);
create client route ( I have stripped this code block to the bare minimum to make it understandable):
from flask import session
# route for creating account credentials
#bp_auth_clients_app.route("/create_client", methods=["POST"])
def create_client():
username = request.form.get("username").lower()
email = request.form.get("email").lower()
# create account code goes here #
auth_authentication = True
session["auth_authentication"] = auth_authentication
req_feedback = {
"status": True,
"message": "Account was successfully created",
"data": feedback_data
}
return jsonify(req_feedback), 200
After the account is successfully created, I am unable to access the session value in subsequent requests, it returns None.
To recreate the problem on my local server, I access the front-end via the domain "localhost:8080" , while I access the flask server via "127.0.0.1:8000" . If I change the front end domain to "127.0.0.1:8080", I don't usually have any problems.
Kindly advice on what to do.
Thanks to Ahmad's suggestion, I was able to resolve the issue using custom domains for both my frontend and backend as follows:
frontend.herokuapp.com -> customDomain.com
backend.herokuapp.com -> api.customDOmain.com
finally I added the line below to my session config:
SESSION_COOKIE_DOMAIN = ".customDomain.com"
And all was well and good.
Sessions use cookies:
On session creation the server will send the cookie value in the set-cookie header. It doesn't work for you because of cross origin issue.
It works fine for you when you use 127.0.0.1 because 127.0.0.1:8080 and 127.0.0.1:8000 are the same origin so the browser accepts the set-cookie header and do set the cookie no problem.
Cookies are sent in the header on each request and your server loads the session from Redis by cookie value (The cookie value is called session_id).
How it gets inserted => Normally your session gets serialized and inserted in Redis with the cookie hash as Key in the end of the request life cycle.
If you want to keep using sessions and cookies you need to find another solution for your deployment to so that your backend and frontend have the same hostname.
If you can't do I'd recommend to read about JWT (Json-Web-Tokens).
EDIT
You can send the session id in your response body and save it in local storage.
Then you need to configure:
frontend set the session id value it in the Authorization header base64 encoded.
Backend base64 decode Authorization header value from request and check for the session in Redis, if exists load it.
EDIT
How to deploy both backend/frontend on same hostname using apache:
using apache you need to create 2 virtual hosts one for backend and the other for frontend listening on different ports then configure your web server deployment to use the backend VH if the path is prefixed by /api/ and use the frontend Virtual host for anything else.
This way any request you make to your api your backend will handle it otherwise it'll serve your frontend app.
This is just a way on how to do it there is plenty others
Check this question.

Redirect URI mismatch error from Google OAuth

I have a Flask web application which is hosting in Google Cloud Run which is hosted with https://mydomain.run.app.
Now I am trying to add google authentication to it. I have created the API under credentials in GCP. I have given https://mydomain.run.app/authorize in the redirect uri but when I tried to login from my app it throws me redirect mismatch error.
And the error shows me http://mydomain.run.app/authorize.
The mismatch is the https and http
When I tried to give http in the credentials uri it throws me
Invalid Redirect: This app has a publishing status of "In production". URI must use https:// as the scheme.
#app.route('/login/google')
def google_login():
google = oauth.create_client('google')
redirect_uri = url_for('authorize', _external=True,_scheme='https')
return google.authorize_redirect(redirect_uri)
#app.route('/authorize')
def authorize():
google = oauth.create_client('google')
token = google.authorize_access_token()
resp = google.get('userinfo')
user_info = resp.json()
user = oauth.google.userinfo()
session['profile'] = user_info
session.permanent = True
return redirect('/select')
Under Authorized redirect URIs
You should put 1 more URI :
https://mydomain.run.app/
Then check again. I have got same issue before.
your app is currently set to production in google developer console.
This means that all of the redirect uris you try to add to your project. Must be HTTPS and not HTTP you can also not use localhost
As you are trying to use http://mydomain.run.app/authorize you need to change it so that it is https://mydomain.run.app/authorize note that the first one was http:// and not https://
The error is coming because your application itself is trying to send a redirect uri of http and not https. You need to fix your application so that it is using https.

Flask - How to access remember_me token in all subdomains

I am trying to create an app using Flask with more than 9 controllers, some of them are in a different subdomains.
I am using Flask_Login to allow users to login, the users controller exist in a separated subdomain, the problem happens if i visited that subdomain, inside my console it shows a redirect to login the user first to access that subdomain, and inside cookies i can't see the remember_me token.
Here are the configurations for the extension:
SERVER_NAME = 'localhost:5000'
# Login configurations
REMEMBER_COOKIE_DURATION = timedelta(seconds=7*24*60*60)
REMEMBER_COOKIE_NAME = 'myapp.remember'
REMEMBER_COOKIE_SECURE = True
REMEMBER_COOKIE_HTTPONLY = True
REMEMBER_COOKIE_REFRESH_EACH_REQUEST = True
REMEMBER_COOKIE_DOMAIN = '.localhost:5000'
from .controllers.client import client_route
app.register_blueprint(client_route, subdomain='client')
The domain inside cookies is localhost, how can i change it to something like .localhost ??
You need to set REMEMBER_COOKIE_SECURE in your config to False
According to Flask-Login's documentation:
Restricts the “Remember Me” cookie’s scope to secure channels
(typically HTTPS).
It only sets the cookie on HTTPS version of your application

How to set cookie in django redirect under HTTPS?

I'm making an app (like single sign on) that:
user request to login service A
get username/password from db and login A by web_api of A
make a HttpResponseRedirect, set cookie and return to user.
User <==> app <==> service(https)
The whole process is good in HTTP. but when service A is under HTTPS then it's not work. The cookie set in step 3 will disappear when user be redirected to A website.
response = HttpResponseRedirect('https://xxxx/service')
response.set_cookie(key=cookie.name, value=cookie.value,
domain=cookie.domain, path=cookie.path, expires=cookie.expires)

How to enable https in Django-auth generated pages?

Using the Django-auth application (Django version 1.3), I want to have my login page go to https://mysite.com/login/. Currently, I'm using:
# urls.py
from django.contrib.auth.views import login
urlpatterns = patterns('', url(r'^login/$', login, name='login-view'),)
# navbar.html
<li id="nav-login"><a href="{% url login-view %}" ><b>Login</b></a></li>
which works nicely, but goes to http://mysite.com/login/.
Is there some way to tell Django-auth what prefix (https) to use, when it reverses the view name? I've read the entire manual page, and haven't found anything that covers it. Or maybe some way to tell the url tag to go to https?
Or is the only option to specify the entire URL manually? I hope not :) And given how powerful Django has been so far, I can't believe it wouldn't have that ability - I must be overlooking it. :)
Set OS environmental variable HTTPS to on
You need to enable the OS environmental variable HTTPS to 'on' so django will prepend https to fully generated links (e.g., like with HttpRedirectRequests). If you are using mod_wsgi, you can add the line:
os.environ['HTTPS'] = "on"
to your wsgi script. You can see the need for this by reading django/http/__init__.py:
def build_absolute_uri(self, location=None):
"""
Builds an absolute URI from the location and the variables available in
this request. If no location is specified, the absolute URI is built on
``request.get_full_path()``.
"""
if not location:
location = self.get_full_path()
if not absolute_http_url_re.match(location):
current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
self.get_host(), self.path)
location = urljoin(current_uri, location)
return iri_to_uri(location)
def is_secure(self):
return os.environ.get("HTTPS") == "on"
Secure your cookies
In settings.py put the lines
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
and cookies will only be sent via HTTPS connections. Additionally, you probably also want SESSION_EXPIRE_AT_BROWSER_CLOSE=True. Note if you are using older versions of django (less than 1.4), there isn't a setting for secure CSRF cookies. As a quick fix, you can just have CSRF cookie be secure when the session cookie is secure (SESSION_COOKIE_SECURE=True), by editing django/middleware/csrf.py:
class CsrfViewMiddleware(object):
...
def process_response(self, request, response):
...
response.set_cookie(settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
domain=settings.CSRF_COOKIE_DOMAIN,
secure=settings.SESSION_COOKIE_SECURE or None)
Direct HTTP requests to HTTPS in the webserver
Next you want a rewrite rule that redirects http requests to https, e.g., in nginx
server {
listen 80;
rewrite ^(.*) https://$host$1 permanent;
}
Django's reverse function and url template tags only return relative links; so if you are on an https page your links will keep you on the https site.
As seen in other StackOverflow questions, you could implement middleware that would automatically redirect the login page to a secure version.
If you are really serious about security, you should probably migrate the entire website to SSL. From the EFF's How to Deploy HTTPS Correctly:
You must serve the entire application domain over HTTPS. Redirect HTTP requests with HTTP 301 or 302 responses to the equivalent HTTPS resource.
Some site operators provide only the login page over HTTPS, on the theory that only the user’s password is sensitive. These sites’ users are vulnerable to passive and active attack.