Product College starts on September 5. Apply now.

Lecture

Download the slides here.

Now it's your turn!

With the lessons learned from the lecture above you should extend your Trip Planner backend. Extend the backend to support the following features:

  • Provide an endpoint that allows a client to register a new user by providing username and password
  • Provide an endpoint that allows a user to verify credentials (username and password). It should return a status code of 200 (OK) for correct credentials and status code 401 (Unauthorized) for incorrect credentials
  • The endpoint that allows clients to create trips should require authentication. The backend should associate the created trip with the authenticated user
  • The endpoint that allows clients to retrieve trips should require authentication. It should only return trips that belong to the authenticated user

Remember that it's preferred that you write tests for these new features before you write the implementation.

Snippets and Additional Resources

Basic Auth in Flask

To implement Basic Auth in flask you should use this starter code:

def check_auth(username, password):
    return username == 'admin' and password == 'secret'

def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            message = {'error': 'Basic Auth Required.'}
            resp = jsonify(message)
            resp.status_code = 401
            return resp

        return f(*args, **kwargs)
    return decorated

Once you have defined these methods you can use the @requires_auth annotation to wrap API methods that require authentication, e.g. like this:

class Trip(Resource):

    @requires_auth
    def get(self, trip_id=None):
        if trip_id is None:
          
        else:
          

What does this starter code do?

The requires_auth method defines a wrapper that will check the authentication header of an incoming request. It first reads the authentication header by accessing request.authorization. Then it checks if the header was provided. If the header was provided it passes the details to the check_auth method. The check_auth method needs to decide whether or not the user has provided valid credentials. We've provided you with a dummy implementation, you'll need to replace this implementation with one that accesses the DB and checks the password. If the authentication was successful the wrapped function will be called as usual. If the authentication was not successful we generate a 401 response to show the client that the username or password were incorrect.

Using the Bcrypt library

As mentioned throughout the lecture you will need to use an encryption library to encrypt paasswords before they are stored. As encryption library you should be using bcrypt (documentation). Like any other python library you can install it through pip3.

Generating an Encrypted Password

To encrypt a password, provide the unencrypted password and a random salt:

hashed = bcrypt.hashpw(userPassword, bcrypt.gensalt(12))

The number provided as part of gensalt is called the work factor. The higher the work factor, the longer it takes to generate a hash. This also means that attackers will need to spend more time on a brute force attack. 12 is the default value. Depending on how fast your server is you can adjust this value to increase the cost for potential attackers.

Pro Tip: Since encrypting passwords will also slow down your tests you might want to make the work factor configurable. You can add a propery to your app instance by adding this line as part of the server setup at the top of server.py:

app.bcrypt_rounds = 12 

Then you can pass this variable to the hashpw method instead of providing a literal value:

hashed = bcrypt.hashpw(userPassword, bcrypt.gensalt(app.bcrypt_rounds)) 

Comparing Passwords

Comparing passwords that have been encrypted with bcrypt is very simple. Here's an example from the bcrypt documentation:

if bcrypt.hashpw(password, hashed) == hashed:
        print("It Matches!")
    else:
        print("It Does not Match :(")

All you need to do is provide the unencrypted and the encrypted password to hashpw; then compare the result with the hashed password.

Next Steps

  1. Implement your custom check_auth method that actually uses the DB to compare encrypted passwords
  2. Create a User resource and endpoint that allows the client to sign up new users and test username and password of existing users (if you're unclear about how to structure this, come and ask :) )
  3. Make your API secure by implementing the requirements stated at the beginning of this step:
    • Provide an endpoint that allows a client to register a new user by providing username and password
    • Provide an endpoint that allows a user to verify credentials (username and password). It should return a status code of 200 (OK) for correct credentials and status code 401 (Unauthorized) for incorrect credentials
    • The endpoint that allows clients to create trips should require authentication. The backend should associate the created trip with the authenticated user
    • The endpoint that allows clients to retrieve trips should require authentication. It should only return trips that belong to the authenticated user

Once you've implemented the modified API the server is complete. From now on we'll focus on implementing the iOS client for Trip Planner.

Feedback

If you have feedback on this tutorial or find any mistakes, please open issues on the GitHub Repository.

Summer academy

An iOS Development Summer Course

Design, code and launch your own app. Locations across the USA and Asia

Find your location

Product College

A computer science college

Graduate into a successful career as a founder or software engineer.

Learn more