How to display username in multiple pages using flask? - python

I have encountered a problem of having the username not being shown on all the pages running under my flask application, it only runs at the home page because I return it from the login function. However, I don't know how could I get all the pages being called to have the username visible in all of them.
In the code attached, the def featured_books() doesn't return the username as it should be, instead it returns it as 1. How to solve this issue?
flask.py
#app.route('/')
# Gets correct username
#app.route('/index')
def home(post=1):
return render_template("index.html", username=post)
# Doesn't get the username
#app.route('/featured_books')
def featured_books(post=1):
return render_template("featured_books.html", username=post)
#app.route('/login')
def login(post=1):
if not session.get('logged_in'):
return render_template("login.html")
else:
return home(post)
#app.route('/login', methods=['POST'])
def do_login():
POST_USERNAME = str(request.form['username'])
POST_PASSWORD = str(request.form['password'])
secure_password = sha512_crypt.encrypt(POST_PASSWORD)
Session = sessionmaker(bind=engine)
s = Session()
query = s.query(User_reg).filter(User_reg.username.in_([POST_USERNAME]),
User_reg.password.in_([POST_PASSWORD]))
result = query.first()
if result:
session['logged_in'] = True
else:
flash('Wrong password!')
return login(POST_USERNAME)

I was working on the same thing and came across your question.
Have you figured it out?
Some suggest using Flask-Login extension to handle session management. Because I'm working on a simple app I decided to put the user name into a session cookie. Then can just grab it from the session on another page. Seemed like the path of least resistance, though, may not be the best choice.
Storing it in route.py
session['user_name'] = user.firstname + ' ' + user.lastname
Grabbing it from an html page
<h3> Welcome, {{session['user_name']}}! </h3>

Related

user authentification failure despite successful login

The below login solution is not successfully authenticating the user.
After the login_user(user) function is called, the current_user is supposed to be authenticated.
When trying: print(flask_login.current_user.is_authenticated) immediately after login this returns TRUE, but as soon as the next page loads it returns FALSE.
In summary: The user authentification state is not persistent.
Consequently, pages protected with the #flask_login.login_required decorator (below the #app.route line) are inaccessible and I am redirected to the #login_manager.unauthorized_handler page.
As I understand it, the Flask framework is supposed to set cookies or send some kind of information in the headers that contain some kind of authentication. I also tried using the flask 'sessions' module and also tried using the 'g' module, but these produced entirely different errors.
The below version of the code I plagiarised from https://github.com/maxcountryman/flask-login, which uses only the things I have below. Yet for some reason, my version isn't working.
For the purposes of this testing phase, I made a list containing python dicts of a user so I don't have to make multiple unnecessary calls to the Firebase db I'm using to store the info.
This is the setup section for the login manager including my copy of the UserMixin class:
app.secret_key = 'dummysecretkey'
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
class User(flask_login.UserMixin):
pass
This is a sample of the structure of the users:
{
'email': 'email#domain.com',
'password': 'password',
'username': 'user01'
...*other fields*...
}
Here are the routes for the login manager:
#login_manager.user_loader
def user_loader(username):
for entries in users:
if entries['username'] not in users:
return
for entries in users:
if entries['username'] == username:
user = User()
user.id = entries['username']
return user
#login_manager.request_loader
def request_loader(request):
username = request.form.get('username')
try:
for entries in users:
if entries['username'] == username:
user = User()
user.id = entries['username']
user.is_authenticated = request.form['password'] == entries['password']
return user
except:
return None
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
uname = request.form['uname']
psw = request.form['psw']
for entries in users:
if uname == entries['username'] and psw == entries['password']:
user = User()
user.id = uname
flask_login.login_user(user)
print(flask_login.current_user.is_authenticated)
return redirect(url_for('addnew'))
return 'Bad login'
Example route that requires authentication:
#app.route('/addnew')
#flask_login.login_required
def addnew():
return render_template("addnew.html")
Notwithstanding the flaws in the password comparison (I'm aware they're insecure), could someone please point out the flaw in the above code or present an alternative to achieve the login authentication?
I am bit confused by your code.
From the Flask Login documentation:
Sometimes you want to login users without using cookies, such as using header values or an api key passed as a query argument. In these cases, you should use the request_loader callback.
But I understand you want to use cookie based login?
So you should not use the request_loader.
Also, why do you instantiate a new User in the login route? The login should compare the login data with already existing users.
I suggest you "manually" instantiate the users and put them in a list like this:
users = []
a = User()
a.name = "name_a"
a.id = "id_a"
users.append(a)
...
And then adapt your user_loader to compare the login data from the form with the existing data.
There is an excellent Flask tutorial out there by Miguel Grinberg, which also explains the user login:
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

Update global dictionary/list from within an #app.route in Flask

I'm trying to find a way to update a dictionary (global) from within a function in a flask app.
I want to store information about users that create an account in a dictionary like this.
user {
'key': {'key1':'value1', 'key2':'value2'}
}
But I can't seem to be able to update the dict or list from within the function.
I've tried both
user['key']={'key1':'value1', 'key2':'value2'}
and
user.update({'key': {'key1':'value1', 'key2':'value2'}
methods of updating but it's not working.
This is the full code of this portion.
channel_list = []
users = {}
#app.route("/", methods=["GET", "POST"])
def index():
username = request.form.get("username")
firstname = request.form.get("firstname")
lastname = request.form.get("lastname")
if request.method == "GET":
if session.get("user_name"):
user_name = session.get("user_name")
get_user = users[user_name]
render_template("profile.html", user_name=get_user, users=users)
else:
return render_template("index.html")
if request.method == "POST":
session["user_name"] = username
newuser = {username: {"username": username, "firstname": firstname, "lastname": lastname}}
users.update(newuser)
get_user = users[username]
return render_template("profile.html", user=get_user, users=users)
#app.route("/signin", methods=["GET", "POST"])
def signin():
username = request.form.get("username")
if request.method == "GET":
if session.get("user_name"):
user_name = session.get("user_name")
get_user = users[user_name]
render_template("profile.html", user=get_user, users=users)
else:
return render_template("signin.html")
if request.method == "POST":
session["user_name"] = username
get_user = users[username]
return render_template("profile.html", user=get_user, users=users)
return render_template("signin.html")
I've figured out the problem from trying this in pure python and there, I'm also not able to update the dict without first running the function. But, how do I run each individual function (ex, index() or signin()) in flask, because I think this is the problem? I think this should be taken care of by app.run() but it's not working.
if __name__ == "__main__":
app.run()
socketio.run(app)
I keep getting "KeyError" each time because nothing is being inserted in the dict so there's nothing to select or access.
I'm trying to avoid using a DB and I really want to figure this out, I don't know what I'm doing wrong. The code works really well outside the flask app which is why it's frustrating.
The key error is being generated here due to this line:
if request.method == "GET":
...
get_user = users[user_name]
This is because when you start your Flask application, initially the 'users' dictionary is empty. So when you access either of the two routes for the first time using a 'GET' method (which is the default method when you type the URL into a browser), this line here would generate a KeyError as the dictionary is empty and the key "user_name" does not exist. A common way to fix this would be to use a try-except block and handle the Exception.
Also, both of the two functions seem to be doing the same thing, i.e. signing the user in. It would be better if you distinguished the two functions properly. I would recommend keeping all the sign-in logic in the 'signin' route and using the 'index' route to simply display the login page. You should also set the methods of the two routes properly. Only the 'signin' function is supposed to be accessed via a POST method.

Flask throwing "Could not build url for endpoint 'index'" after adding a decorator

I've a custom app written with flask and I'm trying to add an authentication decorator (d_auth), so that I don't have to check whether the user has authenticated or not within each routing function. The decorator works fine, but the problem is that url_for("index") fails after the user signs in. Here is my code for the decorator and the index routing function where I've added that decorator:
def d_auth(func):
wraps(func)
def decorated(*ags, **kgs):
#print("DECORATOR_RUNNING")
login_valid = (flask.session.get('auth_email') != None)
if not login_valid:
return redirect(url_for("login"))
else:
#func(*args, **kwargs)
return func(*ags, *kgs)
#pass
return decorated
#app.route("/", methods=["GET", "POST"])
#d_auth
def index():
creds = gdrive.get_gdrive_credentials(session['auth_user_id'])
if not creds:
info = "<h2 id='lblGAuthStatus' class='text-center text-danger'> <span class='glyphicon glyphicon-alert'></span> NOT Authenticated. <a href='/gdrive_auth'>Click here to Authenticate.</a></h2>"
elif creds.access_token_expired:
info = "<h2 id='lblGAuthStatus' class='text-center text-danger'> <span class='glyphicon glyphicon-alert'></span> Access Token EXPIRED. <a href='/gdrive_auth'>Click here to Authenticate.</a></h2>"
else:
info = "<h2 id='lblGAuthStatus' class='text-center text-success'> <span class='glyphicon glyphicon-ok'></span> Successfully Authenticated.</h2>"
return render_template('static/index.html', info=info)
What the decorator basically does is check whether the user has logged-in or not (not login_valid) and redirects them to login page if they haven't. This works perfectly. Problem is that once the user logs in and the login page tries to redirect them to the index page back again, it throws this error:
werkzeug.routing.BuildError: Could not build url for endpoint 'index'. Did you mean 'view' instead?
Here is the code for the /login route:
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == 'GET':
return render_template("static/login.html")
elif request.method == 'POST':
email = request.form['email']
password = request.form['password']
conn, cursor = db.opendb()
cursor.execute("select id, is_admin, first_name, last_name from user where email=? and password=?", (email, password))
row = cursor.fetchone()
if row == None:
return render_template("static/login.html", error="Invalid Credentials")
else:
session['auth_user_id'] = str(row['id'])
session['auth_email'] = email
session['auth_first_name'] = row['first_name']
session['auth_last_name'] = row['last_name']
session['auth_is_admin'] = row['is_admin']
return redirect(url_for("index"))
On that last line, url_for("index") is being called and that's where the error is coming. I know I can workaround with url_for("/") instead which is working, but I want to fix this permanently so that something else may not stop working in my relatively large code-base.
I just found an answer to my question here. Turns out that I've to wrap a decorator function using the #wraps(func), not simply wraps(func) like I'd done. Wonder why it didn't throw an error!

session.pop() does not clear cookies

I am new to Flask framework and am playing around with it to learn it better. I am following this tutorial along my way.
As per the User Authentication tutorial in the series, I am stuck with the below:
In the tutorial, when the user logs out by hitting the /logout route, the first thing that happens is :
session.pop('logged_in', None)
Now as per the video mentioned above, the moment user hits the /logout route the cookie also gets deleted from the browser.
Now 2 questions here:
In my case, with the exact same setup as the tutorial, although the session might be getting invalidated from the server end, the cookie does NOT get deleted/changed in any way from the browser after the /logout route is hit. Is there something wrong that am doing?
session.pop(...) => how/why exactly will it delete something from the front end, the browser. It can only control things on the server, isn't it ?
For your reference below is my code (taken from the tutorial itself)
# import the Flask class from the flask module
from flask import Flask, render_template, redirect, url_for, request, session, flash
# create the application object
app = Flask(__name__)
app.secret_key = 'my precious'
# use decorators to link the function to a url
#app.route('/')
def home():
return "Hello, World!" # return a string
#return render_template(index.html)
#app.route('/welcome')
def welcome():
return render_template('welcome.html') # render a template
# route for handling the login page logic
#app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or request.form['password'] != 'admin':
error = 'Invalid Credentials. Please try again.'
else:
session['logged_in'] = True
flash('You were just logged in')
return redirect(url_for('home'))
return render_template('login.html', error=error)
#app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were just logged out')
return redirect(url_for('welcome'))
# start the server with the 'run()' method
if __name__ == '__main__':
app.run(debug=True)
First of all session and cookie is not the same. Session is more like unique id posted to you browser and something like a key for the dictionary for you backend. So most of the time, when you change session(not session id), you just modify backend part(add or delete values in backend dictionary by that key). Not browser's cookie.
You understood all correct. When you pop "logged in" from session server will remember that this browser will not logged_in any more.
So cookie used here just to identify the client browser. That is it.
You can set the expiry time of cookie to 0, this will make it invalid
#app.route('/logout')
def logout():
session.pop('token', None)
message = 'You were logged out'
resp = app.make_response(render_template('login.html', message=message))
resp.set_cookie('token', expires=0)
return resp
To remove the cookie you want to remove, you need to set the cookie's max-age to 0, and send it back to the user's browser. You can achieve this by doing this:
#app.route('/logout')
def logout():
resp = make_response(render_template('login.html', message="You are logged out."))
resp.delete_cookie('token')
return resp
the delete_cookie method is a shorthand for calling set_cookie with the cookie's name, an empty string for the value, and expires=0. The method sets the max-age of the cookie to 0, which means it is immediately expired.
In some older browsers you may find that they don't handle the Expires attribute correctly, so using delete_cookie might be a safer choice.
It also looks more clean IMHO.

flask redirect not working

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.

Categories