# Adding Authentication to Your FARM Stack App

>If you have not read my Introduction to FARM stack tutorial, I would urge you to do that now and then come back. This guide assumes you have already read and understood the previous article so some things might be confusing or opaque if you have not.

An important part of many web applications is user management, which can be complex with lots of different scenarios to cover: registration, logging in, logging out, password resets, protected routes, and so on.  In this tutorial, we will look at how you can integrate the FastAPI Users package into your FARM stack.

## Prerequisites

-   Python 3.9.0
-   A MongoDB Atlas cluster. Follow the "Get Started with Atlas" guide to create your account and MongoDB cluster. Keep a note of your database username, password, and connection string as you will need those later.
-   A MongoDB Realm App connected to your cluster. Follow the "Create a Realm App (Realm UI)" guide and make a note of your Realm App ID.

## Getting Started

Let's begin by cloning the sample code source from GitHub

``` shell
git clone git@github.com:mongodb-developer/FARM-Auth.git
```

Once you have cloned the repository, you will need to install the dependencies. I always recommend that you install all Python dependencies in a virtualenv for the project. Before running pip, ensure your virtualenv is active. The requirements.txt file is within the back end folder.

``` shell
cd FARM-Auth/backend
pip install -r requirements.txt
```

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.

You'll need two new configuration values for this tutorial. To get them, log into Atlas and create a new Realm App by selecting the Realm tab at the top of the page, and then clicking on "Create a New App" on the top-right of the page.

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 `REALM_APP_ID` value.

    

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:

``` shell
python -c 'import secrets; print(secrets.token_hex(32))'
```

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).

``` shell
export DEBUG_MODE=True
export DB_URL="mongodb+srv://:@/?retryWrites=true&w=majority"
export DB_NAME="farmstack"
export JWT_SECRET_KEY=""
export REALM_APP_ID=""
```

Set these values appropriately for your environment, ensuring that `REALM_APP_ID` and `JWT_SECRET_KEY` use the values from above. Remember, anytime you start a new terminal session, you will need to set these environment variables again. I use direnv to make this process easier. Storing and loading these values from a .env file is another popular alternative.

The final step is to start your FastAPI server.

``` shell
uvicorn main:app --reload
```

Once the application has started, you can view it in your browser at .

    

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 `Users` package. 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 Tasks` route, 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/register` and `/auth/jwt/login` routes to create and authenticate as a new user. Once you are successfully logged in, try accessing the `List Tasks` route again. It should now grant you access and return an HTTP status of 200. Use the Atlas UI to check the new `farmstack.users` collection and you'll see that there's now a document for your new user.

## Integrating FastAPI Users

The routes and models for our users are within the `/backend/apps/user` folder. Lets walk through what it contains.

### The User Models

The FastAPI `Users` package includes some basic `User` mixins with the following attributes:

-   `id` (`UUID4`) – Unique identifier of the user. Default to a UUID4.
-   `email` (`str`) – Email of the user. Validated by `email-validator`.
-   `is_active` (`bool`) – Whether or not the user is active. If not, login and forgot password requests will be denied. Default to `True`.
-   `is_superuser` (`bool`) – Whether or not the user is a superuser.  Useful to implement administration logic. Default to `False`.

``` python
from fastapi_users.models import BaseUser, BaseUserCreate, BaseUserUpdate, BaseUserDB

class User(BaseUser):
    pass

class UserCreate(BaseUserCreate):
    pass

class UserUpdate(User, BaseUserUpdate):
    pass

class UserDB(User, BaseUserDB):
    pass
```

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 User Routers

The FastAPI Users routes can be broken down into four sections:

-   Registration
-   Authentication
-   Password Reset
-   User CRUD (Create, Read, Update, Delete)

``` python
def get_users_router(app):
    users_router = APIRouter()

    def on_after_register(user: UserDB, request: Request):
        print(f"User {user.id} has registered.")

    def on_after_forgot_password(user: UserDB, token: str, request: Request):
        print(f"User {user.id} has forgot their password. Reset token: {token}")

    users_router.include_router(
        app.fastapi_users.get_auth_router(jwt_authentication),
        prefix="/auth/jwt",
        tags="auth"],
    )
    users_router.include_router(
        app.fastapi_users.get_register_router(on_after_register),
        prefix="/auth",
        tags=["auth"],
    )
    users_router.include_router(
        app.fastapi_users.get_reset_password_router(
            settings.JWT_SECRET_KEY, after_forgot_password=on_after_forgot_password
        ),
        prefix="/auth",
        tags=["auth"],
    )
    users_router.include_router(
        app.fastapi_users.get_users_router(), prefix="/users", tags=["users"]
    )

    return users_router
```

You can read a detailed description of each of the routes in the [FastAPI Users' documentation, but there are a few interesting things to note in this code.

#### The on_after Functions

These functions are called after a new user registers and after the forgotten password endpoint is triggered.

The `on_after_register` is a convenience function allowing you to send a welcome email, add the user to your CRM, notify a Slack channel, and so on.

The `on_after_forgot_password` is 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.

#### The get_users_router Wrapper

In order to create our routes we need access to the `fastapi_users` object, which is part of our `app` object. Because app is defined in `main.py`, and `main.py` imports these routers, we wrap them within a `get_users_router` function to avoid creating a cyclic import.

## Creating a Custom Realm JWT

Currently, Realm's user management functionality is only supported in the various JavaScript SDKs. However, Realm does support custom JWTs for authentication, allowing you to use the over the wire protocol support in the Python drivers to interact with some Realm services.

The available Realm services, as well as how you would interact with them via the Python driver, are out of scope for this tutorial, but you can read more in the documentation for Users & Authentication, Custom JWT Authentication, and MongoDB Wire Protocol.

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 `backend/apps/user/auth.py` we define `MongoDBRealmJWTAuthentication` which inherits from the FastAPI Users' `CookieAuthentication` class.

``` python
class MongoDBRealmJWTAuthentication(CookieAuthentication):
    def __init__(self, *args, **kwargs):
        super(MongoDBRealmJWTAuthentication, self).__init__(*args, **kwargs)
        self.token_audience = settings.REALM_APP_ID

    async def _generate_token(self, user):
        data = {
            "user_id": str(user.id),
            "sub": str(user.id),
            "aud": self.token_audience,
            "external_user_id": str(user.id),
        }
        return generate_jwt(data, self.lifetime_seconds, self.secret, JWT_ALGORITHM)
```

Most of the authentication code stays the same. However we define a new `_generate_token` method which includes the additional data Realm expects.

## Protecting the Todo App Routes

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.py` and are almost identical to those found in the Introducing FARM Stack tutorial, with one addition. Each router now depends upon `app.fastapi_users.get_current_active_user`.

``` python
@router.post(
    "/",
    response_description="Add new task",
)
async def create_task(
    request: Request,
    user: User = Depends(app.fastapi_users.get_current_active_user),
    task: TaskModel = Body(...),
):
    task = jsonable_encoder(task)
    new_task = await request.app.db"tasks"].insert_one(task)
    created_task = await request.app.db["tasks"].find_one(
        {"_id": new_task.inserted_id}
    )

    return JSONResponse(status_code=status.HTTP_201_CREATED, content=created_task)
```

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.

## Creating Our FastAPI App and Including the Routers

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.

``` python
@app.on_event("startup")
async def configure_db_and_routes():
    app.mongodb_client = AsyncIOMotorClient(
        settings.DB_URL, uuidRepresentation="standard"
    )
    app.db = app.mongodb_client[settings.DB_NAME]

    user_db = MongoDBUserDatabase(UserDB, app.db["users"])

    app.fastapi_users = FastAPIUsers(
        user_db,
        [jwt_authentication],
        User,
        UserCreate,
        UserUpdate,
        UserDB,
    )

    app.include_router(get_users_router(app))
    app.include_router(get_todo_router(app))
```

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.

``` python
@app.on_event("shutdown")
async def shutdown_db_client():
    app.mongodb_client.close()
```

The shutdown event handler does not change. It is still responsible for closing the connection to our database.

## Wrapping Up

In this tutorial we have covered one of the ways you can add user authentication to your [FARM stack application.  There are several other packages available which you might also want to try. You can find several of them in the awesome FastAPI list.

Or, for a more in-depth look at the FastAPI Users package, please check their documentation.

>If you have questions, please head to our developer community website where the MongoDB engineers and the MongoDB community will help you build your next big idea with MongoDB.