I am trying to integrate flask-login with a React application. After the user has been logged in, it appears that flask-login enforces a page redirect.
Since we don't want to redirect to the same page we have to make sure that the actual back redirect is slightly different (only use the submitted data, not the referrer)
Source: https://web.archive.org/web/20120504074405/http://flask.pocoo.org/snippets/62
The difference between what the documentation is showing and what I'm doing is that my client is on a different domain (thus, I will be faced with CORS problems), whereas the documentation's client and server (I assume) on the same domain.
This is evident in this code snippet:
def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ('http', 'https') and \
ref_url.netloc == test_url.netloc
From above, the function returns true if ref_url.netloc == test_url.netloc (I left out the other portion for simplicity). I looked into the attribute of netloc which turns out to be the domain name without the top-level domain. For example, if the domain name is: domain.com, then the netloc would be domain.
However, for my scenario, that won't work.
For example, frontend: www.frontend.com backend: www.backend.com
The request to login to backend should redirect back to say, www.frontend.com/home. Based off the code presented in the documentation, it would never redirect to the intended destination. So I tried to modify the code a bit and got this:
def is_safe_url(request, target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ['http', 'https'] and \
(test_url.netloc == 'localhost:3000' or \
ref_url.netloc == test_url.netloc
)
I figured if I configure the redirect in a way where it checks for a suitable domain (in my example, it would be localhost:3000) and check for domains that are not allowed, then there shouldn't be any security threats here. However, please correct me if I'm missing something.
Sadly, this results in a CORS issue.
I would also like to add that most of the examples provided by flask-login is using jinja templating. Something that isn't very clear from the documentation is that should redirects even be used if you aren't using them?
Rest of the code:
def is_safe_url(request, target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ['http', 'https'] and \
(test_url.netloc == 'localhost:3000' or \
ref_url.netloc == test_url.netloc
)
def get_redirect_target(request):
for target in request.values.get('next'), request.referrer:
print('target', target)
if not target:
continue
if is_safe_url(request, target):
return target
@user_bp.route('/login', methods=['GET', 'POST'])
def login():
next = get_redirect_target(request)
if current_user.is_authenticated:
return jsonify({ 'login': True })
if request.method == 'POST':
data = request.get_json()
form_input = ImmutableMultiDict(data)
form = UserAuthenticationForm(form_input)
if form.validate():
try:
user = RegisteredUser.query.filter_by(email = data['user']).first() or RegisteredUser.query.filter_by(username = data['user']).first()
if user is not None and user.check_password(data['password']):
login_user(user)
response = redirect(next)
print(response.get_data())
return response
# return jsonify({ 'login': True })
except Exception as e: # Handle a faulty connection to the database
print('Error: ' + str(e))
return jsonify({ 'login': False })
Edit:
CORS Setting:
CORS(app, origins=["http://localhost:3000"], expose_headers=["Content-Type", "X-CSRFToken"], supports_credentials=True)
