Integrate Your Python Django Application With FusionAuth
Integrate Your Python Django Application With FusionAuth
In this tutorial, you are going to learn how to integrate a Python Django application with FusionAuth.
Here’s a typical application login flow before integrating FusionAuth into your Python Django application.
And here’s the same application login flow when FusionAuth is introduced.
Prerequisites
For this tutorial, you’ll need to have python3 and pip3 installed.
You’ll also need Docker, since that is how you’ll install FusionAuth.
The commands below are for macOS, but are limited to mkdir
and cd
, which have equivalent in Windows and linux.
Download and Install FusionAuth
First, make a project directory:
mkdir integrate-fusionauth && cd integrate-fusionauth
Then, install FusionAuth:
curl -o docker-compose.yml https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/docker-compose.yml
https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/docker-compose.override.yml
curl -o .env https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/master/docker/fusionauth/.env
docker-compose up -d
Create a User and an API Key
Next, log into your FusionAuth instance. You’ll need to set up a user and a password, as well as accept the terms and conditions.
Then, you’re at the FusionAuth admin UI. This lets you configure FusionAuth manually. But for this tutorial, you’re going to create an API key and then you’ll configure FusionAuth using our python client library.
Navigate to + button to add a new API Key.
Copy the value of the Key field and then save the key.
It might be a value like CY1EUq2oAQrCgE7azl3A2xwG-OEwGPqLryDRBCoz-13IqyFYMn1_Udjt
.
Doing so creates an API key that can be used for any FusionAuth API call. Save that key value off as you’ll be using it later.
Configure FusionAuth
Next, you need to set up FusionAuth. This can be done in different ways, but we’re going to use the python client library. The below instructions use maven from the command line, but you can use the client library with an IDE of your preference as well.
First, make a directory:
mkdir setup-fusionauth && cd setup-fusionauth
If you want, you can login to your instance and examine the new application configuration the script created for you after you are done.
Now, cut and paste the following file into requirements.txt
.
fusionauth-client==1.42.0
Then copy and paste the following code into the setup.py
file.
from fusionauth.fusionauth_client import FusionAuthClient
import os
import sys
APPLICATION_ID = "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e";
# You must supply your API key
api_key_name = 'fusionauth_api_key'
api_key = os.getenv(api_key_name)
if not api_key:
sys.exit("please set api key in the '" + api_key_name + "' environment variable")
client = FusionAuthClient(api_key, 'http://localhost:9011')
# set the issuer up correctly
client_response = client.retrieve_tenants()
if client_response.was_successful():
tenant = client_response.success_response["tenants"][0]
else:
sys.exit("couldn't find tenants " + str(client_response.error_response))
client_response = client.patch_tenant(tenant["id"], {"tenant": {"issuer":"http://localhost:9011"}})
if not client_response.was_successful():
sys.exit("couldn't update tenant "+ str(client_response.error_response))
# generate RSA keypair for signing
rsa_key_id = "356a6624-b33c-471a-b707-48bbfcfbc593"
client_response = client.generate_key({"key": {"algorithm":"RS256", "name":"For PythonExampleApp", "length": 2048}}, rsa_key_id)
if not client_response.was_successful():
sys.exit("couldn't create RSA key "+ str(client_response.error_response))
# create application
# too much to inline it
application = {}
application["name"] = "PythonExampleApp"
# configure oauth
application["oauthConfiguration"] = {}
application["oauthConfiguration"]["authorizedRedirectURLs"] = ["http://localhost:8000/oidc/callback/"]
application["oauthConfiguration"]["requireRegistration"] = True
application["oauthConfiguration"]["enabledGrants"] = ["authorization_code", "refresh_token"]
application["oauthConfiguration"]["logoutURL"] = "http://localhost:8000/"
application["oauthConfiguration"]["clientSecret"] = "change-this-in-production-to-be-a-real-secret"
# some libraries don't support pkce, notably mozilla-django-oidc: https://github.com/mozilla/mozilla-django-oidc/issues/397
# since we are server side and have a solid client secret, we're okay turning pkce off
application["oauthConfiguration"]["proofKeyForCodeExchangePolicy"] = "NotRequiredWhenUsingClientAuthentication"
# assign key from above to sign our tokens. This needs to be asymmetric
application["jwtConfiguration"] = {}
application["jwtConfiguration"]["enabled"] = True
application["jwtConfiguration"]["accessTokenKeyId"] = rsa_key_id
application["jwtConfiguration"]["idTokenKeyId"] = rsa_key_id
client_response = client.create_application({"application": application}, APPLICATION_ID)
if not client_response.was_successful():
sys.exit("couldn't create application "+ str(client_response.error_response))
# register user, there should be only one, so grab the first
client_response = client.search_users_by_query({"search": {"queryString":"*"}})
if not client_response.was_successful():
sys.exit("couldn't find users "+ str(client_response.error_response))
user = client_response.success_response["users"][0]
# patch the user to make sure they have a full name, otherwise OIDC has issues
client_response = client.patch_user(user["id"], {"user": {"fullName": user["firstName"]+" "+user["lastName"]}})
if not client_response.was_successful():
sys.exit("couldn't patch user "+ str(client_response.error_response))
# now register the user
client_response = client.register({"registration":{"applicationId":APPLICATION_ID}}, user["id"])
if not client_response.was_successful():
sys.exit("couldn't register user "+ str(client_response.error_response))
Then, you can run the setup script. You’ll use venv
to keep your workspace clean. This will create FusionAuth
configuration for your Python Django application.
python -m venv venv && \
source venv/bin/activate && \
pip install -r requirements.txt
Now run the setup script:
fusionauth_api_key=<your API key> python setup.py
This configures FusionAuth for your Python Django application.
Create Your Python Django Application
Now you are going to create a Python Django application. While this section builds a simple Python Django application, you can use the same configuration to integrate your complex Python Django application with FusionAuth.
First, make a directory:
mkdir ../setup-django && cd ../setup-django
Now, create a new requirements.txt
file to include Python Django.
Add the Django
and mozilla-django-oidc
plugins so that your requirements file looks like this:
fusionauth-client==1.42.0
Django==3.2.15
mozilla-django-oidc==2.0.0
Then, re-run pip install
to install these new packages
pip install -r requirements.txt
Then, create your site:
django-admin startproject mysite
Start Up The Server
Then, open a new terminal. You’ll need to re-source your virtual environment settings:
source venv/bin/activate
Then, start your server:
python mysite/manage.py runserver
You can visit it to make sure it is running. If you need to stop it, hit control-C
to do so. You can leave this running while you build out your application so that you can visit the app and see your progress.
Create a View
Switch back to your previous terminal window. It is going to be easier to run commands from within your mysite
directory, so cd
into it:
cd mysite
Create an app within your mysite
project:
python manage.py startapp app
If you visit http://localhost:8000/app you’ll see an error message because you need to create a view. Make a templates directory
mkdir -p app/templates/app
And paste this HTML into the app/templates/app/authentication.html
file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django OAuth App</title>
</head>
<body>
<div>
Hello!
</div>
</body>
</html>
Create a urls.py
file for your application, at app/urls.py
, and put the following text in it:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
Then you have to tie the view to the template you created above by modifying app/views.py
to look like:
from django.shortcuts import render
# Create your views here.
from django.http import HttpResponse
def index(request):
return render(request, template_name='app/authentication.html')
Then, modify mysite/settings.py
. First, import the os
module, changing the imports at the top of mysite/settings.py
to look like:
import os
from pathlib import Path
Then, add 'app’ to your list of installed applications. Add it to the `INSTALLED_APPS
array.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app',
]
The last change with mysite/settings.py
is to update the templates variable. Look for the TEMPLATES
variable and replace the DIRS
field with this:
'DIRS': (os.path.join(BASE_DIR, 'polls/templates'),),
Finally you have to edit the mysite/urls.py
file:
-
Add
path('app/', include('app.urls')),
to theurlpatterns
variable in that file. -
Change the import line:
from django.urls import path
tofrom django.urls import include, path
.
Here’s what mysite/urls.py
should look like:
"""mysite URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('app/', include('app.urls')),
]
At this point, you can visit http://localhost:8000/app and you should be greeted with the contents of the authentication.html
file you created above.
Add Authentication
Now you are going to link up FusionAuth and add authentication to this project.
First, modify mysite/settings.py
and add mozilla_django_oidc
to the list of applications. You installed this app in your environment when you updated requirements.txt
.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app',
'mozilla_django_oidc',
]
You need to add some additional configuration for this app. Just below the INSTALLED_APPS
array, add this:
AUTHENTICATION_BACKENDS = (
'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
)
# The below config is taken from fusionauth admin UI when you set up the Django application in it
OIDC_RP_CLIENT_ID = 'e9fdb985-9173-4e01-9d73-ac2d60d1dc8e'
OIDC_RP_CLIENT_SECRET = 'change-this-in-production-to-be-a-real-secret'
OIDC_OP_AUTHORIZATION_ENDPOINT = "http://localhost:9011/oauth2/authorize"
OIDC_OP_TOKEN_ENDPOINT = "http://localhost:9011/oauth2/token"
OIDC_OP_USER_ENDPOINT = "http://localhost:9011/oauth2/userinfo"
OIDC_RP_SCOPES = "openid profile email"
OIDC_RP_SIGN_ALGO = "RS256"
OIDC_OP_JWKS_ENDPOINT = "http://localhost:9011/.well-known/jwks.json"
LOGIN_REDIRECT_URL = "http://localhost:8000/app/"
LOGOUT_REDIRECT_URL = "http://localhost:8000/app/"
Save the mysite/settings.py
file.
Next, update the paths to include those provided by the mozilla_django_oidc code. Here’s what mysite/urls.py
should look like:
"""mysite URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('app/', include('app.urls')),
path('oidc/', include('mozilla_django_oidc.urls')),
]
Then you want to update the app/templates/app/authentication.html
file to have this content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django OAuth App</title>
</head>
<body>
<div>
{% if user.is_authenticated %}
<p>You are signed-in! Welcome {{user.email}}</p>
<form action="{% url 'oidc_logout' %}" method="post">
{% csrf_token %}
<input type="submit" value="Logout">
</form>
{% else %}
<a href="{% url 'oidc_authentication_init' %}">Login</a>
{% endif %}
</div>
</body>
</html>
Run all your migrations to make sure you have any needed database tables set up.
python manage.py migrate
You can now open up an incognito window and visit the Python Django app. Log in using the user you added in FusionAuth, and you’ll see a welcoming message.
Feedback
How helpful was this page?
See a problem?
File an issue in our docs repo
Have a question or comment to share?
Visit the FusionAuth community forum.