As Flask API documentation says, I need to add
session.modified = True
after my code in order to propagate session changes to mutable structures like dict/list.
As I understand, session object won't see any changes and won't add modified session data to cookies.
Example#1 from docs:
session['objects'].append(42)
# so mark it as modified yourself
session.modified = True
Example#2 from google:
# Add a logout handler.
#app.route('/logout')
def logout():
# Delete the user's profile and the credentials stored by oauth2.
del session['profile']
session.modified = True
oauth2.storage.delete()
return redirect(request.referrer or '/')
But I tried Flask 1.0.2 and Flask 0.10.1. Everything also works without session.modified = True between requests. I see changes, cookies are updated.
Is that line redundant now?
Related
I was wondering what I'm doing wrong. I am trying to implement the most simple session with React frontend and Django backend. I am aware that my methods are insecure and bad but its just a project for university and I need something that works so I should do other stuff that require sessions in my project.
This is how my backend looks for Login and SessionInfo:
#api_view(['POST'])
def login(request):
data = request.data
try:
user = MyUser.objects.get(username=data.get('username'),
password=data.get('password'))
request.session['uuid'] = user.id
request.session.modified = True
except MyUser.DoesNotExist:
return HttpResponse("User does not exist.")
return HttpResponse(request.session['uuid'])
#api_view(['GET'])
def getsession(request):
if request.session.has_key('uuid'):
return HttpResponse(request.session['uuid'])
else:
return HttpResponse(False)
When I am trying to test this with Postman it always work and I get wanted session ID but when I'm trying to do same stuff with react using Axios post method it always return False. I have no clue why? It looks like Django destroys session after calling login function or it doesn't even create it.
This is how my post method looks in React:
function login(){
axios.post('http://127.0.0.1:8000/evidencija/login/',{
username: 'admin',
password: 'admin'
}).then(
(response) =>{
console.info(response.data)
getSession()
},
(error) =>{
console.log(error)
}
)
}
Some browsers (Chrome, for example) provide settings that allow users to continue browsing sessions after closing and re-opening the browser. In some cases, this can interfere with the SESSION_EXPIRE_AT_BROWSER_CLOSE setting and prevent sessions from expiring on browser close. Please be aware of this while testing Django applications which have the SESSION_EXPIRE_AT_BROWSER_CLOSE setting enabled.
Documentation: https://docs.djangoproject.com/en/3.2/topics/http/sessions/#browser-length-vs-persistent-sessions
I would like to know how to restrict access to certain pages of my website using sessions. In, summery I'd like the administrator to log in through the same login form as normal users but using the credentials he entered, he'll be redirected to the backend page where he'll have full access to both front-end web pages and back-end web pages. When a user logs in, they can only view front-end web pages. I'm trying to avoid a situation where a user logs in and then changes the URL in the search bar of the web browser from 127.0.0.7/homepage to 127.0.0.7/backend and is allowed access because he's logged in.
I'm using the code below to check if the user is logged in. If they are not then they're redirected to the login page. I'm using flask framework. Any suggestions are greatly appreciated.
Thank you.
app.route('/backend', methods=['POST', 'GET'])
def backend():
if 'userkey' in session:
# connect to database using pymysql
if cursor.rowcount == 0:
return render_template('backend.html', msg="No orders found...")
else:
rows = cursor.fetchall()
return render_template('backend.html', orderdata=rows)
elif 'userkey' not in session:
return redirect('/login')
else:
return redirect('/login')
You must already be setting session['userkey'] in your login view once the user has authenticated, you just need to add the additional information about the users admin status to get this working. It would also be much easier if you moved all of the if 'userkey' in session checks outside of your view function into a decorator so that it can easily be reused to protect all of your view functions. This pattern is shown in the flask documentation under the heading 'Login Required Decorator' http://flask.pocoo.org/docs/1.0/patterns/viewdecorators/.
In your login function you should query your database for the users admin status at the same time as retrieving the userkey and hashed password. Then simply set session["admin"] = True if the user is an admin or don't set this at all if they are not an admin.
The following code would then work for the decorator.
from functools import wraps
from flask import session
def login_required(status=None):
def login_decorator(func):
#wraps(func)
def wrapper(*args, **kwargs):
if 'userkey' in session and (status is None or status in session):
return func(*args, **kwargs)
else:
return redirect("/login")
return wrapper
return login_decorator
This decorator should then be applied to protect all of your view functions. It will then be executed prior to each view function either allowing the view to proceed if all the conditions are met or redirecting the user back to the login page if not. It takes an optional single argument status which in your case should be 'admin' for all of the back end views and should not be supplied for the front end views that do not require admin privileges. The functools.wraps decorator inside the function is just there to update the metadata of the wrapped function so that attributes like function.__name__ behave as expected. The 'userkey' in session check is the same as in your original code but we additionally check if a status was supplied and if so if it is also stored in the session. In your case the only value of status would be 'admin' but this pattern could easily be expanded to allow other groups that would each have access to different parts of the website.
Give that all the user checks are carried out in the decorator your backend view function can be simplified to
#app.route('/backend', methods=['POST', 'GET'])
#login_required("admin")
def backend():
# connect to database using pymysql
if cursor.rowcount == 0:
return render_template('backend.html', msg="No orders found...")
else:
rows = cursor.fetchall()
return render_template('backend.html', orderdata=rows)
The same decorator can be used to protect all of your front end view functions, just omit the "admin" argument.
#app.route('/frontend', methods=['POST', 'GET'])
#login_required()
def frontend():
# .....
I work on my simple blogging system written in Python, Flask and SQLite,
and I've created a simple authorization system for it. There is no need for anything fancy, so it's just a matter of sending login and password through a form and setting a flag in Flask's session. I wanted to know how things like this are done, so I didn't use any modules.
I'm wondering if this method is correct and secure just enough.
# from auth module
#auth.route('/login', methods=['POST'])
def login():
"""Login as blog admin."""
# Successeful login conditions
user_is_valid = request.form['user'] == current_app.config['USER']
password_is_valid = request.form['password'] == current_app.config['PASSWORD']
trap_is_empty = not request.form['trap']
# Login user if credentials are correct
if user_is_valid and password_is_valid and trap_is_empty:
session['is_admin'] = True
return redirect(url_for('admin.posts_page'))
else:
return render_template('auth.html')
# from admin module
#admin.before_request
def before_request():
""" Before any request check admin flag, redirect to the main page if there is none. """
if not session.get('is_admin'):
return redirect(url_for('blog.index_page'))
proj.db.connect()
It honestly looks fine for just a basic authentication system. The bad part is storing the credentials in the config.
If you want to get all cool and fancy, you can use itsdangerous to generate hashes and salts of passwords and store them in your sqlite database.
Typically, you'd have a table with id, username, password, and a boolean flag like "is_admin" or something that you can check when you authenticate.
So, it's fine for some playing around, but I wouldn't recommend anything like this in production.
I have a Flask application that uses an ajax request, and this request should give a true/false response. The user enters an authorization code, and if the authorization code matches what is needed or is already in the session, then the response should be true and the code added to the session and saved. if it's not, then simply return false
#app.route('/auth-ajax', methods=['POST'])
def auth():
result_id = request.form.get('result_id', 'true')
result = load_result(result_id)
if result:
auth = result['auth_hash']
auth_input = request.form.get('auth_input', '')
if (session.get('auths').get(result_id) != auth and auth_input != auth):
return 'false'
#else, save new authorization into session
session['auths'][result_id] = auth
# return true
return 'true'
However, the session isn't saving as I'd hope. This is my first Python app, and so I'm learning as I go. From what I understand, I need to create a response with Flask and not just simply output "true" or "false" - creating a response save the session as it doesn't save on modification. The only response functions I've used is render_template() for views, but I'm not wanting a view but rather a simple true/false (possibly HTTP status responses) to see if authorization was granted. How would I go about doing this?
Found the problem. I simply has to add session.modified = True. So simple.
I am using flask-login https://github.com/maxcountryman/flask-login and the field remember in login_user does not seem to work.
The session gets destroyed after every restart of the apache ..ideally the remember field should take care of this.. even the session values gets destroyed. this is really frustrating... anyone knowing the solution please ping .. thanks
i am using login_user as
login_user(user, remember=True)
If anyone is suffering with this problem, you have to write the function user_loader properly.
#login_manager.user_loader
def load_user(id):
return "get the user properly and create the usermixin object"
I ran into this issue, but it was because we were setting Flask.secret_key to a new GUID on startup. We moved this to a configuration file (unique ID per environment) and now the session is persisted.
you have to set the get_auth_token in the user mixen as well as the user_loader
class User(UserMixin):
def get_auth_token(self):
"""
Encode a secure token for cookie
"""
data = [str(self.id), self.password]
return login_serializer.dumps(data)
And
#login_manager.token_loader
def load_token(token):
"""
Flask-Login token_loader callback.
The token_loader function asks this function to take the token that was
stored on the users computer process it to check if its valid and then
return a User Object if its valid or None if its not valid.
"""
#The Token itself was generated by User.get_auth_token. So it is up to
#us to known the format of the token data itself.
#The Token was encrypted using itsdangerous.URLSafeTimedSerializer which
#allows us to have a max_age on the token itself. When the cookie is stored
#on the users computer it also has a exipry date, but could be changed by
#the user, so this feature allows us to enforce the exipry date of the token
#server side and not rely on the users cookie to exipre.
max_age = app.config["REMEMBER_COOKIE_DURATION"].total_seconds()
#Decrypt the Security Token, data = [username, hashpass]
data = login_serializer.loads(token, max_age=max_age)
#Find the User
user = User.get(data[0])
#Check Password and return user or None
if user and data[1] == user.password:
return user
return None
Both of those methods use the module itsdangerous to encrypt the remember me cookie
from itsdangerous import URLSafeTimedSerializer
I wrote a blog post about how I did it
Flask-Login Auth Tokens