> For the complete documentation index, see [llms.txt](https://fusionauth.io/docs/llms.txt)

# Python Django | FusionAuth Docs

Quickstart integration of Python Django web application with FusionAuth.

# Python Django

[Edit on GitHub](https://github.com/FusionAuth/fusionauth-site/blob/main/astro/src/content/docs/get-started/quickstarts/web/quickstart-python-django-web.mdx)

[View Markdown](https://fusionauth.io/docs/get-started/quickstarts/web/quickstart-python-django-web.md)

In this quickstart, you are going to build an application with Python and Django and integrate it with FusionAuth. You'll be building it for [ChangeBank](https://www.youtube.com/watch?v=CXDxNCzUspM), a global leader in converting dollars into coins. It'll have areas reserved for users who have logged in as well as public facing sections.

The Docker Compose file and source code for a complete application are available at [https://github.com/FusionAuth/fusionauth-quickstart-python-django-web](https://github.com/FusionAuth/fusionauth-quickstart-python-django-web).

## Prerequisites[#](#prerequisites)

For this Quickstart, you'll need:

*   [Python3.8](https://www.python.org/downloads/) or later.
*   [Docker](https://www.docker.com), which is the quickest way to start FusionAuth. (There are [other ways](https://fusionauth.io/docs/get-started/download-and-install.md)).

## General Architecture[#](#general-architecture)

Here's a typical application login flow before FusionAuth:

```mermaid
sequenceDiagram
    participant User
    participant App as Application

    User ->> App : View Homepage
    User ->> App : Click Login Link
    App ->> User : Show Login Form
    User ->> App : Fill Out and Submit Login Form
    App ->> App : Authenticates User
    App ->> User : Display User's Account or Other Info
```

Request flow during login before FusionAuth

And here's the same application login flow after introducing FusionAuth:

```mermaid
sequenceDiagram
    participant User
    participant App as Application
    participant FusionAuth

    User ->> App : View Homepage
    User ->> App : Click Login Link (to FusionAuth)
    User ->> FusionAuth : View Login Form
    FusionAuth ->> User : Show Login Form
    User ->> FusionAuth : Fill Out and Submit Login Form
    FusionAuth ->> FusionAuth : Authenticates User
    FusionAuth ->> User: Go to Redirect URI
    User ->> App: Request the Redirect URI
    App ->> FusionAuth : Is User Authenticated?
    FusionAuth ->> App : User is Authenticated
    App ->> User : Display User's Account or Other Info
```

Request flow during login after FusionAuth

In general, you are introducing FusionAuth in order to normalize and consolidate user data. This helps make sure it is consistent and up-to-date as well as offloading your login security and functionality to FusionAuth.

## Getting Started[#](#getting-started)

Start with getting FusionAuth up and running and creating a new Django application.

### Clone The Code[#](#clone-the-code)

First, grab the code from the repository and change into that directory.

```plaintext
git clone https://github.com/FusionAuth/fusionauth-quickstart-python-django-web.git
cd fusionauth-quickstart-python-django-web
```

All shell commands in this guide can be entered in a terminal in this folder. On Windows, you need to replace forward slashes with backslashes in paths.

The files you'll create in this guide already exist in the `complete-application` folder, if you prefer to copy them.

### Run FusionAuth Via Docker[#](#run-fusionauth-via-docker)

You'll find a Docker Compose file (`docker-compose.yml`) and an environment variables configuration file (`.env`) in the root directory of the repo.

Assuming you have Docker installed, you can stand up FusionAuth on your machine with the following.

```shell-session
docker compose up -d
```

Here you are using a bootstrapping feature of FusionAuth called [Kickstart](https://fusionauth.io/docs/get-started/download-and-install/development/kickstart.md). When FusionAuth comes up for the first time, it will look at the `kickstart/kickstart.json` file and configure FusionAuth to your specified state.

If you ever want to reset the FusionAuth application, you need to delete the volumes created by Docker Compose by executing `docker compose down -v`, then re-run `docker compose up -d`.

FusionAuth will be initially configured with these settings:

*   Your client Id is `E9FDB985-9173-4E01-9D73-AC2D60D1DC8E`.
*   Your client secret is `super-secret-secret-that-should-be-regenerated-for-production`.
*   Your example username is `richard@example.com` and the password is `password`.
*   Your admin username is `admin@example.com` and the password is `password`.
*   The base URL of FusionAuth is `http://localhost:9011/`.

You can log in to the [FusionAuth admin UI](http://localhost:9011/admin) and look around if you want to, but with Docker and Kickstart, everything will already be configured correctly.

If you want to see where the FusionAuth values came from, they can be found in the [FusionAuth app](http://localhost:9011/admin). The tenant Id is found on the Tenants page. To see the Client Id and Client Secret, go to the Applications page and click the `View` icon under the actions for the ChangeBank application. You'll find the Client Id and Client Secret values in the `OAuth configuration` section.

The `.env` file contains passwords. In a real application, always add this file to your `.gitignore` file and never commit secrets to version control.

### Create A Basic Django Application[#](#create-a-basic-django-application)

Next, you'll set up a basic Django project with a single app. While this guide builds a new Django project, you can use the same method to integrate your existing project with FusionAuth.

#### Set Up Your Environment[#](#set-up-your-environment)

You should work in a virtual environment for this.

```shell
python3 -m venv venv && source venv/bin/activate
```

Using `venv` isolates Python dependencies for this project locally so that any dependencies used by other projects on your machine are not affected.

Create a `requirements.txt` file to list the project dependencies with the following content.

```shell
fusionauth-client==1.47.0
Authlib==1.2.0
python-dotenv==1.0.0
requests==2.31.0
Django==3.2.15
mozilla-django-oidc==2.0.0
```

Then install the dependencies into your virtual environment.

```shell
pip install -r requirements.txt
```

#### Create The Application[#](#create-the-application)

Create the default Django starter project.

```shell
django-admin startproject mysite
```

Now create a `.env` file in your `mysite` folder and add the following to it (note that this is a different environment file to the one in the root folder used by Docker for FusionAuth).

```shell
CLIENT_ID=e9fdb985-9173-4e01-9d73-ac2d60d1dc8e
CLIENT_SECRET=super-secret-secret-that-should-be-regenerated-for-production
ISSUER=http://localhost:9011
APP_SECRET_KEY=0386ffa9-3bff-4c75-932a-48d6a763ce77
```

## Authentication[#](#authentication)

Create an app within your `mysite` project.

```shell
cd mysite
python manage.py startapp app
cd ..
```

Now you will work from the top down to set up authentication URLs in the project `settings.py`, then in the application `urls.py`, then the `views.py`, and finally, the HTML templates for the views.

### Project Settings[#](#project-settings)

Overwrite your `mysite/mysite/settings.py` contents with the following code.

```python
import os
from pathlib import Path
from dotenv import find_dotenv, load_dotenv

ENV_FILE = find_dotenv()
if ENV_FILE:
    load_dotenv(ENV_FILE)

BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ['APP_SECRET_KEY']
DEBUG = True
ALLOWED_HOSTS = []

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app',
    'mozilla_django_oidc',
]

AUTHENTICATION_BACKENDS = ('mozilla_django_oidc.auth.OIDCAuthenticationBackend',)

OIDC_RP_CLIENT_ID = os.environ['CLIENT_ID']
OIDC_RP_CLIENT_SECRET = os.environ['CLIENT_SECRET']
OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ['ISSUER'] + "/oauth2/authorize"
OIDC_OP_TOKEN_ENDPOINT = os.environ['ISSUER'] + "/oauth2/token"
OIDC_OP_USER_ENDPOINT = os.environ['ISSUER'] + "/oauth2/userinfo"
OIDC_RP_SCOPES = "openid profile email"
OIDC_RP_SIGN_ALGO = "RS256"
OIDC_OP_JWKS_ENDPOINT = os.environ['ISSUER'] + "/.well-known/jwks.json"
LOGIN_REDIRECT_URL = "http://localhost:8000/app/account/"
LOGOUT_REDIRECT_URL = os.environ['ISSUER'] + "/oauth2/logout?client_id=" + OIDC_RP_CLIENT_ID

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'mysite.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': (os.path.join(BASE_DIR, 'app/templates'),),
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

AUTH_PASSWORD_VALIDATORS = [
    {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},
    {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},
    {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},
    {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',},
]

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True

STATIC_URL = '/static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
```

Here, your `app` and `mozilla_django_oidc` are added to the list of `INSTALLED_APPS`. The authentication library from Mozilla is set up by `mozilla_django_oidc` and the `OIDC_OP_` constants imported from the `.env` file. This automatically handles the interaction with FusionAuth on the endpoints configured in this file and the matching ones configured in the `kickstart.json` file that you used to initialize FusionAuth.

### Project URLs[#](#project-urls)

Overwrite the `mysite/mysite/urls.py` file with the following code:

```python
from django.contrib import admin
from django.urls import include, path
from django.views.generic import RedirectView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app/', include('app.urls')),
    path('oidc/', include('mozilla_django_oidc.urls')),
    path('', RedirectView.as_view(url='/app/', permanent=True)),
]
```

*   The `admin` site is the default Django admin.
*   The `app` URLs are for the app you created in your project.
*   The `oidc` URLs are automatically generated from the settings you configured in the previous step.

### App URLs[#](#app-urls)

From this point, you'll create files in the `mysite/app` application folder, not in the main `mysite/mysite` project.

Create a `mysite/app/urls.py` file and set its content to match the project URLs.

```python
from django.urls import path
from . import views

urlpatterns = [
    path('', views.app, name='app'),
    path('account/', views.account, name='account'),
    path('logout/', views.logout, name='logout'),
    path('change/', views.change, name='change'),
]
```

### App Views[#](#app-views)

Now overwrite the `mysite/app/views.py` file. The code below will create three pages: the home page, the account page, and the make change page.

```python
from json import loads
from math import ceil
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.conf import settings

def app(request):
    if not request.user.is_authenticated:
        return render(request, template_name='home.html')
    return redirect('account')

def account(request):
    if not request.user.is_authenticated:
        return redirect('oidc_authentication_init')
    return render(request, 'account.html', {'email': request.user.email})

def logout(request):
    # https://mozilla-django-oidc.readthedocs.io/en/stable/installation.html
    # When a user logs out, by default, mozilla-django-oidc will end the current Django session. 
    # However, the user may still have an active session with the OpenID Connect provider. In this case
    # the user has been directed back to the FusionAuth server to end the session there. FusionAuth will redirect
    # back here. This is where servers side cleanup will happen, if needed.
    return redirect('app')

def change(request):
    if not request.user.is_authenticated:
        return redirect('oidc_authentication_init')
    change = { "error": None }
    if request.method == 'POST':
        dollar_amt_param = request.POST.get("amount")
        try:
            if dollar_amt_param:
                dollar_amt = float(dollar_amt_param)
                nickels = int(dollar_amt / 0.05)
                pennies = ceil((dollar_amt - (0.05 * nickels)) / 0.01)
                change["total"] = "{:,.2f}".format(dollar_amt)
                change["nickels"] = "{:,d}".format(nickels)
                change["pennies"] = "{:,d}".format(pennies)
        except ValueError:
            change["error"] = "Please enter a dollar amount"
    return render(
        request,
        'change.html',
        {
            'email': request.user.email,
            'change': change
        }
    )
```

Here's what these views do:

*   The `app` view, or home page, redirects the user to the `account` view if they are already logged in.
*   The `account` view checks whether the user is logged in. If the user is logged in, it returns the account page template, passing in the user's email address returned from FusionAuth. If the user isn't logged in, they are redirected to the login page. This also happens in the change page.
*   The `logout` view is necessary because you need to clear the session for the user in Django after the user logs out of FusionAuth. FusionAuth sends the user to this view after logging them out, which then returns them to the home page.
*   The `change` view looks longer only because it demonstrates how to do some calculations if the request is a POST instead of a GET. These calculations are then passed to the template in the `change` variable to display onscreen.

## Customization[#](#customization)

The final step is to create the HTML pages and style them with CSS. Copy the `static` and `templates` folders into your app. Use the appropriate command for your operating system.

```batch
# linux, mac
cp -r complete-application/mysite/app/static mysite/app
cp -r complete-application/mysite/app/templates mysite/app

# windows
xcopy /e complete-application\mysite\app\static mysite\app
xcopy /e complete-application\mysite\app\templates mysite\app
```

There are only two lines in these templates related to authentication. In the `home.html` template, there is a login button.

```html
<a class="button-lg" href="{% url 'oidc_authentication_init' %}">Login</a>
```

This URL is made available by the Mozilla plugin in the settings file. It directs the user to FusionAuth to log in, which then redirects back to your app account page.

In the account and change pages, there is a logout form.

```html
<form id="logoutForm" class="button-lg"  action="{% url 'oidc_logout' %}" method="post">
  {% csrf_token %}
  <a class="button-lg" href="#" onclick="document.getElementById('logoutForm').submit();">Log out</a>
</form>
```

Log out has to be a form that submits a `POST` request to FusionAuth. Here you use an anchor element that is easy to style, but if your users might disable JavaScript, you could use a traditional form `input` element.

## Run The Application[#](#run-the-application)

Update the default SQLite database and start your server.

```bash
python mysite/manage.py migrate
python mysite/manage.py runserver
```

You can now browse to [the Django app](http://localhost:8000/app).

Log in using `richard@example.com` and `password`. The change page will allow you to enter a number and see the result of the POST. Log out and verify that you can't browse to the [account page](http://localhost:8000/app/account).

## Made it this far? Want a free t-shirt? We got ya.

Thank you for spending some time getting familiar with FusionAuth.

\*Offer only valid in the United States and Canada, while supplies last.

## Next Steps[#](#next-steps)

This quickstart is a great way to get a proof of concept up and running quickly, but to run your application in production, there are some things you're going to want to do.

### FusionAuth Customization

FusionAuth gives you the ability to customize just about everything to do with the user's experience and the integration of your application. This includes:

*   [Hosted pages](https://fusionauth.io/docs/customize/look-and-feel.md) such as login, registration, email verification, and many more.
*   [Email templates](https://fusionauth.io/docs/customize/email-and-messages/email-templates.md).
*   [User data and custom claims in access token JWTs](https://fusionauth.io/articles/tokens/jwt-components-explained.md).

### Security

*   Learn more about configuring the [open source OIDC library](https://github.com/mozilla/mozilla-django-oidc).
*   You may want to customize the [token expiration times and policies](https://fusionauth.io/docs/lifecycle/authenticate-users/oauth.md) in FusionAuth.
*   Choose [password rules](https://fusionauth.io/docs/get-started/core-concepts/tenants.md#password) and a [hashing algorithm](https://fusionauth.io/docs/reference/password-hashes.md) that meet your security needs.

### Tenant and Application Management

*   Model your application topology using [Applications](https://fusionauth.io/docs/get-started/core-concepts/applications.md), [Roles](https://fusionauth.io/docs/get-started/core-concepts/roles.md), [Groups](https://fusionauth.io/docs/get-started/core-concepts/groups.md), [Entities](https://fusionauth.io/docs/get-started/core-concepts/groups.md), and more.
*   Set up [MFA](https://fusionauth.io/docs/lifecycle/authenticate-users/multi-factor-authentication.md), [Social login](https://fusionauth.io/docs/lifecycle/authenticate-users/identity-providers.md), or [SAML](https://fusionauth.io/docs/lifecycle/authenticate-users/identity-providers/overview-samlv2.md) integrations.
*   Integrate with external systems using [Webhooks](https://fusionauth.io/docs/extend/events-and-webhooks.md), [SCIM](https://fusionauth.io/docs/lifecycle/migrate-users/scim.md), and [Lambdas](https://fusionauth.io/docs/extend/code/lambdas.md).