How to implement OIDC authentication with Django and Okta
Tags: python, django, okta, tutorial
Published: 2022-12-13 10:38:26

This article details how to implement [OIDC](https://curity.io/resources/openid-connect) authentication using [Django](https://www.djangoproject.com/) and [mozilla-django-oidc](https://mozilla-django-oidc.readthedocs.io/en/stable/) with [Okta](https://developer.okta.com/) as our identity provider


## Introduction

- **OpenID Connect (OIDC)** is a protocol that allows a user to authenticate with a third-party service and then use that authentication to sign in to other services. OIDC is built on top of the OAuth2 protocol and adds an additional layer of authentication on top of it. This allows a user to not only grant permission for a service to access their data, but also to verify their identity.

- **Django** is a popular web framework for building web applications in Python. It includes a robust authentication system that can be easily configured to support OIDC. 
- **Mozilla-django-oidc** is an open-source library that adds OIDC support to Django, making it easy to authenticate users with an OIDC provider.

- **Okta** is an identity and access management platform that enables organizations to securely connect users to technology. It supports the OIDC protocol, which allows users to be authenticated and receive information about their identity and access rights across different applications.

**Note**: _Here's a link to the final project  on Github. {% embed https://github.com/Hesbon5600/oidc-connect %}_

---

## Part 1 : Setting up an Okta Account

To set up an Okta account for our OIDC account, follow these steps:

1. Go to the [Okta developer website](https://developer.okta.com/signup/) and create an account. Use your `gmail` account in order to get a free developer account.

2. After you have created your Okta account, log in to the Okta dashboard.

3. Click on the `Applications` tab in the left menu and then click on the `Create App Integration` button.![Create App Integration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn5he5i6c1dd2fgvosk4.png)

4. Choose `OIDC - OpenID Connect` `Sign-in method` and Select an `Application type` of `Web Application`, then click Next
![Sign-in method Selection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/36lgaj8g2vj3duqbwqj5.png)

5. Enter an App integration name (e.g oidc-connect).
Enter the Sign-in redirect URIs for local development, such as http://localhost:8080/authorization-code/callback.

6. Optionally Enter the Sign-out redirect URIs for both local development, such as http://localhost:8080/signout/callback. 
![App integration Details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9zpny7roedkwu5jxvbhv.png)

7. In the `Assignments section`, define the type of `Controlled access` for your app. Select the `Everyone` group for now. For more information, see the [Assign app integrations](https://help.okta.com/okta_help.htm?type=oie&id=ext-lcm-user-app-assign) topic in the Okta product documentation.
![App integration Access](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bbwsdt7t1x0o14ktlso4.png)

8. `Click Save` to create the app integration. The configuration pane for the integration opens after it's saved. 

**Note:** _Keep this pane open as you copy some values when configuring your app._

---

## Part 2.0 : Setting up the Django application

To set up a Django application, follow these steps:

1. Install Django by running the following command: ```bash
pip install django
```

2. Create a new Django project by running the following command:```bash
django-admin startproject oidc_app
```

3. This will create a new directory called "oidc_app" with the basic structure for a Django project. Change into the new directory by running the following command:```bash
cd oidc_app
```
4. Apply the default database migrations using the command.```bash
python manage.py migrate
```
5. Run the app on port `8080` using the following command.```bash
python manage.py runserver 8080
```![Running App](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lpx69kyqjbaytldphnvn.png)

5. That's it! Your Django application is now set up and you are ready to move on to the next step: configuring Django to use OIDC.

---

## Part 2.2 : Creating a dummy login page and home screen

1. Create a superuser with the following command: ```bash
python manage.py createsuperuser```

2. Create a new app in your project directory by running the command: ```python manage.py startapp authentication```

3. Add `authentication` to the `INSTALLED_APPS` in `settings.py` as follows:```python
#oidc_app/settings.py
   INSTALLED_APPS = [
...
 "authentication",
]
```

3. Let's make our login page! By default, Django will look for auth templates within a templates folder. To create a login page, create a directory called `templates`, and within it a directory called `registration`. 
4. Inside the `registration` directory, create a file called `login.html`. Add the following piece of code. ```html
<!-- oidc_app/templates/registration/login.html -->
<h2>Log In</h2>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Log In</button>
</form>
```
5. Update the `settings.py` file to tell Django to look for a templates folder inside the oidc_app directory. Update the `DIRS` setting within `TEMPLATES` as follows.```json
# django_project/settings.py
TEMPLATES = [
    {
        ...
        'DIRS': [BASE_DIR.joinpath("oidc_app/templates")],
        ...
    },
]
```
6. If you start the app and navigate to: `http://127.0.0.1:8080/accounts/login/`, You should see/test the login page. ![Login Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w1dvnt4et98etnlxgjng.png)
7. For the home page, we need to make a file called `home.html` located in the `templates` folder. The home page will display a different message to `logged out` and `logged in` users.```html
<!-- oidc_app/templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>{% block title %}Django OIDC{% endblock %}</title>
</head>
<body>
    <main>
        {% if user.is_authenticated %}
        Hi {{ user.username }} - {{ user.email }}!
        <p><a href="{% url 'logout' %}">Log Out</a></p>
        {% else %}
        <p>You are not logged in</p>
        <a href="{% url 'login' %}">Log In</a>
        {% endif %}
    </main>
</body>
</html>
```
8. We now update the `urls.py` file in order to display the home page.```python
from django.contrib import admin
from django.urls import path, include
from django.views.generic.base import TemplateView
urlpatterns = [
    path("admin/", admin.site.urls),
    path("accounts/", include("django.contrib.auth.urls")),
    path('', TemplateView.as_view(template_name='home.html'), name='index')
]
```
9. Now that we have a homepage view we should use that instead of the default setup. We update the settings file as follows: ```python
#oidc_app/settings.py
LOGIN_REDIRECT_URL = "index"
LOGOUT_REDIRECT_URL = "index"
```
9. Let's create a `superuser` that we can use to test the authentication. Type in the command: ```bash
python manage.py createsuperuser
```

10. Enter your desired username, email address and password. You should now have a superuser created for your Django project.

<table>
    <tr>
        <th>Logged Out</th>
        <th>Logged In</th>
    </tr>
<tr>
    <td> <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pnwr2x4ickiy82gq6c3w.png" alt="Logged Out" style="width: 250px;"/> </td>
    <td> <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/59n84rui5g66rh99efcq.png" alt="Logged in" style="width: 250px;"/> </td>
    </tr>
</table>

**Note:** The above steps are a very basic example of how to set up a Django application. In a real-world scenario, you would likely want to do more advanced configuration and set up additional features such as a database and static file serving. For more information on how to do this, please see the [Django documentation](https://www.djangoproject.com/).

---

## Part 3.0 : Configuring Django to use OIDC.

**Note:** _We will be using the OpenID Connect Authorization Code Flow. Refer to the [Curity documentation](https://curity.io/resources/learn/openid-code-flow/) for more details._ 

> The Authorization Code Flow is the most advanced flow in OpenID Connect. It is also the most flexible, that allows both mobile and web clients to obtain tokens securely. It is split into two parts, the authorization flow that runs in the browser where the client redirects to the OpenID Provider (OP) and the OP redirects back when done, and the token flow which is a back-channel call from the client to the token endpoint of the OP.

`mozilla-django-oidc` package has abstracted all of the code flow steps required to enable OIDC authentication.

1. Once you’ve created and configured your Django application, you can start configuring `mozilla-django-oidc`. First, install mozilla-django-oidc using pip:```bash
pip install mozilla-django-oidc
```

2. Make the following changes to your `settings.py` file:```python
# oidc_app/settings.py
# Add 'mozilla_django_oidc' to INSTALLED_APPS
INSTALLED_APPS = (
    # ...
    'django.contrib.auth',
    'mozilla_django_oidc',  # Load after auth
    # ...
)
# Add 'mozilla_django_oidc' authentication backend
AUTHENTICATION_BACKENDS = (
    'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
    # ...
)
OKTA_DOMAIN =  "[Your Okta domain]"
OIDC_RP_CLIENT_ID = "[Your Okta application’s client ID]"
OIDC_RP_CLIENT_SECRET = "[Your Okta application’s client secret]"
OIDC_RP_SIGN_ALGO = "RS256"
OIDC_OP_AUTHORIZATION_ENDPOINT = f"https://{OKTA_DOMAIN}/oauth2/default/v1/authorize" # The OIDC authorization endpoint
OIDC_RP_TOKEN_ENDPOINT = f"https://{OKTA_DOMAIN}/oauth2/default/v1/token" # The OIDC token endpoint
OIDC_OP_USER_ENDPOINT = f"https://{OKTA_DOMAIN}/oauth2/default/v1/userinfo" # The OIDC userinfo endpoint
OIDC_OP_TOKEN_ENDPOINT = f"https://{OKTA_DOMAIN}/oauth2/default/v1/token" # The OIDC token endpoint
OIDC_OP_JWKS_ENDPOINT = f"https://{OKTA_DOMAIN}/oauth2/default/v1/keys" # The OIDC JWKS endpoint
```
3. Next, edit your `urls.py` and add the following:```python
#oidc_app/settings.py
from mozilla_django_oidc import views as oidc_views
urlpatterns = [
    # ...
    path("authorization-code/authenticate/", oidc_views.OIDCAuthenticationRequestView.as_view(), name="oidc_authentication_init"),
    path("authorization-code/callback/", oidc_views.OIDCAuthenticationCallbackView.as_view(), name="oidc_authentication_callback"),
    # ...
]
```
4. We need to add `Login with Okta` option to our `login` page. Edit the `login.html` file as follows:```html
<!-- oidc_app/templates/registration/login.html -->
<h2>Log In</h2>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Log In</button>
    <br />
    or
    <br />
    <a href="{% url 'oidc_authentication_init' %}">
        <button type="button">Log In with Ota</button>
    </a>
</form>
```
**Note:** 
- You can get your `Okta Domain` by clicking on your profile section on the top right side of the page. ![Okta Domain](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/58m8ni505bamjxm35i7s.png)  
- The Okta `client ID` and `client secret` are fund in the application settings (`oidc-connect` app we created)

## Part 3.1 Testing the Okta integration

1. Your login page should now have a 'Login with Okta' option
![Login with Okta](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9fsp7h7rtrjaw8j1mf8a.png)
2. When you click the 'Login with Okta' button, you should be redirected to the okta domain for authentication. 
![Okta Login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u1qzu7eygigoeczbv3ge.png)
3. Enter your username and password or sign in with Google.
4. After successful authentication, you should be redirected to the `home` page. 
![Successful Login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6jdz9pu4aa8bxxe228av.png)
**Note:** The Username is a base64 encoded sha224 of the email address. More information can be found [here](https://github.com/mozilla/mozilla-django-oidc/blob/63f56222e3/mozilla_django_oidc/auth.py#L24)

**That's it!** You now have a Django app that authenticated with Okta. 

The `mozilla-django-oidc` package can be further customized to better suit your application needs.

**Next Up:** {% embed https://dev.to/hesbon/customizing-mozilla-django-oidc-544p %}

Feel free to leave a comment or suggestion. Thank you!