It seems like in Flask, cookies are set by modifying the response object directly.
How can I return a response object, but also redirect a user to a different page upon successful login? I'd like to specifically redirect the user instead of rendering a different page, in case the user hits REFRESH.
Here's my current code, which simply displays the same page, login.html:
#app.route('/login', methods=['POST', 'GET'])
def login():
errors = []
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
#Check the user's e-mail
try:
u = User(email)
except UserError, e:
errors.append(e)
else:
#Check the user's password
if not u.authenticatePassword(password):
errors.append(('password','Invalid password'))
return render_template('login.html',error=errors)
#Set the session
s = Session()
s.user_id = u.user_id
s.ip = request.remote_addr
#Try to set the cookie
if s.setSession():
response = make_response( render_template('login.html',error=errors))
response.set_cookie('session_id', s.session_id)
return response
return render_template('login.html',error=errors)
You should change your code to something like:
from flask import make_response
if s.setSession():
response = make_response(redirect('/home'))
response.set_cookie('session_id', s.session_id)
return response
Related
I am trying to using to use Flask-JWT extended at the basic level at the moment.
I get the user and password authenticated from the user form. I create an accesss token, include it in the response and route to another protected route. Please find the shorter version of code as below...
from flask import Flask, jsonify, request
from flask_jwt_extended import,JWTManager, jwt_required, create_access_token,get_jwt_identity)
app.config['JWT_SECRET_KEY'] = 'super-secret'
jwt = JWTManager(app)
app.config['JWT_TOKEN_LOCATION'] = ['headers']
app.config['JWT_BLACKLIST_ENABLED'] = True
jwt = JWTManager(app)
app.config['PROPAGATE_EXCEPTIONS'] = True
#log_blueprint.route('/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'POST':
if error is None and username = form.request['user'] and pwd = form.request['pwd'] :
access_token = create_access_token(identity=user)
resp = redirect(url_for('log_blueprint.protected'),access_token)
resp.headers = {'Authorization': 'Bearer {}'.format(access_token)}
return resp
#log_blueprint.route('/protected', methods=["POST","GET"])
#jwt_required
def protected():
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
It gives me the error as below...
{"msg":"Missing Authorization Header"}
I tried the answers on this page...https://stackoverflow.com/questions/52087743/flask-restful-noauthorizationerror-missing-authorization-header
But couldnt get better.
Please let me know any solution for this issue.
Sorry if any typo mistake.
Thanks and regards,
Abhinay J K
Depending on the version you are using, accordind to change log of latest stable version, you should be using notation like:
#log_blueprint.route('/protected', methods=["POST","GET"])
#jwt_required()
def protected():
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
If you are using postman to send request, make sure you check the "key".
I'm currently testing my Flask application using Pytest and ran into a problem with a POST request and a redirect. Let me explain a bit more.
A user wants to register for our new site, but must confirm they have an account with a different site. Once they confirm the credentials of the other account, they are taken to the register page. They can only hit the register page if coming from the confirmation page else they are redirected back to the home page.
I want to test this functionality and can successfully make a POST request to the confirmation page. If I don't specify follow_redirects=True and print the response data, I get the following HTML:
Redirecting...
Redirecting...
You should be redirected automatically to target URL: /register?emp_id=1. If not click the link.
Great! Exactly what I'm looking for! I want to be redirected to the registration page.
Now when I do specify follow_redirects=True and print out the response data, I expected the register page HTML to return. The response data instead returns the home page HTML.
I further investigated where the problem was. As I mentioned before, the only way you can hit the registration page is from the confirmation page. I took a look at the request.referrer attribute in the view during the test and it will return None. I attempted setting the Referrer header content in the test's POST request, but to no luck.
Here is the code I'm working with:
views.py
#app.route('/confirm', methods=['GET', 'POST'])
def confirm_bpm():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = BPMLoginForm()
if form.validate_on_submit():
bpm_user = BPMUser.query\
.filter(and_(BPMUser.group_name == form.group_name.data,
BPMUser.user_name == form.username.data,
BPMUser.password == encrypt(form.password.data)))\
.first()
if not bpm_user:
flash('BPM user account does not exist!')
url = url_for('confirm_bpm')
return redirect(url)
if bpm_user.user_level != 3:
flash('Only L3 Users can register through this portal.')
url = url_for('confirm_bpm')
return redirect(url)
url = url_for('register', emp_id=bpm_user.emp_id)
return redirect(url)
return render_template('login/confirm_bpm.html', form=form)
#app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
if request.method == 'GET' and\
request.referrer != request.url_root + 'confirm':
return redirect(url_for('index'))
emp_id = request.args.get('emp_id')
emp_id_exists = User.query.filter_by(emp_id=emp_id).first()
if emp_id_exists:
flash('User is already registered!')
return redirect(url_for('login'))
form = RegistrationForm()
if form.validate_on_submit():
new_user = User(login_type=form.login_type.data, login=form.login.data,
emp_id=emp_id)
new_user.set_password(form.password.data)
db.session.add(new_user)
db.session.commit()
flash('Registration successful!')
return redirect(url_for('login'))
return render_template('login/register.html', form=form)
TESTS
base.py
from config import TestConfig
from app import app, db
#pytest.fixture
def client():
"""
Initializes test requests for each individual test. The test request
keeps track of cookies.
"""
app.config.from_object(TestConfig)
client = app.test_client()
ctx = app.app_context()
ctx.push()
yield client
ctx.pop()
def confirm_bpm_login(client, group_name, username, password):
"""
POST to /confirm
"""
return client.post('/confirm', data=dict(
group_name=group_name,
username=username,
password=password,
submit=True
), follow_redirects=True)
test_auth.py
from app import db
from app.models import BPMCompany, BPMEmployee, User, BPMUser
from tests.base import client, db_data, login, confirm_bpm_login
def test_registration_page_from_confirm(client, db_data):
"""
Test registration page by HTTP GET request from "/confirm" url.
Should cause redirect to registration page.
!!! FAILING !!!
Reason: The POST to /confirm will redirect us to /register?emp_id=1,
but it will return the index.html because in the register view,
request.referrer does not recognize the POST is coming from /confirm_bpm
"""
bpm_user = BPMUser.query.filter_by(id=1).first()
rv = confirm_bpm_login(client, bpm_user.group_name,
bpm_user.user_name, 'jsmith01')
assert b'Register' in rv.data
The db_data parameter is a fixture in base.py that just populates the DB with the necessary data for the registration flow to work.
My goal is to test a complete registration flow without having to separate the confirmation and registration into two tests.
The Flask test client does not appear to add the Referer header when it follows redirects.
What you can do is implement your own version of following redirects. Maybe something like this:
def confirm_bpm_login(client, group_name, username, password):
"""
POST to /confirm
"""
response = client.post('/confirm', data=dict(
group_name=group_name,
username=username,
password=password,
submit=True
), follow_redirects=False)
if 300 <= response.status_code < 400:
response = client.get(response.headers['Location'], headers={
"Referer": 'http://localhost/confirm'
})
return response
Please test this, I wrote it from memory and may need some minor adjustments.
Good day. I'm starting with Flask in Python. Everything goes fine, but I have a problem with the session. The idea is to store the base64 avatar of the user into his session after he logged in. Then, in the layout just access to his avatar by <img src="data:image/png;base64,{{session.user.photo}}" />.
The problem comes when I login, put the base64 into the session and redirect to /home. Inside /home route, I have a simple access control, if the session hasn't user key, redirect to login again. The problem is after set the session and redirect to home, the session is empty in /home, and never render the template.
#root.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
if 'user' in session:
return redirect('/home')
else:
return render_template('login.html', message = None)
else:
username = request.form['username']
pwd = request.form['password']
user = User.where('username', username).where('pwd', pwd).get().first()
if(user == None):
return render_template('login.html', message = 'Usuario o contraseƱa incorrecta')
else:
session['user'] = {
'id': user.id,
'username': user.username,
'photo': user.photo
}
return redirect('/home')
#root.route('/home')
def home():
if 'user' in session: # THIS IS FALSE, SESSION IS EMPTY
return render_template('home.html')
else:
return redirect(url_for('root.login', message = None))
Flask only accept small data in session? thanks.
I am sorry you shouldn't store a big image in the session.
A good way to achieve it is to store unique image id, and call url with this unique ID. Like this:
<img src = "//myview?id=513654534343543543" />
In short:
By only using the Flask micro-framework (and its dependencies) can we perform an internal redirect from one route to another?
For example:
User submits the registration form (both username and password) to #app.route('/register', methods=['POST'])
If the registration is successful, Flask internally does an HTTP POST to #app.route('/login', methods['POST']) passing the username and password
Process and log in the user
Details:
I am building a REST API using Flask and the Flask-JWT extension. More specifically I'm implementing the login and registration.
Login works perfectly and returns a JSON object with a token.
Following is my (login) authentication handler (i.e. /auth (POST request) - Default Flask-JWT authentication URL rule):
#jwt.authentication_handler
def authenticate(username, password):
user = User.query.filter_by(username=username).first()
if user and user.verify_password(password):
return user
return None
A successful login returns:
{
"token": "<jwt-token>"
}
Following is my registration route:
#app.route('/register', methods=['PUT'])
def register():
username = request.form.get('username')
password = request.form.get('password')
if username is None or password is None:
abort(400) # missing parameters
user = User.query.filter_by(username=username).first()
if user:
abort(400) # user exists
else:
user = User(user=user)
user.hash_password(password)
db.session.add(user)
db.session.commit()
# How do we generate a token?
# Perform an internal redirect to the login route?
return jsonify({'token': <jwt-token>}), 201
You should use the Post-Redirect-Get pattern.
from flask import Flask, redirect, request, render_template
app = Flask("the_flask_module")
#app.route('/', methods=["GET", "POST"])
def post_redirect_get():
if request.method == "GET":
return render_template("post_redirect_get.html")
else:
# Use said data.
return redirect("target", code=303)
#app.route("/target")
def target():
return "I'm the redirected function"
app.run(host="0.0.0.0", port=5001)
And if you want to pass data to the target function (like that token) you can use the session object to store it
So that would break down something like
#app.route('/register', methods=['PUT'])
def register():
username = request.form.get('username')
password = request.form.get('password')
if username is None or password is None:
abort(400) # missing parameters
user = User.query.filter_by(username=username).first()
if user:
abort(400) # user exists
else:
user = User(user=user)
user.hash_password(password)
db.session.add(user)
db.session.commit()
# How do we generate a token?
redirect("login_success", code=307)
#app.route("login_success", methods=["GET", "POST"])
#jwt_required()
def login_success():
return "Redirected Success!"
Edit:
I haven't used Flask-JWT before and didn't know about the post requirement. But you can tell Flask to redirect with the current method used (rather than a get request) by passing the redirect function code=307.. Hopefully that solves your extended problem.
I'm using flask. Here's my login function:
#app.route("/", methods=["GET", "POST"])
def login():
if request.method == "POST" and "imei" in request.form and "password" in request.form:
imei = request.form["imei"]
password = request.form["password"]
worked, name = checkDB(imei, password)
if worked:
uid = hashlib.sha256(imei.encode('utf-8')).hexdigest()
u = User(imei, name, password, uid)
USERS[uid] = u
login_user(u)
#return request.args.get("next")
return redirect(url_for("analyzer")) #THIS DOESENT WORK
else:
return redirect(url_for("login") + "?failure=true")
elif request.method == "GET" and request.args.get("failure"):
return render_template("auth.html", fail="true")
else:
return render_template("auth.html", fail="false")
When the line attempts to trigger (the one labeled THIS DOESENT WORK), it redirects to: /?next=%2Fwebike%2Fanalyzer.
The analyzer is simple:
#app.route('/analyzer', methods=['GET'])
#login_required
def analyzer():
return render_template('index.html')
What am I doing wrong? If the username and password are wrong, everything works as intended.
The login didn't work properly. You successfully redirect to /analyzer, but that redirects back to your login method because of the #login_required decorator, appending the analyzer method url as the next parameter.
u = User(imei, name, password, uid)
Here, you're not performing a lookup, you're making a new User object. Instead of making a new object, you'll need to get the User from your database and pass that to the login_user method. Documentation for the method here.