Getting Started With OAuth2

Ismail Benmoussa
BPAM Tech Blog
Published in
10 min readJul 31, 2017

We’ve all experienced the situation where you want to sign up to an app or a website, and they suggest you sign up with your Facebook or Google account.

Let’s say I want to create a Spotify account for the first time, I go to spotify.com and hit ‘Sign up’. This is what I get:

If I decide to sign up with Facebook, I get redirected to a Facebook sign in page where it is specified that by signing in via Facebook, Spotify will get access to some of my information. (Here, it will get my public profile, friends list, email address and birthday stored on Facebook).

Spotify also wants to be able to post on my behalf on my Facebook wall.

Once I accept all of this, my Spotify account is finally created and I get redirected to its account overview page.

What’s the point of allowing a user to sign up through another service or website?

  1. Creating a secure authentication system is hard and sometimes you might want to rely on the one created by very big and powerful companies like Facebook or Google (although that might not be the main reason Spotify uses it in this example)
  2. Spotify might want to use Facebook data in order to improve your experience on its app.

How does it work?

To do that, Spotify needs to read or write in your personal data stored on Facebook’s servers. So Spotify needs to ask Facebook for the right to read or write data on its servers, and somewhere in the process, someone needs to ask you for your permission.

This workflow works using an authorization framework that enables applications (Spotify) to get a limited access to a user account (your account) on an app or a website (Facebook). This framework is called OAuth 2.

OAuth Roles

In OAuth 2, four roles are defined:

  • The Resource Owner, which is the user that is capable of granting access to a protected resource. In this case, the Resource Owner is the end-user and the protected resource is his Facebook data. The Resource owner can chose to grant access to a limited scope. For example only reading rights on his data.
  • The Client, which is the app that wants to use the resource of a user. Here, the client is Spotify. (Generally, the client is registered by a user on the site, which means Spotify probably has a developer account on Facebook registered by a specific Facebook user).
  • The Authorization Server, which is generally the server that hosts the protected user account and issues tokens to the application. Here, the authorization server is Facebook, it issues tokens to Spotify.

Basically, this is how it works

Step 0:

The User (or the resource owner) tries to sign up on Spotify (the Client) via Chrome (called the User-Agent).

Step 1:

Spotify initiates the flow by directing your browser to an authorization endpoint made available by Facebook. Spotify includes in the request its client id (identifier as a Facebook API user), requested scope (for example if it wants to read or write a user’s data) and a redirection URI to which Facebook will send the user-agent back once access is granted.

Step 2:

Facebook authenticates the user and prompts them to authorize or deny Spotify access to their data.

Step 3:

If the user authorizes Spotify to access his data, Facebook redirects the user’s browser to the redirect URI given earlier by Spotify (the account overview page in this case). The redirection URI includes an authorization code.

Step 4:

Spotify can now retrieve an access token from Facebook. Spotify makes a request to Facebook’s API using the authorization code retrieved previously and adds its client id and its client secret in order to be authenticated by Facebook’s authorization server.

Step 5:

Spotify receives an access token. The access token can only be used to access the user’s Facebook data. It usually expires after a while.

Now that you understand the basic workflow, let’s see how to implement a simple python OAuth2 Server (Facebook’s authorization server) using Flask-Oauthlib.

Implementing an OAuth2 authorization flow with Flask-Oauthlib

As we saw, the main goal of an OAuth2 server is to protect a user’s resource.

This tutorial shows you how to implement a simple python OAuth2 server using Flask-Oauthlib.

The code used as an example for this implementation is available on github.

Dependencies and creating a basic flask server

Before getting into the OAuth2 specifics, we need to set up a basic flask server. We need to install the following dependencies first:

  • Flask (version 0.10.1)
  • Flask-SQLAlchemy (version 1.0)
  • Werkzeug (version 0.9.4)
  • Flask-OAuthlib (any version above 0.5.0)

The code for the basic flask server is available here and here.

Notice that on line 19, we registered an OAuth provider by adding the line:

oauth = OAuth2Provider(app)

You should be able to start your server by running python app.py in your terminal.

The Resource Owner

Remember from the first part of the article, this is the end-user. As we are trying to implement the Authorization Server (like Facebook would do), we need a user model.

We can create a very simple user model, by adding this simple SQLAlchemy model, in which we give each user a unique id and username.

We can also add a function to get the user currently, based on the session we are in.

The Client

As specified in the first part of the article, this is the app that wants to access user data (Spotify in our example). Generally, the client is registered on the authorization server by a user. It has a client_id, a client_secret, a list of redirect_uris, a default_redirect_uri and a list of default_scopes. redirect_uris are used to specify to which page of the client app (Spotify), the authorization server (Facebook) should redirect to after the user has granted access to his data. Scopes are used to define to the data the client wants to access and if he only wants to read data or write as well. The client model can be implemented this way.

As you can see, a client is linked to a unique registered user. Later, we will want to load a client from our database so we can add a function used to load a client from our database:

@oauth.clientgetter
def load_client(client_id):
return Client.query.filter_by(client_id=client_id).first()

Notice that we registered this function as a clientgetter by adding a decorator ‘@oauth.clientgetter’. The OAuth2Provider needs several getters registered to use for validation and the clientgetter is one of them.

The Grant

The Grant Token is generated in the authorization flow, it contains the authorization code described in the 3rd step of the authorization flow description. This token is specific is linked to a unique client (and to a unique user) and should be destroyed as soon as the authorization flow is over. It is exchanged for an access_token if the user accepts to grant access to its resource.

You can add the following model for the grant token.

Notice that we included the method delete below so we can delete the Grant Token at the end of the authorization flow.

def delete(self):
db.session.delete(self)
db.session.commit()
return self

As for the client model, we also need to implemented functions to set and get a grant token and to register them as grantsetter and grantgetter

To set a grant token, we can add:


from datetime import datetime, timedelta
@oauth.grantsetter
def save_grant(client_id, code, request, *args, **kwargs):
# decide the expires time yourself
expires = datetime.utcnow() + timedelta(seconds=100)
grant = Grant(
client_id=client_id,
code=code[‘code’],
redirect_uri=request.redirect_uri,
_scopes=’ ‘.join(request.scopes),
user=current_user(),
expires=expires
)
db.session.add(grant)
db.session.commit()
return grant

To set a grant, we need to know the user that is making the request. To do that we can get the unique user connected in the current session. Here the request object is defined in OAuthlib. It contains at least a client model object, a list of scopes, a user model object and a redirect_uri parameter.

The Token

This is the final token that can be used by the client to get the protected resource. This class necessarily contains an access_token, the client id of the client that gets the token, the user model of the user linked to this client id, a list of scopes, an expiring time and a delete function. It can be implemented this way.

Like for the grant token, we need getters and setters for this token to be registered.

To set a token in a database, we can add the following function:


@oauth.tokensetter
def save_token(token, request, *args, **kwargs):
toks = Token.query.filter_by(
client_id=request.client.client_id,
user_id=request.user.id
)
for t in toks:
db.session.delete(t)
expires_in = token.pop(‘expires_in’)
expires = datetime.utcnow() + timedelta(seconds=expires_in)
tok = Token(
access_token=token[‘access_token’],
_scopes=token[‘scope’],
expires=expires,
client_id=request.client.client_id,
user_id=request.user.id,
)
db.session.add(tok)
db.session.commit()
return tok

As you can see, before creating a new token, we make sure that every client has only one token connected to a user.

Routes

Now we can start implementing the routes that will be used by the client (Spotify).

The first one should be /oauth/authorize.

The GET method to this route will render an authentication page if the user is not authenticated or a page for the resource owner to authorize the client to access his data. This is step 2 in the authentication flow described in the first part of the article.

The POST method to this route will return a boolean that specifies whether the user granted access to the client. This will be used as part of step 3 of the authorization flow.

If you want to implement the whole authentication flow, you can also add the html for the authorization page in a file in a template folder, like here.

The second route should be a token handler, a route made to get an access token. This route will be used during step 4, once the client has an authorization code and wants to exchange it against an access token.

The code for this route is:

@app.route(‘/oauth/token’, methods=[‘POST’])
@oauth.token_handler
def access_token():
return None

As you can see, we don’t have much to do thanks to the token_handler decorator provided by Flask-OAuthlib.

You will also need a home page route (/) with its HTML.

For demo purposes, you can add:

- A route /client that generates a new client id and secret. This is supposed to be generated when the client registers to use the authorization server’s API. In our example, the client_id and client_secret was generated when Spotify registered its app to Facebook’s API.

- A route /api/me that fetches user data (here his username) and protect it using the decorator @oauth.require_oauth provided by Flask-Oauthlib:

@app.route(‘/api/me’)
@oauth.require_oauth()
def me():
user = request.oauth.user
return jsonify(username=user.username)

We’re finally ready to test our server. To test it properly, it is better to have a client side server. You can use the client.py file in the github repository.

  1. Start your Authorization server by running ‘python app.py’
  2. Visit http://localhost:5000 and authenticate yourself

3. Generate a client_id and a client_secret by visiting http://localhost:5000/client. (If you used the client.py file as a client side server, you should copy the generated client id and secret in the file and run ‘python client.py’).

4) Now try to authorize the client’s app using:

http://localhost:5000/oauth/authorize?response_type=code&client_id=[YOUR_CLIENT_ID]&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000%2Fauthorized&scope=email (by replacing the client_id in the URL by the one you just generated).

You should see something similar to that:

This is the step 2 of the authorization flow described at the beginning of the article. The client gets back an authorization code.

5) Once the user chooses to grant access, a post is sent to our oauth/authorize route, as specified in step 3 of the authorization process.

The client side server will make a request to our /token using the authorization code route to ask for an access_token. This is step 4 of the authorization process. After having granted access, you should be redirected to a page similar to this one:

Now using this access token, the client can have access to the protected resource, my username :). This is step 5 of the authorization process.

We successfully implemented an OAuth2 authorization server! You should have a pretty good idea of how OAuth2 works.

Actually, OAuth2 describes several different authorization flows using different grant types. The one implemented here is the “Authorization code grant type” which is the most commonly used because it is optimized for server-side applications. If you want to know more about this grant type, the other grant types described by the OAuth2 framework, or need assistance for the implementation, please refer to the further reading material below.

Further reading material

--

--