Adding Authentication to Your FARM Stack App
Rate this tutorial
- Python 3.9.0
It may take a few moments to download and install your dependencies. This is normal, especially if you have not installed a particular package before.
Configure the Realm app to connect to your existing cluster:
You should see your Realm app's ID at the top of the page. Copy it and keep it somewhere safe. It will be used for your application's
Click on the "Authentication" option on the left-hand side of the page. Then select the "Edit" button next to "Custom JWT Authentication". Ensure the first option, "Provider Enabled" is set to "On". Check that the Signing Algorithm is set to "HS256". Now you need to create a signing key, which is just a set of 32 random bytes. Fortunately, Python has a quick way to securely create random bytes! In your console, run the following:
Running that line of code will print out some random characters to the console. Type "signing_key" into the "Signing Key (Secret Name)" text box and then click "Create 'signing_key'" in the menu that appears underneath. A new text box will appear for the actual key bytes. Paste in the random bytes you generated above. Keep the random bytes safe for the moment. You'll need them for your application's "JWT_SECRET_KEY" configuration value.
Now you have all your configuration values, you need to set the following environment variables (make sure that you substitute your actual credentials).
Set these values appropriately for your environment, ensuring that
JWT_SECRET_KEYuse the values from above. Remember, anytime you start a new terminal session, you will need to set these environment variables again. I use to make this process easier. Storing and loading these values from a is another popular alternative.
The final step is to start your FastAPI server.
You may notice that we now have a lot more endpoints than we did in the FARM stack Intro. These routes are all provided by the FastAPI
Userspackage. I have also updated the todo app routes so that they are protected. This means that you can no longer access these routes, unless you are logged in.
If you try to access the
List Tasksroute, for example, it will fail with a 401 Unauthorized error. In order to access any of the todo app routes, we need to first register as a new user and then authenticate. Try this now. Use the
/auth/jwt/loginroutes to create and authenticate as a new user. Once you are successfully logged in, try accessing the
List Tasksroute again. It should now grant you access and return an HTTP status of 200. Use the Atlas UI to check the new
farmstack.userscollection and you'll see that there's now a document for your new user.
The routes and models for our users are within the
/backend/apps/userfolder. Lets walk through what it contains.
UUID4) – Unique identifier of the user. Default to a UUID4.
str) – Email of the user. Validated by
bool) – Whether or not the user is active. If not, login and forgot password requests will be denied. Default to
bool) – Whether or not the user is a superuser. Useful to implement administration logic. Default to
You can use these as-is for your User models, or extend them with whatever additional properties you require. I'm using them as-is for this example.
The FastAPI Users routes can be broken down into four sections:
- Password Reset
- User CRUD (Create, Read, Update, Delete)
These functions are called after a new user registers and after the forgotten password endpoint is triggered.
on_after_registeris a convenience function allowing you to send a welcome email, add the user to your CRM, notify a Slack channel, and so on.
on_after_forgot_passwordis where you would send the password reset token to the user, most likely via email. The FastAPI Users package does not send the token to the user for you. You must do that here yourself.
In order to create our routes we need access to the
fastapi_usersobject, which is part of our
appobject. Because app is defined in
main.pyimports these routers, we wrap them within a
get_users_routerfunction to avoid creating a cyclic import.
Realm expects the custom JWT tokens to be structured in a certain way. To ensure the JWT tokens we generate with FastAPI Users are structured correctly, within
MongoDBRealmJWTAuthenticationwhich inherits from the FastAPI Users'
Most of the authentication code stays the same. However we define a new
_generate_tokenmethod which includes the additional data Realm expects.
Now we have our user models, routers, and JWT token ready, we can modify the todo routes to restrict access only to authenticated and active users.
The todo app routers are defined in
backend/apps/todo/routers.pyand are almost identical to those found in the Introducing FARM Stack tutorial, with one addition. Each router now depends upon
Because we have declared this as a dependency, if an unauthenticated or inactive user attempts to access any of these URLs, they will be denied. This does mean, however, that our todo app routers now must also have access to the app object, so as we did with the user routers we wrap it in a function to avoid cyclic imports.
The FastAPI app is defined within
backend/main.py. This is the entry point to our FastAPI server and has been quite heavily modified from the example in the previous FARM stack tutorial, so let's go through it section by section.
This function is called whenever our FastAPI application starts. Here, we connect to our MongoDB database, configure FastAPI Users, and include our routers. Your application won't start receiving requests until this event handler has completed.
The shutdown event handler does not change. It is still responsible for closing the connection to our database.