Basic guide for FastAPI framework from A – Z (Part 2)

Tram Ho

Introduction

Today I will study with you to continue the FastAPi framework, specifically about the security of this framework.

Security

There are many ways to deal with security, authentication, or authorization today. This is a difficult topic, and many programmers spend a lot of effort writing code. This article will encapsulate the framework of the framework, introducing tools to help you implement security functions quickly and easily.

Security standards

This part goes quickly, by default you know and then go.

OAuth2

As an authorization protocol standard launched in October 2012, used in almost every application (web, mobile), allowing users to provide personal information by third-party applications, also Used to provide the mechanism for user authentication.

OAuth 1

This is also a protocol standard, but different from OAuth2, it is more complicated because of the extra encryption mechanism for communication. At this point, this standard is no longer in use.

OpenID

Older than the two above, this standard requires the user to provide a username and password. Generally there is an OpenID provider, we account on it, this account can access websites using OpenID’s authentication mechanism.

 

OpenID Connect

Based on OAuth2, which is a layer above the OAuth2 protocol.

Security mechanism of FastAPI

Due to being based on OpenAPI, FastAPI inherits the security flow of OpenAPI

  • apiKey : just the key, can come from the query param, header or cookie.
  • http : HTTP authentication system, including:
    • bearer: header param with the value of a token (inherits from OAuth2)
    • HTTP Basic authentication
    • HTTP Digest authentication
  • oauth2
  • openIdConnect

Practice

This area is for illustration purpose, the code is not yet complete

Suppose now I have a backend API on the server, 1 frontend on Android, the backend and the frontend are connected but missing a lock, otherwise any request can be easily passed through the frontend to the backend. The simplest I use username and password, here I use OAuth2 to build.

In the main.py function, add OAuth2’s login username / password mechanism. When running the app, check in the doc we have 1 url api /items/ , the top right corner has an Authorize box where to enter username / password.

Applied in practice when there is a frontend:

  • User will login username / password on frontend
  • Frontend (browser for example) sends username and password to the url api (tokenUrl = “token”)
  • The API checks the username and password, and returns 1 token (the above code does not have this paragraph, the following paragraph will mention)
    • Token is a string of characters used to authenticate users.
    • Token exists for a period, this period is set up by the backend side
  • The Frontend then stores the token somewhere temporarily (cookies, ram, …).

Create an instance of OAuth2PasswordBearer oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") . Here I am not creating the endpoint but defining the path / token for the user to take the token later, which is quite important when working with a proxy.

If you want to return the token in the response body. Ok pass the param to the read_items function, the token variable is of type string dependent on the instance oauth2_scheme: token: str = Depends(oauth2_scheme) . This parameter will look over the request at the Authorization header, check if there are tokens or not and return, if not, it will report error 401 UNAUTHORIZED .

OAuth2 with Password and Bearer

As the title, I will use the simplest authentication method to create a lock, which is to use username / password, so I need to validate when the user enters username / password. Let’s say I have a dict user whose username is admin and password 123456 . When the user logs in I have to compare whether the usernames are the same or if the two passwords are the same.

Ok here there are 2 cases of entering incorrectly => returning error code 400. As shown below, I entered the wrong name.

The correct case would look like this:

If I want more security, I have to customize the password I receive, such as adding a hash code function, so that if the hacker has hacked and lost the password, he can only get the hashed password.

Get current user

Before going into JWT, as you know the Pratice section above I have briefly introduced the security mechanism of fastapi is follow oauth2 so I have to import OAuth2PasswordBearer , api requires login but where is the username and password? To solve this problem I combined OAuth2PasswordBearer and OAuth2PasswordRequestForm . The latter gives the user a form to login (url api), the result is to create a token, notifying that there is a token with this type bearer, and the one that first detects the token will allow the user to return information. It sounds a bit confusing but it’s okay I will explain the code to you.

The overall code is here, I will separate each part.

The user declaration has the following information

Declare User model inherits from BaseModel of pydantic library.

This login function creates a url api token , when the user logs in, it sends a request to the server, the request’s information is contained in the variable form_data . Based on the username attribute of form_data, if the value is correct, we will get 1user dict, otherwise return the error 400. Again consider the password attribute of form_data, if false also returns error 400, otherwise return the token is the name of the person use (you can return arbitrary tokens, not necessarily a username).

Ok already have the token, we need to pass through the Authorize box in the top right corner. We continue to validate the token with the get_current_user function. This function has a parameter passed as a string token, depending on the instance oauth2_scheme declared above. Token is then passed to the fake_decode_token function, this function is responsible for decode token and return the original form (if you encode with some encryption algorithm, then you must use that algorithm at decode), but here I just code for example so there is no tokenization at all. In the function fake_decode_token call get_user function to see if the token (here is username) is in db (here I just declare a variable dict, you can customize it) or not, if so, return the user. That validate is just that simple, if you want to be complex, you can customize these functions.

Correct result when returned

Wrong result returned

Finally, declare the read_users_me function to create api /users/me , the variable current_user depends on the get_current_user function.

OAuth2 with Password (hashing), Bearer with JWT tokens

Briefly talk about JWT, short for JSON Web Tokens , is a string of characters, the encoding form of Json. For example:

Each string is separated by a period. The above has 3 strings corresponding to:

The signature aka the signature to pass is 1 token.

The libraries need to install: jose (to sing the JWT token, passlib (hash password)

This section focuses on “hashing” and validating passwords, so I added some libraries.

Declare user dict

Call the required instance

First I need the functions used to authenticate the user with the variables passed as user, username, password. If the username does not match user["email"] or the password does not match the hashed user["password"] , False is returned, otherwise user (True) is returned.

To encrypt the password, I call get_password_hash, instance pwd_context algorithms Bcrypt to encrypt passwords.

To verify the password, I compare the passed password variable and the user’s password in db using the verify_password function. The comparison mechanism does not care because the pwd_context instance support is available.

Next, if you pass the validate username / password, you will receive the return token, feel familiar, the above code is already coded, and the following function will add security mechanisms such as encryption, time of existence. The purpose of the function is to encode 1 json data (can be user information or something else, depending on you).

Create a copy data variable called to_encode . Initialize the expire variable has a lifetime of 15 minutes from the present time. The variable to_encode will update the expire . Time for me to use the jose library. This library encodes json dict to_encode to JWT with Secret Key and HS256 algorithm. The variable ACCESS_TOKEN_EXPIRE_MINUTES is a time of about 30 minutes, if not passed into the create_access_token function, the default is 15 minutes.

After all, where will the authenticate_user and create_access_token functions be called? Of course in the login function already. This function accepts requests from the form, the request goes through the authenticate_user function to validate. If the result is false, then return error 401, otherwise create a token with payload of email. and return that token. Simple as that.

If you notice that the api usually requires the api key in the header, so I will also add an api url that requires the access token. The text_to_speech function has a variable passed as a Header parameter, this variable will be decode to get the payload, the decode uses the algorithm and the Secret Key is identical to the encode. Validate if the two users are the same, if the same is “do something”, return the result, otherwise return the error 400.

Synthesize all the code blocks above:

Page 1

Page 2

Page 3: Copy the access token from page 2 and put it in the header box

Conclude

Done there, probably will have part 3 because this framework has a lot to play with. Anyone passing by can give an upvote so that I have the motivation to write. Thanks for reading until here

Reference

https://fastapi.tiangolo.com/tutorial/security/

https://jwt.io/

https://oauth.net/2/

https://dzone.com/articles/open-id-connect-authentication-with-oauth20-author

https://medium.com/@greekykhs/whats-the-difference-oauth-1-0-and-oauth-2-0-9f1d22e06963

https://en.wikipedia.org/wiki/OAuth

Share the news now

Source : Viblo