user1138143
user1138143

Reputation: 81

Secure Python REST APIs

I am trying to write some REST APIs in python, to begin with I started to write Authenticate code. I found sample code for authenticate on one of the site:

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(username, password):
            return authenticate()
        return f(*args, **kwargs)
    return decorated

I have used the above piece of code to secure my sample app:

@app.route('/student/<studentid>', methods = ['GET'])
@requires_auth
def api_users(studentid):
    students = {'1':'ABC', '2':'XYZ', '3':'TEST'}

    if studentid in students:
        return jsonify({studentid:students[studentid]})
    else:
        return not_found()

Now, I am trying to call this url through python requests/pycurl module. But, everytime it returns the 401 error irrespective of valid username/password.

using requests:

import requests, base64
usrPass = "admin:secret"
b64Val = base64.b64encode(usrPass)
from requests.auth import HTTPBasicAuth
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
res = requests.get('https://<abc.com>/student/1', auth=HTTPBasicAuth('admin','secret'), headers={'Authorization': 'Basic %s' % b64Val}, data={}, verify=False)
print res

using curl:

myCurlPut = pycurl.Curl()
myCurlPut.setopt(pycurl.URL, "https://<abc.com>/student/1")
myCurlPut.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
myCurlPut.setopt(pycurl.USERPWD, "%s:%s" % ('admin', 'secret'))
myCurlPut.setopt(pycurl.SSL_VERIFYPEER, 0)
myCurlPut.setopt(pycurl.HTTPHEADER, ['X-HTTP-Method-Override: GET'])
myCurlPut.perform()

Can, anyone please help me why everytime it returns the 401 error. Please suggest.

Upvotes: 3

Views: 1220

Answers (2)

formatkaka
formatkaka

Reputation: 1358

This is a working example of flask-authorization.

from functools import wraps

from flask import Flask,Response,request, abort


app = Flask(__name__)

def check_auth(name,passw):
    return (name=='admin' and passw=='pass')

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            abort(401)
        return f(*args, **kwargs)
    return decorated


@app.route('/')
@requires_auth
def hello():
    return "Hello World"


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

My requests file:

import requests, base64
usrPass = "admin:pass"
b64Val = base64.b64encode(usrPass)
from requests.auth import HTTPBasicAuth
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
res = requests.get('http://127.0.0.1:5000/', auth=HTTPBasicAuth('admin','pass'), headers={'Authorization': 'Basic %s' % b64Val}, data={}, verify=False)
print res

If you are running this on localhost you should use the localhost address.
What is <abc.com> in your code.Probably that is the error.

EDIT 2

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired


def gen_token(name,passw, expiration=None):
    s = Serializer(app.config['SECRET_KEY'], expires_in = expiration)
    return s.dumps(name, passw)

def verify_token(token):
    serial = Serializer(app.config['SECRET_KEY'])
    try:
        data = serial.loads(token)
    except BadSignature:
        return "Error"
    except SignatureExpired:
        return "Error"

    name = data[0]
    passw = data[1]

    return name,passw

These are couple of methods that will help you get started with the token based auth.

What I did was

  1. User requests token from the server by sending a request with username and password in Auth headers
  2. After you check that the username and password is correct you can generate the token using gen_token method. You can modify this method according to your requirments. Read Here
  3. Now the user sends the token it recieved from method 2 in the Auth header in the place of username. password can be left blank or send None in that place.
  4. When you recieve the token you need to load it using the SECRET_KEY.Exceptions can be handled as per your requirements. If the token is valid you will be able to obtain the user who sent the request and thus carry your procedure.

Hope it helps!

Check out this link for a more detailed explanation.

Upvotes: 1

shivaji bhosale
shivaji bhosale

Reputation: 103

Looks like you are not passing username and password correctly to authenticate. Value of username and password you should get from the auth variable. So try changing the requires_auth funtion as:

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

Upvotes: 0

Related Questions