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.
Related
I have a django rest framework application with custom authentication scheme implemented. Now I want to allow external app call some methods of my application.
There's an endpoint for external app to login /external-app-login which implemented like this:
class ExternalAppLoginView(views.APIView):
def post(self, request):
if request.data.get('username') == EXTERNAL_APP_USER_NAME and request.data.get('password') == EXTERNAL_APP_PASSWORD:
user = models.User.objects.get(username=username)
login(request, user)
return http.HttpResponse(status=200)
return http.HttpResponse(status=401)
Now I want to add authentication. I implemented it like this:
class ExternalAppAuthentication(authentication.SessionAuthentication):
def authenticate(self, request):
return super().authenticate(request)
But authentication fails all the time. What is the correct way to do it? I want to store login/password of external app in variables in application, not in database.
The authentication fails, because it needs to return a registered user in your database to authenticate. However as the user info is all in variables instead of database, the issue arises.
There are more than one ways to overcome this issue. Firstly i would suggest you write the authentication code instead of using
return super().authenticate(request) as this would lead you to the real reason of the issue.
Also must give a read to this documentation link, it clears a lot of things regarding authentication.
https://www.django-rest-framework.org/api-guide/authentication/
Now after you have done all that, and you seek ways how to authenticate, then you can try either remote user authentication, or you can check for existing users in your variables and use anonymous user for authentication which resolves the issue.
I am currently using flask-login in my application for user login session management. I am using flask-sqlalchemy, so all working ok.
Because of my previous sql experience, I am not fan of ORM. I like to use SQL directly. May be it is because of my not sound knowledge in ORM. Anyways, my question is- is there any way I can use flask-login without ORM/flask-sqlalchemy, where I am getting user data by pymysql or MySQL-connector?
I know I can create sessions myself and pop when I logout, but I want to know if there is any way to use flask-login or any other session management library with pymysql?
I went with the following solution. It allows to redefine getting user from request Authorization header and use in stateless application.
from flask_login import login_required, LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
#login_manager.request_loader
def load_user_from_request(request):
api_key: str = request.headers.get('Authorization')
if api_key:
token_type, token = api_key.split()
print("token_type:" + token_type)
if token_type == "JWT" and token:
decoded_token: dict = decode_token(token)
username = decoded_token['identity']
user: User = get_user(username)
return user
return None
So if your endpoint is allowed to be used only by authorized users, #jwt_required or #login_required annotations can be used with method.
Be sure that route annotation is the first among others to guarantee correct work of other annotations; your endpoint can be look like:
#app.route('/api/users', methods=['GET'])
#login_required
#roles_required(['Admin']) # optional
def list_users():
return list[] # your code here
Here is another one annotation mentioned, #roles_required, it is not obligatory of course. Since Flask user management requires you to use ORM, you can overcome that by defining your own #roles_required annotation, see example here.
I'm trying to implement a really simple webpage for internal monitoring. It is supposed to display some data, which is updated in real-time via socketio. The server runs a thread in the background, which fetches the data and relays it to the client.
I'd like to protect the page with a login form. To keep things simple, I chose HTTP Basic Auth, mostly because I didn't want to design a login form.
I have done the following:
Under #login_manager.request_handler, I check for request.authorization. If it is valid, I return an authenticated User object.
Under #login_manager.unauthorized_handler, I trigger the authentication dialog.
The '/' page is protected with #login_required.
I also intercept the socketio.on('connect') event and I check for current_user there. If it is not authenticated, I drop the connection.
Here's the entire working example:
## Standard imports, disregard them
import functools
import gevent
## Otherwise I'm getting KeyError on shutdown
import gevent.monkey
gevent.monkey.patch_all()
from flask import Flask, request, Response
from flask.ext.login import LoginManager, UserMixin, login_required, current_user
from flask.ext.socketio import SocketIO
## To see the logging.debug call in socketio.on('connect')
import logging
logging.getLogger().setLevel(logging.DEBUG)
## App configuration
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'a long and random string'
login_manager = LoginManager()
login_manager.init_app(app)
socketio = SocketIO(app)
## This thing sends updates to the client
class BackgroundThread(gevent.Greenlet):
def run(self):
while True:
socketio.emit(
'my event',
{'my field': 'my data'},
namespace='/my-namespace'
)
gevent.sleep(2)
## Not bothering with a database
class User(UserMixin):
users = {
u'1': (u'myname', u'mypass')
}
def __init__(self, username, password):
self.username = username
self.password = password
def get_id(self):
return u'1'
#classmethod
def get_by_username(cls, requested_username):
for username, password in cls.users.itervalues():
if username == requested_username:
return User(username, password)
return None
## From https://flask-socketio.readthedocs.org/en/latest/
def authenticated_only(f):
#functools.wraps(f)
def wrapped(*args, **kwargs):
if not current_user.is_authenticated():
request.namespace.disconnect()
else:
return f(*args, **kwargs)
return wrapped
## The password is checked here
#login_manager.request_loader
def load_request(request):
auth = request.authorization
if auth is not None:
username, password = auth['username'], auth['password']
user = User.get_by_username(username)
if user is not None and user.password == password:
return user
return None
## From http://flask.pocoo.org/snippets/8/
#login_manager.unauthorized_handler
def http_basic_auth():
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
#app.route('/')
#login_required
def index():
return "My page" # in real code this is actually a render_template call
#socketio.on('connect', namespace='/my-namespace')
#authenticated_only
def test_connect():
logging.debug('Client connected: {.username}.'.format(current_user))
if __name__ == '__main__':
thread = BackgroundThread()
thread.start()
socketio.run(app)
Is this setup secure, provided that I use HTTPS with a self-signed certificate?
The Flask-Login docs stress that to actually login the user, I have to explicitly call login_user. I don't do that and yet I can log in. How is that possible?
UPD: In the foreseeable future I am going to be the only user, so mostly I am concerned whether it is possible to intercept and decrypt the traffic, or send data through the Websocket connection without being authenticated.
Is this setup secure, provided that I use HTTPS with a self-signed certificate?
You have the user passwords stored in plain text in your database (I know, you don't have a database yet, but I assume you'll have one eventually?). If your database ever gets hacked, then your users will hate you, specially those who use the same password for their online banking. You should store hashed passwords in your database to protect them from hackers. Look at Flask-Bcrypt or the password hashing functions in Werkzeug.
Using HTTPS is good, but since you are also using WebSocket, you need to evaluate if the data that goes over the socket connection also needs encryption.
A self-signed certificate is not a good idea, since browsers are unable to verify their authenticity so they'll (rightly) advice your users to stay away from your site.
The Flask-Login docs stress that to actually login the user, I have to explicitly call login_user. I don't do that and yet I can log in. How is that possible?
The idea of logging users in is that you don't have to re-authenticate them with every request they send. The login_user just records that the user is logged in to the session. In subsequent requests Flask-Login will find the user in the session, so it will not need to invoke your callback to do the authentication again.
In your case you are using HTTP basic authentication. The browser will send the Authorization header with every request, and since Flask-Login never finds anything in the session, it always calls your callback, which authenticates the user every time. I don't see any problem with this, but if you want to avoid the effort of constantly authenticating the user (specially after you add password hashing, which is CPU intensive), you may want to consider calling the login_user function to make things a bit more efficient.
Update: so you claim you plan to leave the user list written in plain text in the code. This is a really really bad idea. You want to make the effort to make the data going between client and server secure, so you should also take good security practices in how you store your passwords.
The biggest risk I see with having the passwords in the code for a small site of which you are the only user is you exposing the code by mistake. For example, if you want to put your code under version control, you will have a copy of your password there in addition to the copy that runs on the server (one more place from where it can be hacked). If you also make backups of your scripts, it'll be there too.
So do yourself a favor and do not write your password in the code. At the very least, read it from an environment variable at start up.
My company has a Flask application that uses flask-login to handle user logins/logouts and sessions. We use the #login_required decorator to protect views. Our clients log via persistent users stored in a database. However, we want employees of our company to be able to log in to our clients' sites without popping up in their users lists. We verify our own authenticity by CAS login at our own CAS server and checking that the username belongs to our company.
We want the employees to simply be able to login without needing to be persisted in the database but flask-login requires a persisted user model.
Sorry for the troublesome use of words here, but I hope this is understadable.
Every authorization system should check user in some storage by some field and in usual cases return exist or has permisions.
So with flask-login you can implement it with next: user_loader/header_loader/request_loader and UserMixin with user_id.
Every request with login_required call *_loader to get UserMixin. So it can look like next:
#login_manager.request_loader
def loader(request):
identifier = get_identifier_from_request(request)
exist = check_on_cas_server(identifier)
if not exist:
return None
user = UserMixin()
user.id = get_specified_or_random_id(identifier, exist)
return user
Details you can found with https://flask-login.readthedocs.org/en/latest/.
Bit of a strange one and I have my reasons for doing this, I know Django does this out-of-the-box so put that to the side when I ask..... is it possible to create a authenticated session in Django for a user that does not exist in the standard user model. i.e I want a one off login (session) created for access that allows me to use request.tempUser.is_authenticated() Almost like anonymous access, but authenticated! I'm not talking about custom user models here, but I want do want use the standard auth stuff in Django, if thats possible?
This is what I have so far where I have tried request.session.save() but that won't ... log-in the user.
if member.check_password(password):
# Start new session for member???????
request.session.save()
return self.create_response(request, {
'success': True
})
I've done this before, we have a session middleware (we wrote) that looks to see if the current user logged in is a valid user or not.
On the login we do the following
def login(request, username, password):
# not the actual code, but you get the gist
logged_in_user = authenticate(username, password)
request['cur_user'] = logged_in_user.username
If that variable is not set or is not set to a proper username we bounce the user and clear out the session.
This will log in the user, essentially you just have to track that variable in your code to ensure that the session has a valid user attached to it.