I've made a successfull login to a html page (lets call it page1). Now I need to allow access to a second page (page2) only if the user has previously done the login. I think I need an IF to specify to which page the user is attempting to go: page1 or page2.
This is what I got:
#app.route('/user')
def user():
if "user" in session:
user = session["user"]
return render_template("page1.html")
else:
return redirect(url_for("login"))
This following is wrong, I need that IF:
def user():
if "user" in session:
user = session["user"]
if ... : # user attempting to go to page 1
return render_template("page1.html")
else:
return render_template("page2.html")
else:
return redirect(url_for("login"))
Thanks to all
Edited, to share the login method:
#app.route('/login', methods = ["GET","POST"])
def login():
error = None;
if request.method == "POST":
user = request.form["email"]
with open("users.txt", "r") as file:
file_reader = csv.reader(file)
for row in file_reader:
if row[0] == request.form['email']:
user_found = [row[0],row[1]]
if user_found[1] != request.form['pass']:
error = "wrong pass"
break
else:
flash("logged in")
session["user"]= user
return redirect(url_for('user'))
else:
error = "user not found"
else:
if "user" in session:
return redirect(url_for("user"))
return render_template('login.html',error=error)
a better way to do this is to use flask-login. Views that require your users to be logged in can be decorated with the login_required decorator:
after installing flask-login, put the following in your main file or init.py
...
app = Flask(__name__) # constructs the Flask app
app.config.from_object('app.config.Config') # injects the configuration
db = SQLAlchemy (app) # flask-sqlalchemy # connects to SQLite DB
lm = LoginManager( ) # flask-loginmanager
lm.init_app(app) # init the login manager
then create your user model and inherit from user_mixin that provides default implementations for the methods that Flask-Login expects
class User(db.model, user_mixin):
then define the user loader
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
in your login route, after checking the username and password (enc) add the following to login the user:
login_user(user)
now you can decorate your protected views with login_required
#app.route("/page2")
#login_required
def page2():
return render_template("page2.html")
#app.route("/page3")
#login_required
def page3():
return render_template("page3.html")
You can implement a wrapper function to check if a user is logged in. In that function utilize the global g variable.
from flask import g
from functools import wraps
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if not "user" in session:
return redirect(url_for('login'))
g.user = session["user"]
return f(**args, **kwargs)
return login_required
#app.route('/user')
#login_required
def user():
return render_template("page1.html")
For a full example take a look at the View Decorators portion of the Flask documentation
Related
I have a Python code with using framework Flask, that check if admin logged (logged = True) to render admin page, if admin is not logged (logged = False), redirecting to login page.
#app.route('/admin_login', methods=['POST', 'GET'])
def admin_login():
if request.method == 'POST':
login = request.form['login']
passsword = request.form['password']
if (login == 'admin') and (passsword == 'admin_pass'):
logged = True
return redirect('/admin_page'), logged
else:
return "Wrond login and passsword!"
else:
return render_template('admin_login.html')
#app.route('/admin_page')
def admin_page():
if logged == True:
return render_template('admin_page.html')
else:
return redirect('/admin_login')
But I get an error in if logged == True: - NameError: name 'logged' is not defined. I tried to make logged global but it didn't helped. So how can I make logged defined and use it in function admin_page?
You should avoid having a global logged_in variable on the server. Then anybody will be allowed to use your website after a successful login! You should use a session variable instead.
Session data is stored on top of cookies and encrypted. For this encryption, a Flask application needs a defined SECRET_KEY. A Session object is also a dictionary object containing key-value pairs.
Add this near the top of your main script if you haven't already got it:
from flask import Flask, session, redirect, url_for, escape, request, flash
app = Flask(__name__)
app.secret_key = 'any random string’
Then change your function admin_login() to set the session variable:
#app.route('/admin_login', methods=['POST', 'GET'])
def admin_login():
if request.method == 'POST':
login = request.form['login']
passsword = request.form['password']
if not 'logged_in' in session:
if (login == 'admin') and (passsword == 'admin_pass'):
session['logged_in'] = True
return redirect(url_for("admin_page"))
else:
flash("Wrong login and passsword!")
return render_template('admin_login.html')
else:
return redirect(url_for("admin_page"))
else:
if "logged_in" in session:
return redirect(url_for("admin_page"))
return render_template("admin_login.html")
Then change your admin_page() function to check this session variable:
#app.route('/admin_page')
def admin_page():
if 'logged_in' in session:
return render_template('admin_page.html')
else:
return redirect(url_for('admin_login'))
You would also need a logout end point to pop out the session variable:
#app.route('/logout')
def logout():
session.pop('logged_in', None)
return redirect(url_for('index'))
When using Flask Login (for the first time) and following every tutorial example I came across, after correctly POSTing credentials, calling login_user(), and redirecting to a protected login_required route, I'm still getting a 401 Unauthorised response. Only when I explicitly set the is_authorised property to True does it work. But this seems like it should be unnecessary, and no tutorial example I can find does this. What am I overlooking? Sample code below. Thanks!
from flask import Flask, render_template, url_for, jsonify, session, redirect, request
from flask_login import LoginManager, login_required, login_user, current_user
login_manager = LoginManager()
app = Flask(__name__)
app.secret_key = b'_5#y2L"F4Q8z\n\xecgwefweligli]/'
login_manager.init_app(app)
class User:
_users = {
'me': 'mypass'
}
_active_users = {}
def __init__(self, user_id):
self.user_id = user_id
self.password = self._users[user_id]
self._authenticated = False
self._active = True
#property
def is_active(self):
return self._active
#property
def is_authenticated(self):
return self._authenticated
#is_authenticated.setter
def is_authenticated(self, value):
if value:
self._authenticated = True
else:
self._authenticated = False
#property
def is_anonymous(self):
return False
def get(user_id):
if user_id in User._active_users:
return User._active_users[user_id]
if user_id in User._users:
User._active_users[user_id] = User(user_id)
return User._active_users[user_id]
return None
def get_id(self):
return self.user_id
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
u = User.get(request.form['username'])
app.logger.debug(u)
if u and u.password == request.form['password']:
app.logger.debug('authenticated')
r = login_user(u)
u.is_authenticated = True # HERE: this shouldn't be necessary?
app.logger.debug(u.is_authenticated)
app.logger.debug(current_user.is_authenticated)
return redirect(url_for('index'))
return redirect(url_for('login'))
return render_template('login.html')
#app.route('/')
#login_required
def index():
return render_template('main.html')
When I remove:
u.is_authenticated = True
the user is not authenticated. I've looked at the source for login_user() and it doesn't seem to set is_authenticated. Am I doing it right? I'm just confused because no documentation / tutorial includes the above line, but all claim to just work.
Your original code needed to explicitly set is_authenticated because from one request to the next, a new instance of the User object is created each time, so the information that the user already authenticated is lost.
To avoid the loss of this information we need a place to store it. While Flask-login helpfully provides a UserMixin class that provides default implementations of this, you can just add authenticated field in the User model:
authenticated = db.Column(db.Boolean, default=False)
When the user login, you should change authenticated to true and save change in database and When the user logout, you should change authenticated to false and save change in database.
The is_authenticated function will be similar to this:
#property
def is_authenticated(self):
return self.authenticated
Please find this tutorial.
I have to design a web-app that provides Flask services and Dash services. For example I would like to create a login in Flask, combined with a Dash application. The problem is that I can't bind the flask login with dash. I would need a method like '#require_login' that filters access to even Dash services.
The code is as follows:
app_flask = Flask(__name__)
app_flask.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////login.db'
app_flask.config['SECRET_KEY'] = 'thisissecret'
db = SQLAlchemy(app_flask)
login_manager = LoginManager()
login_manager.init_app(app_flask)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(30), unique=True)
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
#app_flask.route('/')
def index():
user = User.query.filter_by(username='admin').first()
login_user(user)
return 'You are now logged in!'
#app_flask.route('/logout')
#login_required
def logout():
logout_user()
return 'You are now logged out!'
#app_flask.route('/home')
#login_required
def home():
return 'The current FLASK user is ' + current_user.username
# TODO how to add login_required for dash?
app_dash = Dash(server=app_flask, url_base_pathname='/dash/')
app_dash.layout = html.H1('MY DASH APP')
if __name__ == '__main__':
app_dash.run_server(debug=True)
This line, app_dash = Dash(server=app_flask, url_base_pathname='/dash/'), creates new view_functions in app_flask identified by its url_base_pathname.
You can debug and inspect the value of app_flask.view_functions before and after the creation of app_dash.
Now that we know which view_functions are created by app_dash, we can apply login_required to them manually.
for view_func in app_flask.view_functions:
if view_func.startswith(app_dash.url_base_pathname):
app_flask.view_functions[view_func] = login_required(app_flask.view_functions[view_func])
The `app_dash` endpoints will now be protected.
It would be better if you block all requests by using #app.before_request, and only allow the request if logged in or if the endpoint is marked as public.
def check_route_access():
if request.endpoint is None:
return redirect("/login")
func = app.view_functions[request.endpoint]
if (getattr(func, "is_public", False)):
return # Access granted
# check if user is logged in (using session variable)
user = session.get("user", None)
if not user:
redirect("/login")
else:
return # Access granted```
Now all endpoints will be checked, even dash app endpoints.
Add this decorator named public_route:
def public_route(function):
function.is_public = True
return function
And add the decorator to the public methods, like login, error pages etc.
#public_route
#app.route("/login")
def login():
# show/handle login page
# call did_login(username) when somebody successfully logged in
def did_login(username):
session["user"] = username
This way you never need the #login_required anymore because all endpoints require login unless stated otherwise by #public_route.
A solution : session from flask (work with cookie)
from flask import session
it's an exemple :
#login_manager.user_loader
def load_user(user_id):
# I think here it's good
session["uid"] = user_id
return User.query.get(int(user_id))
# TODO how to add login_required for dash?
if "uid" in session :
app_dash = Dash(server=app_flask, url_base_pathname='/dash/')
app_dash.layout = html.H1('MY DASH APP')
I want to build a simple webapp as part of my learning activity. Webapp is supposed to ask for user to input their email_id if it encounters a first time visitor else it remembers the user through cookie and automatically logs him/her in to carry out the functions.
This is my first time with creating a user based web app. I have a blue print in my mind but I am unable to figure out how to implement it. Primarily I am confused with respect to the way of collecting user cookie. I have looked into various tutorials and flask_login but I think what I want to implement is much simpler as compared to what flask_login is implementing.
I also tried using flask.session but it was a bit difficult to understand and I ended up with a flawed implementation.
Here is what I have so far (it is rudimentary and meant to communicate my use case):
from flask import render_template, request, redirect, url_for
#app.route("/", methods= ["GET"])
def first_page():
cookie = response.headers['cookie']
if database.lookup(cookie):
user = database.get(cookie) # it returns user_email related to that cookie id
else:
return redirect_url(url_for('login'))
data = generateSomeData() # some function
return redirect(url_for('do_that'), user_id, data, stats)
#app.route('/do_that', methods =['GET'])
def do_that(user_id):
return render_template('interface.html', user_id, stats,data) # it uses Jinja template
#app.route('/submit', methods =["GET"])
def submit():
# i want to get all the information here
user_id = request.form['user_id']# some data
answer = request.form['answer'] # some response to be recorded
data = request.form['data'] # same data that I passed in do_that to keep
database.update(data,answer,user_id)
return redirect(url_for('/do_that'))
#app.route('/login', methods=['GET'])
def login():
return render_template('login.html')
#app.route('/loggedIn', methods =['GET'])
def loggedIn():
cookie = response.headers['cookie']
user_email = response.form['user_email']
database.insert(cookie, user_email)
return redirect(url_for('first_page'))
You can access request cookies through the request.cookies dictionary and set cookies by using either make_response or just storing the result of calling render_template in a variable and then calling set_cookie on the response object:
#app.route("/")
def home():
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return render_template('welcome.html', user=user)
else:
return redirect(url_for('login'))
else:
return redirect(url_for('login'))
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
# You should really validate that these fields
# are provided, rather than displaying an ugly
# error message, but for the sake of a simple
# example we'll just assume they are provided
user_name = request.form["name"]
password = request.form["password"]
user = db.find_by_name_and_password(user_name, password)
if not user:
# Again, throwing an error is not a user-friendly
# way of handling this, but this is just an example
raise ValueError("Invalid username or password supplied")
# Note we don't *return* the response immediately
response = redirect(url_for("do_that"))
response.set_cookie('YourSessionCookie', user.id)
return response
#app.route("/do-that")
def do_that():
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return render_template('do_that.html', user=user)
else:
return redirect(url_for('login'))
else:
return redirect(url_for('login'))
DRYing up the code
Now, you'll note there is a lot of boilerplate in the home and do_that methods, all related to login. You can avoid that by writing your own decorator (see What is a decorator if you want to learn more about them):
from functools import wraps
from flask import flash
def login_required(function_to_protect):
#wraps(function_to_protect)
def wrapper(*args, **kwargs):
user_id = request.cookies.get('YourSessionCookie')
if user_id:
user = database.get(user_id)
if user:
# Success!
return function_to_protect(*args, **kwargs)
else:
flash("Session exists, but user does not exist (anymore)")
return redirect(url_for('login'))
else:
flash("Please log in")
return redirect(url_for('login'))
return wrapper
Then your home and do_that methods get much shorter:
# Note that login_required needs to come before app.route
# Because decorators are applied from closest to furthest
# and we don't want to route and then check login status
#app.route("/")
#login_required
def home():
# For bonus points we *could* store the user
# in a thread-local so we don't have to hit
# the database again (and we get rid of *this* boilerplate too).
user = database.get(request.cookies['YourSessionCookie'])
return render_template('welcome.html', user=user)
#app.route("/do-that")
#login_required
def do_that():
user = database.get(request.cookies['YourSessionCookie'])
return render_template('welcome.html', user=user)
Using what's provided
If you don't need your cookie to have a particular name, I would recommend using flask.session as it already has a lot of niceties built into it (it's signed so it can't be tampered with, can be set to be HTTP only, etc.). That DRYs up our login_required decorator even more:
# You have to set the secret key for sessions to work
# Make sure you keep this secret
app.secret_key = 'something simple for now'
from flask import flash, session
def login_required(function_to_protect):
#wraps(function_to_protect)
def wrapper(*args, **kwargs):
user_id = session.get('user_id')
if user_id:
user = database.get(user_id)
if user:
# Success!
return function_to_protect(*args, **kwargs)
else:
flash("Session exists, but user does not exist (anymore)")
return redirect(url_for('login'))
else:
flash("Please log in")
return redirect(url_for('login'))
And then your individual methods can get the user via:
user = database.get(session['user_id'])
im new to flask and flask-login and ive been struggling with this for days.
Im trying to log a user in like this:
from creds import auth_username, auth_password, pgsql_dbuser, pgsql_dbpassword, pgsql_db1name
from flask import Flask, render_template, request, Response, redirect, url_for
from flask.ext.bcrypt import Bcrypt
from flask.ext.login import LoginManager, login_required, login_user, current_user, logout_user
import logging
import psycopg2
import uuid
import datetime
app = Flask(__name__)
app.secret_key = str(uuid.uuid4()) # <- required by login_manager.init_app(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'index'
#app.route('/', methods=['GET','POST'])
def index():
page_name = '/'
if request.method == 'POST':
email = request.form['email']
candidate_password = request.form['password']
user = finduserindbbyemail(email)
if user != None:
password_hash = checkuserpasswordindb(email)
if bcrypt.check_password_hash(password_hash, candidate_password):
user_object = User(user)
result = login_user(user_object) # <- here for successful login
return redirect(url_for('loggedin', user_object=type(user_object), user=user, result=result, current_user=current_user))
else:
user_object = User(user)
error_message = "The password you entered is incorrect"
return render_template('index.html', error_message=error_message)
else:
error_message = "The email address you entered does not match any we have in our records"
return render_template('index.html', error_message=error_message)
if request.method == 'GET':
return render_template('index.html')
I have a User class and a user callback:
class User():
def __init__(self, user):
self.user = user
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return unicode(self.user)
#login_manager.user_loader
def load_user(user):
con = psycopg2.connect(database=pgsql_db1name, user=pgsql_dbuser, password=pgsql_dbpassword, host='localhost')
uuid = "'"+user+"'"
cur = con.cursor()
cur.execute("SELECT uuid FROM users WHERE uuid = "+ uuid)
uuid = cur.fetchone()
con.close()
if uuid != None:
user = unicode(uuid[0])
return User.get_id(user)
else:
return None
After authentication is successful (apparently?), the user is redirected to a loggedin page which has a #login_required decorator. But instead of loading the loggedin page, the app redirects the user to the login page, telling me the user isnt being logged in?
If try to send values to the page and i remove the #login_required decorator so i can see the page, this is what i see in the browser after 'logging in':
current_user.is_authenticated() = False
current_user.is_active() = False
current_user.is_anonymous() = True
current_user.get_id() = None
user_object = <type 'instance'>
user = 2ca1296c-374d-43b4-bb7b-94b8c8fe7e44
login_user = True
current_user = <flask_login.AnonymousUserMixin object at 0x7f2aec80f190> Logout
It looks like my user hasn't been logged and is being treated as anonymous?
Can anyone see what I've done wrong? I'm having a lot of trouble understanding how this is supposed to work.
Another reason you might not be able to log a user in or current_user is Anonymous after going through your login form: The active=false flag is set on the user in the db. This behavior is confirmed in the docs:
flask_login.login_user(user, remember=False, duration=None, force=False, fresh=True)[source]
Logs a user in. You should pass the actual user object to this. If the user’s is_active property is False, they will not be logged in unless force is True.
This will return True if the log in attempt succeeds, and False if it fails (i.e. because the user is inactive).
So, when you call login_user, you can do this:
login_user(user, remember=form.remember_me.data, force=True), if you want to allow inactive users to log in.
So.. I managed to get it to work, but not using the user_loader callback. For whatever reason, my user loader exhibits the same behaviour as this:
Flask-login with static user always yielding 401- Unauthorized
Anyway, I used a request_loader callback instead based on this example:
http://gouthamanbalaraman.com/blog/minimal-flask-login-example.html
so for a user logging in, which starts here:
if bcrypt.check_password_hash(password_hash, candidate_password):
user_object = User(user, password_hash)
result = login_user(user_object) # <- here for successful login
token = user_object.get_auth_token(user, password_hash)
return redirect(url_for('loggedin', token=token))
I create a user object which has the user's id and their password hash.
then i log the user in. then i create a time-serialized token of the user id and password hash using itsdangerous. the get_auth_token function is part of the User class. it looks like this:
class User():
def __init__(self, user, password_hash):
self.user = user
self.password = password_hash
.
.
.
def get_auth_token(self, user, password):
data = [str(self.user), self.password]
return serializer.dumps(data, salt=serializer_secret)
you need to create a serializer at the beginning of your code somewhere:
serializer = URLSafeTimedSerializer(serializer_secret)
So after the token is created, pass it to the loggedin view as a URL query parameter.
When you try to load a login_required page, like my loggedin page, which is where login_user redirects me to after a successful login, the request_loader callback is executed. it looks like this:
#login_manager.request_loader
def load_user_from_request(request):
if request.args.get('token'):
token = request.args.get('token')
max_age = 1
try:
data = serializer.loads(token, salt=serializer_secret, max_age=max_age)
username = data[0]
password_hash = data[1]
found_user = finduserindbbyuuid(username)
found_password = checkuserpasswordindbbyuuid(username)
if found_user and found_password == password_hash:
user_object = User(found_user, password_hash)
if (user_object.password == password_hash):
return user_object
else:
return None
else:
return None
except BadSignature, e:
pass
else:
return None
This is the point where my user_loader was failing. I was logging in successfully, but the user_loader was always returning None and so my user would be deemed as anonymous.
So with the request loader, it checks that the request URL contains a 'token' argument in the query string. if so, it takes its value and using itsdangerous, deserializes the data.
you can make the token expire with timed serializers, but there are also non timed ones. after the token is deserialized, take the user and password hash and check in the database if they exist, in exactly the same way that the user_loader was supposed to work.. i imagine? my user_loader didnt work so i was most probably doing it wrong.
anyway if a user and password match in the db, then return the user object and bam, login works.
Im not sure if im doing it the right way, cos pretty much flying by the seat of my pants. i saw examples where people used the token_loader, rather than the request_loader callback function, to load the token, but i couldnt figure out how to set & get the auth token to & from the client. maybe ill figure it out... one day...
if you have the same problem, maybe this might help? or just let me know what you think
cheers
I found this page when searching for help with Flask-Login + Flask-Dance. I was seeing current_user as AnonymousUserMixin in a handler with the #login_required decorator. In my case making sure #app.route is on the line above #login_required fixed the problem. The correct order is in the docs: https://flask-login.readthedocs.io/en/latest/#flask_login.login_required.