Skip to content

Instantly share code, notes, and snippets.

@tsatsujnr139
Created June 27, 2021 16:38
Show Gist options
  • Save tsatsujnr139/b0a6de0a30040db9f74f993d30b13298 to your computer and use it in GitHub Desktop.
Save tsatsujnr139/b0a6de0a30040db9f74f993d30b13298 to your computer and use it in GitHub Desktop.

Implementing a Role Based Access Control Service with FastAPI and Postgres

Created: Apr 08, 2021 11:27 AM Created by: Tsatsu Adogla-Bessa Tags: Authentication, Authorization, FastAPI, Postgres

Introduction

FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints. It boasts of features like;

  • Speed : Very high performance, on par with NodeJS and Go (thanks to Starlette and Pydantic). One of the fastest Python frameworks available.

  • Fast to code: Increase the speed to develop features by about 200% to 300%. *

  • Fewer bugs: Reduce about 40% of human (developer) induced errors. *

  • Intuitive: Great editor support.  everywhere. Less time debugging.

    Completion

  • Easy: Designed to be easy to use and learn. Less time reading docs.

  • Short: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.

  • Robust: Get production-ready code. With automatic interactive documentation.

  • Standards-based: Based on (and fully compatible with) the open standards for APIs: OpenAPI (previously known as Swagger) and JSON Schema.

FastAPI has been out for a little over 2 years and having been impressed with what is has so far at such a young age as compared to the other mature frameworks, my aim was to build a basic skeletal a authentication framework using FastAPI, which can be plugged into any distributed system with a few tweaks to suit any use case.

Authentication & Authorization Primer

Authentication and authorization might sound similar, but they are distinct security processes in the world of identity and access management (IAM). In simple terms, authentication confirms that users are who they say they are. Authorization on the other hand gives those users permission to access a resource.In secure environments, authorization must always follow authentication. Users should first prove that their identities are genuine before an organization’s system grant them access to the requested resources.

Tools

The main tools used for implementation are;

  • fastapi - The web framework used
  • docker - For application containerization
  • poetry- For dependency management
  • alembic - For database migrations
  • sqlalchemy - The Object Relation Mapper (ORM) used

Application Structure

The structure for this service is based on a recommended structure for organizing larger applications in the FastAPI documentation. The high level overview of the application can be seen below

Implementing%20a%20Role%20Based%20Access%20Control%20Service%20w%2032e85c137078428eacf8f206b5a19fc7/Screenshot_2021-04-20_at_9.48.05_PM.png

app structure

We'll be focusing on the structure of the app directory above which houses the actual code and authentication logic. Next we'll go through an overview of the various modules in the app folder.

  • api - Houses the various routes available in the services and serves as the entry point to service.

    Implementing%20a%20Role%20Based%20Access%20Control%20Service%20w%2032e85c137078428eacf8f206b5a19fc7/Screenshot_2021-04-20_at_9.43.28_PM.png

    api folder contents

    Implementing%20a%20Role%20Based%20Access%20Control%20Service%20w%2032e85c137078428eacf8f206b5a19fc7/Screenshot_2021-04-20_at_9.36.19_PM.png

    application structure.

  • core - This folder contains the settings and some security utils that are reused throughout the application.

    Implementing%20a%20Role%20Based%20Access%20Control%20Service%20w%2032e85c137078428eacf8f206b5a19fc7/Screenshot_2021-04-20_at_9.56.12_PM.png

    core folder contents

  • crud - As the folder name suggests this folder contains the code for performing various database operations related to the models in the app. The CRUD (Create, Read, Update, Delete) operations for each model is organized in it's own file.

    Implementing%20a%20Role%20Based%20Access%20Control%20Service%20w%2032e85c137078428eacf8f206b5a19fc7/Screenshot_2021-04-20_at_10.04.34_PM.png

    crud folder contents

  • db - This houses the database configuration and a script for setting up some initial data in the database.

    Implementing%20a%20Role%20Based%20Access%20Control%20Service%20w%2032e85c137078428eacf8f206b5a19fc7/Screenshot_2021-04-20_at_10.15.40_PM.png

    db folder contents

  • models - Contains the model definitions for the database entities

    Implementing%20a%20Role%20Based%20Access%20Control%20Service%20w%2032e85c137078428eacf8f206b5a19fc7/Screenshot_2021-04-20_at_10.25.18_PM.png

    models folder content

  • schemas - This folder contains pydantic models which FastAPI used for the definitions of data models and payloads to the various api's

Authentication

FastAPI provides several tools to help you deal with Security easily, rapidly, in a standard way, without having to learn all the security specifications. For this application we'll be making use of the OAuth2 implementation provided by the framework. The FastAPI documentation has a very detailed and comprehensive tutorial which walks you through all the features it provides so this article will mostly provide a high level overview of the implementation. A repository for the code will also be linked at the end of the article.

For this post we're going to be focusing on 3 main aspects which are core to all good auth systems and how there are implemented using FastAPI.

  • User management
  • Login API's
  • Access Control/Authorization

User Management

The application exposes a few endpoints that allow you to manage users on your platform. This is usually the first step in your auth workflow; getting the information on user who are able to use your platform. This is taken further in our use case by allowing users to be able to update their details later on. Some of the endpoints are secured and require specific roles to access them and we'll look into that later in the article. You can review the endpoints provided below. In the code repository this file is located in the api directory of the application.

https://gist.github.com/tsatsujnr139/1c65a1bb47ef21bba1277e101c767cd5

The payloads and response objects for each of the API's is defined the schema for the user.

https://gist.github.com/tsatsujnr139/b6973283c065f206f623823589655e89

Another component of the user management api's is the database model which describes the database structure for the user table in the database. Here we make use of sqlalchemy to define how to create the table and fields in PostgreSQL. The user model links a registered user to a role and account which help with authorization by providing information to let you know which account details the user is allowed to access if they have the required authorization.

https://gist.github.com/tsatsujnr139/61b621dfbdb112a7f093e76de383abc4

Login/Authentication

The next step once we have the user information stored is to authenticate them when they make a request for a resource. We do that using Json Web Tokens (JWTs). When the wants to login their email and password are sent to the backend and compared against what is stored in the database. If the credentials match a token is returned which will be sent in subsequent requests to access any API's in your ecosystem to ensure the request is coming from an authenticated user. The validity of the token can be configured by changing the value of the ACCESS_TOKEN_EXPIRE_MINUTES in the settings file. We'll look at the authentication

https://gist.github.com/tsatsujnr139/2a155f591588a6b2b940b4e956f89719

The most interesting method we want to take note of is the create_access_token method used in the login_access_token route. This method takes in the payload to be encrypted for the token and the validity duration of the token. The token payload can always be updated to include more/ less details but it currently stores the user_id, role and account_id of the user. These details will be sent in each request and so can be decrypted and used to authorize the user and only show them data for their account. It's worthy to note that just because the token can contain as much data as you want you should be weary of storing sensitive details in the token. This is so that on the off chance an unscrupulous person gets access to your secret key used to encrypt your token and decrypts any token, they won't have access to sensitive user information.

https://gist.github.com/tsatsujnr139/50fd1f2358bb93c91e9cdc7fb8ebda0f

Access Control & Authorization

When a subsequent request from an user reaches your application after login, the authorization process involves checking decrypting the token to authenticate the user and then checking if they have access to the resource they are trying to access. Let's take a look at the read_users api to examine how this is done.

https://gist.github.com/tsatsujnr139/8dab4e7ddc13579163374dc9cb5b4d5e

The read_users api here is marked as a GET using the router.get decorator provided by FastAPI. The function takes in dependencies and query params / path variables that the API expects. in this case the query_params are the skip and limit which are using for pagination and specifying the number of records to retrieve from the database. The db parameter indicates that this API depends on the database to perform it's operation. Now we take a look at the current_user dependency. This paramater makes use of a function which is what is used to authenticate the user and ensure the user has the required role that is permitted to access this API before the request is processed. The Security function provided by FastAPI allows us to make use of OAuth Scopes to determine what the user is allowed to do. In our case we use that to define the role of the user which we saw is stored in the token. A few roles are defined as constants in the role.py file in the constants module and can be expanded to include any roles needed for your use case.

https://gist.github.com/tsatsujnr139/aebf46804d0d328528bddf46a832852a

Each role defined has a name a description just to give some context as to what that role is intended to do. All scopes supported must be defined and included in the OAuth2PasswordBearer definition which is provided by FastAPI. We can then apply these scopes to the API's depending on who we would like to have access to them like in our read_users API above.

The last thing we will be taking a look at is the authorization flow which is executed when we attempt to get fill the current_user dependency passed to the read_user api

https://gist.github.com/tsatsujnr139/f6031075eed4b62a630f12e82341f85a

If the API requires any specific roles they will passed as scopes to the get_current_active_user function. Most of the heavy lifting here is done by the get_current_user function. If no scopes are required then the token passed in the request is decrypted and the user_id stored in the token is used to retrieve the user. If the user is not found, the token is invalid or expired an exception is thrown indicating the error. If scopes are applied, after the token is decrypted, we check to ensure the role in the token matches the role required to access the API. If the user does not have the required role, an unauthorized exception is thrown.

Conclusion and Next Steps

FastAPI is a great option if you're looking to quickly build light micro-services for your distributed system and this overview can be used as a good basis for a role based access control authentication service. The function to authenticate and authorize a user can be replicated in as many micro-services to control access to the API's exposed in those services. You can find the checkout the complete project on github here with instructions on how to get it up and running so you can test it out in your development environment. It also includes detailed tests for the various modules in the code to ensure everything works as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment