Flask-principal tutorial (auth + authr)

Question:

Anybody know of a good tutorial about flask-principal? I’m trying to do authentication and authorization (needRole and needIdentity) and I haven’t gotten anywhere.

I am almost sure there’s no really comprehensive tutorial — maybe some of you has some time on their hands and would like to post a tutorial-as-answer? I’m REALLY determined to use flask instead of django but need to fix this.

Asked By: pocorschi

||

Answers:

The only one out there seems to be this blog post.

The web site of the project is a (very brief) tutorial, followed by complete API documentation. Your question indicates you’ve already seen this.

You might also be interested in flask-login which provides session management, and is also covered in the linked blog post.

It’s unlikely anyone here will have experience with it (and the time to share in detail) but I’m all for a tutorial-as-answer.

Answered By: agf

I know this question is kind of old, but a couple of days ago I was looking for the same thing so hopefully this will help someone in the future . . .

A good place to start is on the github repo for Flask-Principal.

I’ve had some trouble with Flask-Principal (FP) too. If you’re new to decorators, context-managers, and signals you’ll probably want to research them a little before using FP.

Flask registers signals based on a package called Blinker. If you don’t have Blinker, Flask will still allow you to declare signals however they won’t do anything. To see what I mean, have a look in the source for Flask’s signals.py.

So why does this matter for FP? Well, it turns out that FP uses signals to register and update identities. Specifically:

  1. identity_loaded: When this signal is called, we know to create an identity object for the user. (It’s called via Principal._set_thread_identity())

  2. identity_changed: When this signal is called, we know to update the user’s identity. (When it’s called it executes Principal._on_identity_changed())

So what do I mean by called? First, we need to know how signals are set up. Blinker works by allowing functions to “subscribe” to signals. So, for example, Principal._on_identity_changed() is set up as a subscriber for the signal identity_changed. Whenever the signal identity_changed is sent, _on_identity_changed() is executed. The code looks like this:

from blinker import signal

test = signal('test') 
test.connect(func_we_want_to_execute_when_signal_is_called)

Back to the question of how signals are called. In Blinker, signals handlers are executed when we call send() on the signal object. So for our test signal, the syntax is just:

test.send()

When test.send() is called func_we_want_to_execute_when_signal_is_called will execute. Hopefully this example in the FP documentation makes a bit more sense now:

def login_view(req):
    username = req.form.get('username')
    # Your authentication here.

    # Notice our signal (identity_changed) is being called (identity_changed.send())
    # What function is being called? Principal._on_identity_changed()
    identity_changed.send(app, identity=Identity(username)) 

However we can simplify setting up signals if we use a decorator to do it for us. Pretend again that I’ve set up my test signal but haven’t connected it. We can do:

@test.connect
def func_we_want_to_execute_when_signal_is_called():
    return stuff

What the above code does is essentially sets up the function we want to execute when our test signal is sent. Hopefully now the following code from the FP documentation makes sense:

# We're setting up our signal (identity_loaded) 
# to execute the function below (on_identity_loaded) 
# when we call our signal (identity_loaded.send())
# which is called in Principal._set_thread_identity()
@identity_loaded.connect 
def on_identity_loaded(sender, identity):
    # Get the user information from the db
    user = db.get(identity.name)

    # Update the roles that a user can provide
    for role in user.roles:
        identity.provides.add(RoleNeed(role.name))

    # Save the user somewhere so we only look it up once
    identity.user = user

So you can see that signals really drive the identity process. Roles and permissions are really an (easier) afterthought if you’re looking for a way to do authorization of any kind.

For me, wrapping my head around signals was the hardest part; I hope this was helpful for someone else, too. But I’d really encourage you to read through the source code I’ve linked above for Flask-Principal; it is probably going to be the best way to understand what is going on.

Answered By: pal25

For some reason there are very few examples on flask principle out there. I personally found the Flask-Principal documentation cryptic to begin with. Once I went through the source code everything seemed to fit into place better.

Inspired by these 2 links that I was able to find, here and here, I put together a simple example using flask-login, flask-principals and blueprints

https://github.com/shankararul/flask-login-principal

I tried my best to explain in this post here. If you have any feedbck, please let me know and i can modify/update the post

https://medium.com/@shankararul/a-shot-at-demystifying-flask-principal-dda5aaeb6bc6

Answered By: Shankar ARUL