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')
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)
Related
i'm doing my own project.
communicate python program - django server.
first is when program send information about signup(like name, password, id etc.) server return success signal.
next step is when program send login information about sign(like name, password), server return jwt token and program receive jwt token.
I'm try everything what i know... but i don't know how to return jwt token to python program.
any idea?
Assuming you already have a proper way to generate the token correctly:
create an endpoint to login with credentials (note the csrf_exempt to allow POST calls from your program)
path('/login', csrf_exempt(login)) )
create a view to process the request - to protect the credentials, expect them as the payload of a POST request:
#require_POST
def login(request):
username = request.data.get('username', None)
password = request.data.get('password', None)
if username is None:
return HttpResponseBadRequest('username is missing')
if password is None:
return HttpResponseBadRequest('password is missing')
# validate the user/credentials
your_function_to_validate(username, password)
jwt = your_function_to_generate_and_save_the_JWT(username, password)
return HttpResponse(jwt)
call the endpoint using the Python program:
url = base_url + '/login'
credentials = {
'username': 'admin',
'password': '12345'
}
res = post(url, data=credentials)
if res.status_code != 200:
# deal with bad credentials
pass
jwt = res.data
I want to make auth layer in flask app . I was using flask-jwt-extended for jwt auth so for that i have to mentioned #jwt_requied decorator for each protected route . so i dont want to do that for each protected route. I thought to define a function and make that to execute before each request by using #app.before_request. i want auth to happen in this layer.
def check_user_token():
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
if not token:
return jsonify({'message': 'Token is missing'}), 401
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
current_user = UserDetails.query.filter_by(username=data['username']).first()
except:
return jsonify({'message': 'Token is in-valid'}), 401
return current_user
So this the function i want to call before each request to check auth.
#app.before_request
def before_calback():
#want to call that check_user_token() from here and
#also dont want to call that for 'login' route .
#Once i will get some response from check_user_token()
#based on that want to proceed further
#and here i dont know to do this.
Basically my qns is how to do authentication in #app.before_request def before_calback() ?
Can you access check_user_token()?
It would be like this
#app.before_request
def before_calback():
check_user_token()
If you can post the some code, it would be easier to help you.
Again fighting trying to make my first flask application, this time, (after I created every I need and all works smoothly) I'm trying to protect some endpoints with flask_jwt_extended, but I can't find how to work with them in my pages, the documentation is mostly about displaying JSON messages and some tutorials use postman while in my case I'm using HTML templates.
For example, a user sends his credentials from the login page to this endpoint :
#app.route('/login', methods=['POST'])
def UserLogin():
data = parser.parse_args()
current_user = UserModel.find_by_username(data['username'])
if not current_user:
return {'message': 'User {} doesn\'t exist'.format(data['username'])}
if UserModel.verify_hash(data['password'], current_user.password):
access_token = create_access_token(identity = data['username'])
refresh_token = create_refresh_token(identity = data['username'])
resp = jsonify({'login': True}) #I just added this line from the documentation
set_access_cookies(resp, access_token) # and this one
set_refresh_cookies(resp, refresh_token) # and this one
return redirect(url_for('results'))
else:
return {'message': 'Wrong credentials'}
and of course, I added the #jwt_required decorator the results endpoint:
#app.route('/result',methods = ['POST','GET'])
#jwt_required
def results():
temp={}
if request.method == 'POST':
# some code to fill temp with values
return render_template('result.html',data=temp)
So I'm getting a {
"msg": "Missing cookie \"access_token_cookie\""
}Obviously because I'm not sending the jwt back but if send it in the return statement how can I redirect the user the page I want ?? And indeed I used app.config['JWT_TOKEN_LOCATION'] = ['cookies']
You may want to:
resp = make_response(redirect(url_for('results')))
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
I don't think you need this line! --> resp = jsonify({'login': True})
Took me a while to figure it out, not sure why this part is not clear in the docs, most of the examples there just returns JSON directly
Also, you get same error if JWT_ACCESS_COOKIE_PATH is routed wrongly.
Right now, my webapp has a full auth system implemented through Flask-Login. But say that I have this controller:
#app.route("/search_query")
#login_required
def search_query():
query = request.args.get("query")
return jsonify(search(query))
This works great when you login to the page and make searches against it, if you're authenticated it goes through, otherwise it routes you back to the login page (through an unauthorized_handler function).
#lm.unauthorized_handler
def unauthorized():
return redirect(url_for("login"))
The problem is, I want to be able to use some form of simple HTTP authentication for this without having two separate auth systems so that I can make REST API calls against it. In short, I want to avoid having to use both Flask-Login and HTTPBasicAuth, and just use Flask-Login to achieve simple authentication, is this possible through Flask-Login?
Use the flask-login's request_loader to load from request auth header. You don't need to implement the decorator yourself, and current_user will point to the user loaded.
Example:
login_manager = LoginManager()
#login_manager.request_loader
def load_user_from_header():
auth = request.authorization
if not auth:
return None
user = User.verify_auth(auth.username, auth.password):
if not user:
abort(401)
return user
With that, you can mark a view as login required by:
#app.route('/user/info')
#login_required
def user_info():
return render_template('user_info.html', current_user.name)
If you looking for users to authenticate and make REST API calls they are going to have to send their credentials with every api call.
What you could do is a new decorator that checks for the auth header and authenticate the user.
Please refer to the entire app that I've posted on codeshare for more info:
http://codeshare.io/bByyF
Example using basic auth:
def load_user_from_request(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if current_user.is_authenticated:
# allow user through
return f(*args, **kwargs)
if auth and check_auth(auth.username, auth.password):
return f(*args, **kwargs)
else:
return bad_auth()
return decorated
With that code you can now decorate your views to check for the header.
#app.route("/search_query")
#load_user_from_request
def search_query():
query = request.args.get("query")
return jsonify(search(query))
you can curl my example app like this:
curl --user admin#admin.com:secret http://0.0.0.05000/success
I am running a Flask web app and using Apache basic authentication(with .htaccess and .htpasswd files) to password protect it. I want to password protect only one webpage in the app. When I password protect the html file for the webpage there is no effect and the webpage is still not password protected. Could this be because it is my python file that is calling the html file using render_template? I'm not sure how to fix this issue.
You need to restrict access to your endpoint. This snippet should get you started down the right path.
from functools import wraps
from flask import request, Response
def check_auth(username, password):
"""This function is called to check if a username /
password combination is valid.
"""
return username == 'admin' and password == 'secret'
def authenticate():
"""Sends a 401 response that enables 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"'})
def requires_auth(f):
#wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
With this, you could decorate any endpoint you want to restrict with #requires_auth.
#app.route('/secret-page')
#requires_auth
def secret_page():
return render_template('secret_page.html')