8

I am trying to build a super simple web app for my own use. I'll be the only user, so I don't feel the need to involve a database for user management. I'm trying to use flask-login, but even though my call to login_user succeeds, I'm still met with a 401-Unauthorized page after the redirect to a page with @login_required. Here is the entirety of my app:

from flask import Flask, render_template, request, flash, redirect, url_for
from flask.ext.login import LoginManager, login_user, logout_user, current_user, login_required, UserMixin

app = Flask(__name__, static_url_path="")
app.secret_key = "[redacted]"

login_manager = LoginManager()
login_manager.init_app(app)
#login_manager.login_view = "login"

class User(UserMixin):
    def __init__(self, id):
        self.id = id

nathan = User('nathan')

@login_manager.user_loader
def load_user(userid):
    if userid == 'nathan':
        return nathan
    else:
        return None

@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

@app.route('/')
@login_required
def index():
    return render_template('index.html')

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == 'POST':
        if request.form['username'] == 'nathan'\
                and request.form['password'] == '[redacted]':
            login_user(nathan, remember=True)
            flash('logged in...', 'success')
            return redirect(request.args.get("next") or url_for("index"))
        else:
            flash('Incorrect username or password. Try again.', 'error')

    return render_template("login.html");

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)

I've verified that the login actually succeeds (login_user returns true) and that load_user is returning the nathan object.

[EDIT] Also, I'm currently using the Flask development server, in case that's relevant.

Not sure where I'm going wrong. Thanks in advance!

5
  • I'm not sure you can use flask-login without a database. It needs some way to store your session. Commented May 18, 2013 at 19:44
  • I thought session info was all stored in cookies, not a database. Commented May 18, 2013 at 20:30
  • I would suggest reading up on cookies a bit more. In short, cookies are things the browser stores on a per-domain basis that are sent along with every request to that domain. Session cookies simply consist of a session ID, which the application is responsible for looking up upon receiving the request. The application still needs to store some information about the session (such as expire time) which corresponds to the session ID. Flask-login handles all of this for you, but it needs some way to store those sessions. Commented May 18, 2013 at 20:34
  • 1
    I don't see any requirements for a database interface in the flask-login documentation. In fact, it says that it does not "impose a particular database or other storage method on you. You are entirely in charge of how the user is loaded." How would it even interface with the database if I had one? I don't, for example, see that the LoginManager or anything else requests database credentials, nor does the User class interface specify any set* methods or similar which I could use to store something in a database. Commented May 18, 2013 at 21:04
  • just wasted too much time on this one :-/ SharedDataMiddleWare is the way to go. Commented Sep 18, 2013 at 21:49

1 Answer 1

4

Update:

Since a newer version(0.2.2) of Flask-Login this is no more an issue. Check out the changes in this commit.

If you are using an older version, read on.


The problem here is static_url_path="". For Flask-Login to work you can not have an empty string static_url_path.

The following lines in the Flask-Login source(older version) reveal this:

if (current_app.static_url_path is not None and
    request.path.startswith(current_app.static_url_path)
):
    # load up an anonymous user for static pages
    _request_ctx_stack.top.user = self.anonymous_user()
    return

Since your static_url_path is "" the if condition evaluates to True, because of which every page you visit acts like a static page, and hence Flask-Login always loads an anonymous user, instead of continuing to load the actual user(using the load_user callback).


Also do not forget to uncomment #login_manager.login_view = "login"


If you still want to use the root folder of the app itself as the static folder, take a look at this solution, using SharedDataMiddleWare:

app.debug = True
if app.config['DEBUG']:
    from werkzeug import SharedDataMiddleware
    import os
    app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
      '/': os.path.dirname(__file__)
    })

if __name__ == "__main__":
    app.run(host="0.0.0.0")
Sign up to request clarification or add additional context in comments.

3 Comments

Wow, great answer. Thanks so much! The SharedDataMiddleWare solution is working great for me, too.
Looks like it may be fixed in the latest (0.2.7) version of Flask-Login. I don't see any reference to the static file path in that version.
@craigb yup you are right, I think it was changed somewhere during 0.2.2. Have updated answer accordingly, thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.