How to implement OpenID Connect authentication in a Django app using oxd and the Gluu Server


oxd-overview

This tutorial will teach you how to build a simple Django application that sends users to a standard OpenID Connect Provider (OP) for login. The demo app, called user-view, makes use of Gluu’s commercial OAuth 2.0 client software, oxd, to securely and correctly call the OP’s APIs to log the user in, and retrieve the users email address.

Note: If you have apps that are coded in other languages or frameworks you can still use oxd. There are oxd libraries for Ruby, .Net, c#, Java and more. Read the docs.

About the Software in Use

The Gluu Server is a free open source access management platform. You can point custom developed, open source, off-the-shelf, and SaaS applications at your Gluu Server to leverage a single identity and authentication infrastructure. If you haven’t already setup your Gluu server you can follow the installation guide to do so. Or you can simply use Google as your OP.

The oxd server is commercial OAuth 2.0 client software written by Gluu. It is a mediator that provides API’s that can be called by a web application that are easier than directly calling the API’s of an OpenID Connect Provider (OP), like the Gluu Server. oxd can be used with any standard OP including the Gluu Server and Google. Get your oxd license.

Installing Django

When running python applications, it’s important to keep the app’s environment separate from the server’s environment. This prevents conflicts in module versions and prevents unwanted side-effects. In this tutorial we’ll use virtualenv to keep our user-view project’s environment separate from the server’s environment.

Install virtualenv and activate it.

ubuntu:~/userview-app$ pip install virtualenv
ubuntu:~/userview-app$ virtualenv env
ubuntu:~/userview-app$ source ./env/bin/activate
(env)ubuntu:~/userview-app$ 

Now we’re in a new clean environment. Install Django with pip and create a new Django project in the project folder by appending the startproject command with .

(env)ubuntu:~/userview-app$ pip install Django
(env)ubuntu:~/userview-app$ django-admin.py startproject userview .

Run migrations to setup the database and enable admin functionality.

(env)ubuntu:~/userview-app$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
 Applying contenttypes.0001_initial... OK
 Applying auth.0001_initial... OK
 Applying admin.0001_initial... OK
 Applying admin.0002_logentry_remove_auto_add... OK
 Applying contenttypes.0002_remove_content_type_name... OK
 Applying auth.0002_alter_permission_name_max_length... OK
 Applying auth.0003_alter_user_email_max_length... OK
 Applying auth.0004_alter_user_username_opts... OK
 Applying auth.0005_alter_user_last_login_null... OK
 Applying auth.0006_require_contenttypes_0002... OK
 Applying auth.0007_alter_validators_add_error_messages... OK
 Applying auth.0008_alter_user_username_max_length... OK
 Applying sessions.0001_initial... OK

(env)ubuntu:~/userview-app$

Run the Django server:

(env)ubuntu:~/userview-app$ python manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
February 16, 2017 - 10:42:56
Django version 1.10.5, using settings 'userview.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

With a browser visit localhost:8000 to make sure everything is working as expected. You should see something that looks like this:

Screen Shot 2017-02-16 at 10.55.06 AM

Great! Our Django app is up and running.

To create the homepage, we need to update the template directory setting in the userview/settings.py file.

TEMPLATES = [
 {
   'BACKEND': 'django.template.backends.django.DjangoTemplates',
   'DIRS': [os.path.join(BASE_DIR, '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',
     ],
   },
 },

Now create a templates folder in the app’s root folder and create a templates/index.html file with the contents.

<!DOCTYPE html>
<html>
<head>
 <title>World's Simplest Django App. Maybe?</title>
</head>

<body>
 Hi!

</body>
</html>

Yes, this probably is a contender for the world’s simplest site but we will add a bit more later.

Update the userview/views.py to use this new template.

from django.shortcuts import render


def index(request):
  return render(request, 'index.html')

Add this new view to the userview/urls.py

from django.conf.urls import url
from django.contrib import admin
from views import index


urlpatterns = [
 url(r'^$', index),
 url(r'^admin/', admin.site.urls),
]

Run the server with:

(env)ubuntu:~/userview-app$python manage.py runserver

And voila! Our Simple Django App is ready to go.

Screen Shot 2017-02-28 at 4.35.49 PM

Now let’s configure oxd!

Setting up oxd

To use oxd to implement authentication with a standard OP, you need to first sign up for your oxd license.

Screen Shot 2017-02-17 at 11.29.05 AM (2)

Signing up for an oxd license is simple and even includes a $50 credit for charges incurred during your first 60 days of use.

To sign up for oxd:

  1. Click on the “Get a License” button anywhere on the oxd homepage.
  2. Fill in your details and select a password.
  3. An email with your activation link will be sent to your account. Click on the link to activate your oxd account.
  4. Take note of your public and license keys shown on the dashboard. You will need them for your oxd configuration.

Once you’re signed up, you’re ready to get oxd up and running on your server.

Installation of oxd is simple with the provision of the Gluu Repo to allow installation from popular linux package managers like yum and apt-get.

To install oxd on Ubuntu, add the Gluu Repo to your sources list and install from your package manager. For Ubuntu it’s:

(env)ubuntu:~/userview-app$ echo "deb https://repo.gluu.org/ubuntu/ xenial main" > /etc/apt/sources.list.d/gluu-repo.list
(env)ubuntu:~/userview-app$ curl https://repo.gluu.org/ubuntu/gluu-apt.key | apt-key add -
(env)ubuntu:~/userview-app$ apt-get update
(env)ubuntu:~/userview-app$ apt-get install gluu-oxd-server

oxd configuration is simple and can be done in two JSONs file at /opt/oxd-server/conf/oxd-conf.json and /opt/oxd-server/conf/oxd-default-site-config.json. You can read about the properties of this file at the oxd documentation. To configure oxd, you need the License ID, Public Key, Public Password and License Password from your oxd dashboard.

Screen Shot 2017-02-17 at 11.20.14 AM (2)

 

Now install oxd-python start the oxd server:

(env)ubuntu:~/userview-app$apt-get install gluu-oxd-python
(env)ubuntu:~/userview-app$ service gluu-oxd-server start

Great now we’re all set to leverage oxd.

In the userview folder create the configuration file userview.cfg with the necessary configuration:

[oxd]
host = localhost
port = 8099
id =

[client]
authorization_redirect_uri = http://www.example.com/callback
post_logout_redirect_uri = https://www.example.com/
cilent_name = Worlds Simplest Web App

Now we’re ready to write the authorization view to authorize the user. oxd automatically registers the site if it isn’t already registered so there’s no need to call the register_site(). API. Update the userview/views.py file to:

import os

import oxdpython
from django.shortcuts import render, redirect


this_dir = os.path.dirname(os.path.realpath(__file__))
config = os.path.join(this_dir, 'userview.cfg')
oxc = oxdpython.Client(config)


def index(request):
  return render(request, 'index.html')


def authorize(request):
  auth_url = oxc.get_authorization_url()
  return redirect(auth_url)

To complete the call, we need to implement the callback function which is as simple as the authorize call. All we need to do is to get the user access_token with get_tokens_by_code() and retrieve user information using get_user_info() in the userview/views.py file.

def callback(request):
  code = request.GET.get('code')
  state = request.GET.get('state')

  tokens = oxc.get_tokens_by_code(code, state)

  user = oxc.get_user_info(tokens.access_token)

  return render(request, "home.html", {'user': user})

Update the index.html file to authorize and show the user information.

<!DOCTYPE html>
<html>
<head>
 <title>World's Simplest Django App. Maybe?</title>
</head>

<body>
 Hi {{user.email}}! <br>

 <a href="/authorize">Authorize</a>
</body>
</html>

The last thing we need to do is update the userview/urls.py file to add these new views

from django.conf.urls import url
from django.contrib import admin
from views import index


urlpatterns = [
 url(r'^$', index),
 url(r'^authorize/$', authorize),
 url(r'^callback/$', callback),
 url(r'^admin/', admin.site.urls),
]

And we’re done!

Test

Now run the server with python manage.py runserver command and authorize your users:

Screen Shot 2017-02-28 at 4.48.40 PM

Authenticate at the OP:

Screen Shot 2017-02-28 at 4.49.05 PM

Return to the Django app with an authenticated session:

Screen Shot 2017-02-28 at 4.49.36 PM

You can continue to build on this by implementing logout and profile views in your application by following the oxd documentation.