Flask authentication with Flask-Security - python

Trying to enable login in flask with flask-security. The following code works fine (I import in __init__.py)
from flask import Blueprint, render_template, request, redirect, url_for
from coursly import app
from coursly.models import *
from flask.ext.security import Security, LoginForm, SQLAlchemyUserDatastore
user_datastore = SQLAlchemyUserDatastore(db, user.User, user.Role)
Security(app, user_datastore)
user = Blueprint('user', __name__)
#user.route('/')
def index():
return redirect(url_for("user.login"))
#user.route('/login', methods=['GET', 'POST'])
def login():
return render_template('user/login.html', content='Login Page', action=url_for('auth.authenticate'), form=LoginForm())
If, however, I change user = Blueprint('user', __name__) to user = Blueprint('user', __name__, url_prefix='/blah') it fails with a werkzeug.routing.BuildError BuildError: ('auth.authenticate', {}, None). url_for is the problem, however, I don't understand why. HALP!

Figured it out. Turns out Flask-security already has /login /logout built in and my login function was largely ignored. It wasn't used during normal app execution. Except url_for was still processed hence the BuildError.
I was trying to use an outdated example from https://github.com/dracule/FlaskBootstrapSecurity and it was a fail. Flask-Security was updated 3 mo ago. The action param should be updated to action=url_for('security.login') and it will work.
TL;DR adding a url_prefix will not use Flask-Security's built in /login /logout and a lot of vars were renamed

I know this is an old question but just in case someone has this problem as I had, here's the related documentation

Related

How to identify if Google OAuth was authorized or rejected in Flask-Dance framework

Sorry, new to Flask-Dance.
I'm using the template code from:
https://github.com/singingwolfboy/flask-dance-google/blob/master/google.py
import os
from flask import Flask, redirect, url_for
from flask_dance.contrib.google import make_google_blueprint, google
app = Flask(__name__)
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "supersekrit")
app.config["GOOGLE_OAUTH_CLIENT_ID"] = os.environ.get("GOOGLE_OAUTH_CLIENT_ID")
app.config["GOOGLE_OAUTH_CLIENT_SECRET"] = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET")
google_bp = make_google_blueprint(scope=["profile", "email"])
app.register_blueprint(google_bp, url_prefix="/login")
#app.route("/")
def index():
if not google.authorized:
return redirect(url_for("google.login"))
resp = google.get("/oauth2/v1/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
As I understand , if google.authorized is NOT true, it redirects to the route "google.login".
I'm wondering is there a way to know if I have been redirected as a result of being unauthorized from O-auth vs just being directed to the login url.
Thanks
Since you are new to Flask-Dance, I would like to recommend my library Authlib instead. You can check the Google login example for Flask at:
https://github.com/authlib/demo-oauth-client/tree/master/flask-google-login

Flask route at / returns 404 when used with Flask-Restplus

I have a Flask app which has a Flask-RestPlus API as well as a "/" route. When I try to access "/" however, I get a 404. If I remove the Flask-RestPlus extension, the route works. How do I make both parts work together?
from flask import Flask
from flask_restplus import Api
app = Flask(__name__)
api = Api(app, doc="/doc/") # Removing this makes / work
#app.route("/")
def index():
return "foobar"
This is an open issue in Flask-RestPlus. As described in this comment on that issue, changing the order of the route and Api solves the issue.
from flask import Flask
from flask_restplus import Api
app = Flask(__name__)
#app.route("/")
def index():
return "foobar"
api = Api(app, doc="/doc/")
flask-restplus defines a different way of assigning routes according to their docs:
#api.route('/')
class Home(Resource):
def get(self):
return {'hello': 'world'}
Notice that the api variable is used instead of the app. Moreover, a class is used although I am not 100% sure it is required.

Returning result of a different route, without redirecting [duplicate]

I'm trying to send a post request to my Flask app from one of its own views, but it hangs until I kill the server. If I do the request in JavaScript, it works fine. Why does it not work from the Python code?
from flask import Blueprint, render_template, abort, request, Response, session, url_for
from jinja2 import TemplateNotFound
from flask.ext.wtf import Form
from wtforms import BooleanField, TextField, PasswordField
import requests
login = Blueprint('login', __name__, template_folder='templates')
class LoginForm(Form):
email = TextField('Email')
password = PasswordField('Password')
#login.route('/login', methods=['GET', 'POST'])
def _login():
form = LoginForm(request.form, csrf_enabled=False)
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
return render_template('login.html', form=form)
Prior to 1.0, Flask's development server was single-threaded by default. In that mode, it can only handle one request at a time. Making a request blocks until it receives the response. Your Flask code makes a request in the one thread, and then waits. There are no other threads to handle this second request. So the request never completes, and the original request waits forever.
Enable threads in the dev server to avoid the deadlock and fix the immediate problem.
app.run(threaded=True)
However, making a full HTTP request to the app from within the app should never be necessary and indicates a deeper design issue. For example, observe that the internal request will not have access to the session on the client's browser. Extract the common code and call it internally, rather than making a new request.
def common_login(data):
...
#app.route("/login")
def login():
...
common_login(data)
...
#app.route("/api/login")
def api_login():
...
common_login(data)
...
I'm not familiar with Flask. However this bit of code:
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
Seems like you're accepting a posted form, validating it, and then posting it again. Over and over.

Different login views for different flask blueprints

I have a flask web application and it has multiple blueprints:
restserver
webserver
Most of the endpoints in both webserver and restserver require the user to be logged-in. I use flask-login extension, which provides #login_required decorator.
Is it possible to have different login_view for different blueprints?
# for web blueprint
login_mananger.login_view = '/web/login'
.. so on
One of the most important specialities of RESTful is statelessness, it means the server won't "remember" any information from clients, the requests from clients should have contained all the needed informations, including auth informations.
Back to your question, you don't have to use Flask-Login for RESTful service authentication, and you should not use cookies or sessions because of the diversity of the clients. You can DIY the HTTP authentication of course, but Flask-HTTPAuth is what you really need.
Here is a simplest example of Flask-HTTPAuth:
from flask.ext.httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
#auth.verify_password
def verify_password(username, password):
user = User.query.filter_by(username=username).first()
if not user:
return False
g.current_user = user
# You should accomplish the verify_password function by yourself in the User model.
return user.verify_password(password)
According to the doc of Flask-HTTPAuth:
The callback function takes two arguments, the username and the
password and must return True or False.
You should notice that the initialization of the Flask-HTTPAuth just in the blueprint rather than the whole application since this authentication just used in your restserver blueprint.
Then there is a function just like Flask-login's #login_required which provided by Flask-HTTPAuth:
#app.route('/private')
#auth.login_required
def private_page():
return "Only for authorized people!"
This "login_required" callback function will be called when authentication is succesful. Since all of your restserver's route should be protected, you can use a "before_request" handler for applying the protection to the whole blueprint. Assuming that your restserver blueprint's name is "rest" and you have saved the user object in "g" just like what we do before:
from flask import jsonify
#rest.before_request
#auth.login_required
def before_request():
if not g.current_user:
return jsonify({'error':'Unauthorized.'})
I think this will do the trick. What I wrote above is just the simplest example and you can do much better in many ways, such as replacing the user credentials with token after first request. I believe that you will figure it out.
If my answer is helpful, it will be my great honour if you can "upvote" for this answer, thanks.
In your case, you need to place the login manager declaration in the same file as the flask app instance. This is commonly an __init__.py file with the app = Flask(__name__).
At the top, import LoginManager class
from flask_login import LoginManager
Then tie it to the app instance.
login_manager = LoginManager()
login_manager.init_app(app)
(This was not asked but just incase someone needs it) Lets say you have admins and normal users and you are authenticating from different tables:
#login_manager.user_loader
def load_user(user_id):
x = Users.query.get(str(user_id))
if x == None:
x = Admins.query.get(str(user_id))
return x
Finally after importing blueprints you can define the login views for each in a dictionary
login_manager.blueprint_login_views = {
'admin': '/admin/login',
'site': '/login',
}

Flask-Restful ignores blueprint's url_prefix

I want to use Flask blueprints to organize my Flask-Restful resources into separate url prefixes. Regardless of what url prefix I set (during either blueprint creation or registration), everything gets mapped to the raw route paths. How do I correctly use Restful with blueprints?
app = Flask(__name__)
api = Api(app)
api.add_resource(Building, '/<int:id>', endpoint='building')
api.add_resource(Jack, '/<int:id>', endpoint='jack')
building_api = Blueprint('building_api', __name__)
jack_api = Blueprint('jack_api', __name__)
app.register_blueprint(building_api, url_prefix='/buildings')
app.register_blueprint(jack_api, url_prefix='/jacks')
All documentation I can find says that these should now be available at /buildings/<int:id> and /jacks/<int:id>, but both of those urls 404 and instead I can access the building one at /<int:id>. Hard coding the path in add_resource fixes it, but defeats the point of url_prefix.
You need to pass the blueprint to the Api instance, not the app.
building_bp = Blueprint('buildings', __name__)
building_api = Api(building_bp)
building_api.add_resource(Building, '/<int:id>')
app.register_blueprint(building_bp, url_prefix='/buildings')
This is zhe best way to do with blueprint:
from flask import Flask, Blueprint
from flask_restful import Api, Resource, url_for
app = Flask(__name__)
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
class TodoItem(Resource):
def get(self, id):
return {'task': 'Say "Hello, World!"'}
api.add_resource(TodoItem, '/todos/<int:id>')
app.register_blueprint(api_bp)
you should send Blueprint'instance to Api
I do not know why but I struggled using the Blueprint as mentioned in the answers.
But here's a quick solution I found while going through the doc link. Making use of the prefix parameter in Api() does the job.
app = Flask(__name__)
api = Api(app, prefix='/buildings')
Now, all your routes will be prefixed with /buildings. Just make sure you use url_for('link') in places where you might have simply used a /link.
One more strange thing I noticed is that atleast for me, it did not work until I renamed my routes to the same name as their class names. For example, Class Home(Resource) should have a route to /home. Using /homeepage or any other route for Home Class causes an error. Not sure if it is only me.

Categories