Handling user registration and authentication isn’t an easy task. Both users and developers understand this. Users have to select and remember another username and password (taking into account different password policies, of course). A developer’s responsibility is to make this process secure and to properly protect the user’s data. So it’s no surprise that logging in with one account – Facebook or Google, for example – is extremely popular. Users gain easy access, and developers can delegate the sensitive process of authenticating users to social media services.

Logging in with Facebook or some other site is possible thanks to the OAuth protocol. This standard presents specific authorization flow for web applications, desktop applications, and mobile phones. Currently, there are two versions of it. The newest one, OAuth 2.0, is used by Facebook, Google, Github, LinkedIn, Reddit, and Dropbox. Twitter still uses the previous version, OAuth 1.0a, which has a different flow.

To inspect this process, let’s take a look at the OAuth 2.0 protocol. We will use Flask framework and the Rauth OAuth 2.0 consumer library to implement it in Python.

The OAuth 2.0 Protocol Flow

For purposes of implementing an external login functionality, take a look at:

The OAuth 2.0 protocol uses three roles that communicate with each other. These are:

The User – who wants to authenticate using his or her Facebook account

– who wants to authenticate using his or her Facebook account The Application Server – our app which requests, on the user’s behalf, authorization from Facebook

which requests, on the user’s behalf, authorization from Facebook The Facebook Server – which is, in this case, the authorization and resource server. It authenticates users and gives approval to the application to access to the user’s resources.

Because the protocol flow is quite complicated, here’s a short description of whole process. The authentication starts when user clicks the Login with Facebook button. In the next step, the application redirects user to the Facebook login page. The user logs into Facebook and allows the application to access to their data (name, surname, email, etc.). This user consent is represented by the value that the official protocol calls the “authorization grant”. This grant can be one of three types; for our example, we’ll discuss the most common type, the authorization code. The application then sends this code back to Facebook, together with its client id and client secret (application credentials obtained during the app registration). In reply, Facebook sends back another string called the access token. This access token is then exchanged for the user’s data.

The whole authentication flow looks something like the following:

Let’s look at each step in detail.

How The User Logs In Via Facebook

A. The user accesses the application login page

The process starts with the user requesting the application’s login page. In response, the application serves the user a HTML login page.

B. The user authorizes the application in Facebook

The user clicks the Login with Facebook button. As a result, the application prepares an URL that redirects the user to the login dialog. This includes parameters like:

client_id – a unique number obtained from Facebook while registering our application

– a unique number obtained from Facebook while registering our application redirect_uri – the URL to which the user is redirected after the Facebook authorization. It is configured during the application’s registration

– the URL to which the user is redirected after the Facebook authorization. It is configured during the application’s registration scope – a comma-separated list of permissions to request from the user using our app. All available scopes for Facebook are listed here.

– a comma-separated list of permissions to request from the user using our app. All available scopes for Facebook are listed here. grant_type – the credential that represents the Facebook user authentication employed by the application to get its access token. This can be an authorization code, an implicit grant, resource owner password credentials, or client credentials. We will use the authorization code.

If the user isn’t logged in, they must authenticate in Facebook and authorize the application’s access to their Facebook account. (The application doesn’t participate in this and doesn’t know the user’s credentials). After a successful authorization, Facebook directs the user back to the application with the authorization code. While the user access the app page, in the background, the application starts communication with Facebook.

C. The application authorizes in Facebook and requests an access token

While the user accesses the redirect URI in the application – which typically displays a preloader image, the application connects to the Facebook server in the background. The application requests an access token. It sends the obtained authorization code and the redirect URI with its own client id and client secret. During this process, Facebook validates data. In particular:

The application authenticates in Facebook with its client id and client secret. Facebook validates the authorization code. Facebook checks if the received redirect URI matches the URL used to redirect user in Step B.

If everything is valid, Facebook responds back with an access token.

D. The application retrieves the user’s data from Facebook

In order to retrieve the user’s data, the application sends an access token with requested data to Facebook. Facebook verifies the access token and returns the user’s data in response. The application stores the user’s data in its database and signs the user in.

Now that we’ve gotten to know the OAuth 2.0 protocol a little bit, let’s see how it will look in an example application.

Testing OAuth with a Flask Application

Application Requirements

The application uses:

Flask – a Python framework for web applications

Flask-SQLAlchemy – Flask wrapper for Python ORM SQLAlchemy

SQLite database

Rauth – A Python OAuth 2.0 and OAuth 1.0a library

The file requirements.txt contains all of packages with exact versions.

The code for the example application consists of these files:

app.py – contains declared view functions responsible for rendering an HTML page and a function that assists in user session management (logging in and out)

– contains declared view functions responsible for rendering an HTML page and a function that assists in user session management (logging in and out) models.py – contains an SQLAlchemy model related to presented database design

– contains an SQLAlchemy model related to presented database design facebook_auth.py – contains the external authentication part

– contains the external authentication part task.py – an asynchronous task manager for background task handling (the part where the application communicates with Facebook)

– an asynchronous task manager for background task handling (the part where the application communicates with Facebook) /static/facebookAuthStatus.js – updates user on the status of requests between the application and the Facebook server. Depending on the received value, the user sees the pre-loader, error, or success page.

– updates user on the status of requests between the application and the Facebook server. Depending on the received value, the user sees the pre-loader, error, or success page. /templates – a directory that contains HTML templates

The code is hosted on Github.

Database Design

The vast majority of various applications include a user login function, as does the online forum prototype you’ll see below. Usually, login data includes a user’s login and password; in this case, we’ll delegate the process of user authentication to an external resource (Facebook) and rely on their data. The user’s retrieved data will be stored in the user_profile table. Remember: ‘user’ is a reserved keyword in many databases, so naming the table simply ‘user’ is not a good idea).

The following columns in the user_profile table are provided to store appropriate information:

id – is an artificial PK

– is an artificial PK facebook_id – the user id retrieved from Facebook. We’ve created a UNIQUE constraint on this column, since the entry is unique to each user. Looking at the value of this field, we can check the database to see if the user is registered in our application.

– the user id retrieved from Facebook. We’ve created a UNIQUE constraint on this column, since the entry is unique to each user. Looking at the value of this field, we can check the database to see if the user is registered in our application. first_name – the user’s first name, as retrieved from their Facebook account

– the user’s first name, as retrieved from their Facebook account last_name – the user’s last name, as retrieved from their Facebook account

– the user’s last name, as retrieved from their Facebook account email – the user’s email address, as retrieved from their Facebook account

Because Facebook communicates with our application, we will need two extra tables: the async_operation table and a related dictionary table called async_operation_status that stores three available statuses: pending , error , and ok .

The appropriate SQLAlchemy classes that reference to the tables are as follows:

class UserProfile(UserMixin, db.Model): __tablename__ = 'user_profile' id = db.Column(db.Integer, primary_key=True) facebook_id = db.Column(db.String(64), nullable=False, unique=True) first_name = db.Column(db.String(64), nullable=True) last_name = db.Column(db.String(64), nullable=True) email = db.Column(db.String(64), nullable=True) # dictionary table. Stores values: pending, ok, error class AsyncOperationStatus(db.Model): __tablename__ = 'async_operation_status' id = db.Column(db.Integer, primary_key=True) code = db.Column('code', db.String(20), nullable=True) class AsyncOperation(db.Model): __tablename__ = 'async_operation' id = db.Column(db.Integer, primary_key=True) async_operation_status_id = db.Column(db.Integer, db.ForeignKey(AsyncOperationStatus.id)) user_profile_id = db.Column(db.Integer, db.ForeignKey(UserProfile.id)) status = db.relationship('AsyncOperationStatus', foreign_keys=async_operation_status_id) user_profile = db.relationship('UserProfile', foreign_keys=user_profile_id) event.listen( AsyncOperationStatus.__table__, 'after_create', DDL(""" INSERT INTO async_operation_status (id,code) VALUES (1,'pending'),(2, 'ok'),(3, 'error'); """) )

Registering the Application with OAuth Providers

The first step in external authentication is registering our app with an external provider. In order to provide Facebook authentication, visit Facebook for Developers and go to My Apps -> Register. You’ll have an opportunity to register your app with a list of providers who enable OAuth authentication.

Next, you will go to the Application Settings. At this point, we’re most interested in App Id and App Secret (the application’s credentials for Facebook). OAuth uses these keys to figure out who is connecting to the remote application. These keys don’t impart user authentication.

On the provider’s site in the settings of your newly created app, enter your application’s URL and app domain.

Note that Facebook doesn’t allow localhost for apps (URL when registering your app with Facebook). So, in the development phase you’ll have to create a tunnel to your localhost (for example, by using ngrok to make your local server accessible over the Internet). Or you can create a line in /etc/hosts that will map our hostname to an IP address. It would look something like this:

127.0.0.1 example-app.com

Back to our example. In Facebook for Developer’s registered app settings, we should have specified the App Domain as example-app.com and the Site URL as http://example-app.com/.

Now, we’ll move on to application configuration.

Configuring the Application

We must provide configuration for our application. We can internally hardcode it, or we can place it in a separate file located outside the actual application package.

I’ll demonstrate the hardcode option for simplicity’s sake. Flask provides a config object that is a subclass of a dictionary. It handles configuration values for Flask and for extensions.

At this point, we have to add the previously-obtained app_id and app_secret and pass the SQLite connection URI to app.config in models.py . The relevant code looks like this:

app.config['SECRET_KEY'] = 'secret value' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' app.config['OAUTH_CREDENTIALS'] = { 'facebook': { 'id': '4445949623923', 'secret': 'APP_SECRET' } }

A Closer Look at Web Application Flow

Now that we have an overview of the flow, let’s look at the code associated with the various steps in this process.

A. The user accesses the application login page.

The user starts the process by requesting a login page. In the response, the server gives the user an HTML page. This is handled in the Flask index() function presented below.

@app.route('/') def index(): return render_template('index.html')

The working of this view function does the following: when an URI ‘/’ is requested, the function index() renders an index.html (the route decorator registers index() functions with URL rule ‘/’).

The rendered page looks as follows.

The content of the index.html is as follows: