13

I'm trying to allow users to login to my Flask app using their accounts from a separate web service. I can contact the api of this web service and receive a security token. How do I use this token to authenticate users so that they have access to restricted views?

I don't need to save users into my own database. I only want to authenticate them for a session. I believe this can be done using Flask-Security and the @auth_token_required decorator but the documentation is not very detailed and I'm not sure how to implement this.

EDIT:

Here's a code example:

@main.route("/login", methods=["GET", "POST"])
def login():

    payload = {"User": "john", "Password": "password123"}
    url = "http://webserviceexample/api/login"
    headers = {'content-type': 'application/json'})

    #login to web service
    r = requests.post(url, headers=headers, json=payload)
    response = r.json()

    if (r.status_code is 200):
        token = response['user']['authentication_token']

        # allow user into protected view

    return render_template("login.html", form=form)


@main.route('/protected')
@auth_token_required
def protected():
    return render_template('protected.html')
Amerikaner
  • 131
  • 1
  • 1
  • 7
  • You need to store the users with the token; and confirm the validity of the token against the service. Otherwise you will have to authenticate visitors each time against the 3rd party service whenever they start. – Burhan Khalid Sep 10 '15 at 20:02
  • By each time do you mean for each session? If so, that's fine that they would have to authenticate again. Is there a reason this would be an issue? – Amerikaner Sep 10 '15 at 20:15

1 Answers1

26

Hey there Amedrikaner!

It looks like your use-case is simple enough that we can implement this ourselves. In the code below, I'll be storing your token in the users session and checking in a new wrapper. Let's get started by making our own wrapper, I usually just put these in a wrappers.py file but can you can place it where you like.

def require_api_token(func):
    @wraps(func)
    def check_token(*args, **kwargs):
        # Check to see if it's in their session
        if 'api_session_token' not in session:
            # If it isn't return our access denied message (you can also return a redirect or render_template)
            return Response("Access denied")

        # Otherwise just send them where they wanted to go
        return func(*args, **kwargs)

    return check_token

Cool!

Now we've got our wrapper implemented we can just save their token to the session. Super simple. Let's modify your function...

@main.route("/login", methods=["GET", "POST"])
def login():

    payload = {"User": "john", "Password": "password123"}
    url = "http://webserviceexample/api/login"
    headers = {'content-type': 'application/json'})

    #login to web service
    r = requests.post(url, headers=headers, json=payload)
    response = r.json()

    if (r.status_code is 200):
        token = response['user']['authentication_token']

        # Move the import to the top of your file!
        from flask import session

        # Put it in the session
        session['api_session_token'] = token

        # allow user into protected view

    return render_template("login.html", form=form)

Now you can check the protected views using the @require_api_token wrapper, like this...

@main.route('/super_secret')
@require_api_token
def super_secret():
    return "Sssshhh, this is a secret"

EDIT Woah! I forgot to mention you need to set your SECRET_KEY in your apps config.

Just a config.py file with SECRET_KEY="SOME_RANDOM_STRING" will do. Then load it with...

main.config.from_object(config)
pmccallum
  • 808
  • 7
  • 14
  • Do we just need these two elements or should there not be another function to verify user credentials against the database? – Evan Burbidge May 22 '16 at 11:57
  • @EvanBurbidge Sorry for the late reply. This implementation was specifically for authenticating users against an external API elsewhere. Most like you want to follow a standard user login flow, check out this link for more https://flask-login.readthedocs.io/en/latest/ – pmccallum Aug 30 '16 at 13:25
  • @FBoucaut thanks for getting back in touch!! I actually started working with node and made a pretty nice user login / register / logout package using token based auth! Decided to keep all JS all the time :) – Evan Burbidge Aug 31 '16 at 08:55
  • I'm interested in building an authentication similiar to this. Can anyone provide some information to how the authentication API has to look like to make this work. Or maby provide a link to a resource explaining how an API like this can be build. Thanks! – Marc B. May 24 '20 at 12:54
  • @MarcB. this could help you https://realpython.com/token-based-authentication-with-flask/ – prashant Aug 08 '20 at 09:52