· 7 years ago · Feb 11, 2019, 06:50 AM
1import datetime
2from flask import jsonify, make_response, request
3import MySQLdb
4from werkzeug.security import check_password_hash
5import jwt
6from functools import wraps
7
8# token_required function that will be used as the @token_required decorator
9def token_required(f):
10 @wraps(f)
11 def decorated(*args, **kwargs):
12 token = None
13
14 # Check for the token in the requests headers
15 if 'x-access-token' in request.headers:
16 token = request.headers['x-access-token']
17
18 # If there's no token in the request, return a 401
19 if not token:
20 return jsonify({'message' : 'Token is missing!'}), 401
21
22 try:
23 # Decode the token using the app's secret key
24 data = jwt.decode(token, app.config['SECRET_KEY'])
25 #
26 try:
27 dbconn=MySQLdb.connect(database=dbname, user=dbuser, password=dbpass, host=dbhost)
28 cursor = dbconn.cursor()
29 # Instead of a query, it's cleaner to create a procedure in your database to get a user using the public_id
30 # Then call the procedure and provide the public_userid from the token
31 cursor.callproc('userGetUserByPublicID',[data['public_userid']])
32 current_user = User(cursor.fetchone())
33 cursor.close()
34 dbconn.close()
35 except Exception as e:
36 error = "ERROR: " + str(e)
37 return error
38 except:
39 # If token could not be decoded, return a 401
40 return jsonify({'message' : 'Token is invalid!'}), 401
41
42 return f(current_user, *args, **kwargs)
43
44 return decorated
45# Now underneath other routes in your Flask app
46# You can add a @token_required decorator
47# Example:
48###########################################################
49# @app.route('/user/profile/<profile_id>', methods=['GET'])
50# @token_required()
51###########################################################
52# To access a route with the @token_required() decorator
53# First use basic auth to access the login route '/user/standard/login'
54# Then take the token returned and place it in the header of a request to a @token_required() route like below:
55# {'x-access-token':'token goes here'}
56
57
58
59# The request to this route just needs to contain Basic Auth details (username and password)
60# The route will return a token that can be reused for 8 hours to authenticate
61@app.route('/user/standard/login')
62def login():
63 # Access the credentials provided in the request using request.authorization
64 # auth.username and auth.password will contain the username and password provided
65 auth = request.authorization
66
67 # If username or password is not provided, return a 401
68 if not auth or not auth.username or not auth.password:
69 return make_response('Could not verify', 401, {'WWW-Authenticate' : 'Basic realm="Login required!"'})
70
71 # Check against the user table to see if the user exists
72 try:
73 # Use globally set database credentials, preferably read in from a secure file(s)
74 dbconn=MySQLdb.connect(database=dbname, user=dbuser, password=dbpass, host=dbhost)
75 cursor = dbconn.cursor()
76 # Instead of a query, it's cleaner to create a procedure in your database to get a user using the username
77 # Then call the procedure and provide the username provided
78 cursor.callproc('userGetUserByUsername',[auth.username])
79 user = User(cursor.fetchone())
80 cursor.close()
81 dbconn.close()
82 except Exception as e:
83 error = "ERROR: " + str(e)
84 return make_response('Could not verify', 401, {'WWW-Authenticate' : 'Basic realm="Login required!"'})
85
86 # If the username does not exist, return a 401
87 if not user:
88 return make_response('Could not verify', 401, {'WWW-Authenticate' : 'Basic realm="Login required!"'})
89
90 # Check if the password provided matches the hashed password in the database
91 if check_password_hash(user.password, auth.password):
92 # If the passwords match, create a token using the package jwt and encoded using the apps secret key
93 # Token will contain the user's ID and an expiration date 8 hours in the future
94 token = jwt.encode({'public_userid' : user.public_userid, 'exp' : datetime.datetime.utcnow() + datetime.timedelta(hours=8)}, app.config['SECRET_KEY'])
95
96 # Return the token in a json object to be stored by the client and used for subsequent authentication
97 return jsonify({'token' : token.decode('UTF-8')})
98 # If the passwords do not match, return a 401
99 return make_response('Could not verify', 401, {'WWW-Authenticate' : 'Basic realm="Login required!"'})