{"id":18550504,"url":"https://github.com/srmoore/oidc_django_demo","last_synced_at":"2026-04-29T00:31:19.645Z","repository":{"id":82812346,"uuid":"106031345","full_name":"srmoore/oidc_django_demo","owner":"srmoore","description":"Small demo Django application using private keys and OpenID Connect","archived":false,"fork":false,"pushed_at":"2017-10-06T17:21:17.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-15T10:09:37.865Z","etag":null,"topics":["django","django-application","oidc","tutorial"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/srmoore.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-06T17:20:26.000Z","updated_at":"2017-11-02T14:42:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"3fe3aa05-e5ec-4654-8b04-8c65f46e4111","html_url":"https://github.com/srmoore/oidc_django_demo","commit_stats":{"total_commits":1,"total_committers":1,"mean_commits":1.0,"dds":0.0,"last_synced_commit":"ea2cfeca198bafcf7bb70ee27decc0339696c4bc"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/srmoore/oidc_django_demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srmoore%2Foidc_django_demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srmoore%2Foidc_django_demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srmoore%2Foidc_django_demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srmoore%2Foidc_django_demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/srmoore","download_url":"https://codeload.github.com/srmoore/oidc_django_demo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/srmoore%2Foidc_django_demo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32405901,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T19:38:08.556Z","status":"ssl_error","status_checked_at":"2026-04-28T19:37:55.688Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","django-application","oidc","tutorial"],"created_at":"2024-11-06T21:04:58.843Z","updated_at":"2026-04-29T00:31:19.631Z","avatar_url":"https://github.com/srmoore.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# oidc_django_demo\nThis is a small Django project to demonstrate using private keys and OpenID Connect\n\n## What you'll build\nYou'll build a Django application with a login backed by OpenID Connect\n\n## What you'll need\n - [Django](https://www.djangoproject.com/) : This project uses version 1.11.5\n - [Python](https://www.python.org) : We'll use python version 3.5.2 or later\n\n## Create an unsecured web application\nIt may be helpful to set up a [virtualenv](https://virtualenv.pypa.io/en/stable/) to isolate the\nsetup for this project. (There are several tutorials on how to use it, so I'm not going to document that here)\n\nThe following is a shortened form of the [offical Django tutorial](https://docs.djangoproject.com/en/1.11/intro/tutorial01/).\n\n### Install Django\n```\n(python_3.5)$ pip install Django\n```\n\n### Create a 'site'\nDjango 'sites' can be thought of as a project that contains one or more appliations under them.\n```\n(python_3.5)$ django-admin startproject mysite \n```\n\nThis will create the following:\n```\nmysite/\n    manage.py\n    mysite/\n        __init__.py\n        settings.py\n        urls.py\n        wsgi.py\n```\n\n### Create an app\nWe'll create our 'app' under the site.\n```\n(python_3.5)$ cd mysite\n(python_3.5) mysite$ python manage.py startapp simpleapp\n```\n\nThis will create a subdirectory called `simpleapp` containing your new app\nIt's structure will look like:\n```\nsimpleapp/\n    __init__.py\n    admin.py\n    apps.py\n    migrations/\n        __init__.py\n    models.py\n    tests.py\n    views.py\n```\n\n### Create a view\nWe'll do three things to create our `index` view for our application.\n1. Modify `simpleapp/views.py`\n1. Create a template at `simpleapp/templates/simpleapp.html`\n1. \"Install\" the app into our Django site\n\n#### Modify `simpleapp/views.py`\nDjango created the file for us, but it only contains an import.\nChange it to look like:\n```python\nfrom django.shortcuts import render\n\n\n# Create your views here.\ndef index(request):\n    return render(request, \"simpleapp.html\")\n```\nThis will simply forward to the template we're about to create\n\n#### Create a template\nIn this simple case our template is just going to be an HTML file since our application isn't really doing anything.\nYou can read up on [using Django templates here](https://djangobook.com/django-templates/)\n\nFor now just create the `simpleapp/templates` directory and add in `simpleapp.html` so it looks like:\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead lang=\"en\"\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003ctitle\u003esimpleapp\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cp\u003eHello, world. You're at the simpleapp index.\u003c/p\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n#### Set up the urls for Django\nWhile this isn't needed for our super simple application at this point, it will be later on.\nWe have to define which URL patterns go to which views. Add in a `simpleapp/urls.py` file that looks like:\n```python\nfrom django.conf.urls import url\n\nfrom . import views\n\nurlpatterns = [\n  url(r'^$', views.index, name='index'),\n]\n```\n\n#### \"Install\" the app into our Django project\nIn order for Django to know about the template, we have to 'install' the application.\nTo do this we'll add it to the `mysite/mysite/settings.py` file.\nOpen that file, and find the `INSTALLED_APPS` variable. Add in our `simpleapp` like so:\n```python\nINSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'simpleapp'\n]\n```\nNow we need to tell our project about the `simpleapp` URLs.\nModify the `mysite/urls.py` file to look like:\n```python\nfrom django.conf.urls import include, url\nfrom django.contrib import admin\n\nurlpatterns = [\n    url(r'^simpleapp/', include('simpleapp.urls')),\n    url(r'^admin/', admin.site.urls),\n]\n```\n\n### Run the server\nNow we can test out our application.\nRun the following from the top level `mysite` directory where `manage.py` lives:\n```\n(python_3.5) mysite$ python manage.py runserver\n```\n\nOnce that is done you should be able to visit your new application at [http://localhost:8000/simpleapp](http://localhost:8000/simpleapp)\n\n#### You'll find the output of this part in `inital_site`\n\n## Securing your application with OIDC\n\nIn this section we'll\n1. Install and setup `django-oidc`\n2. Create a protected portion of our web application\n\n### Install `django-oidc`\n`django-oidc` is a module for Django that allows authentication to happen via OpenID Connect.\nThe problem with it is there are many forks of the project that have different feature sets.\nThe fork located at [https://github.com/koriaf/django-oidc](https://github.com/koriaf/django-oidc) seems to be the best \nversion for working with Python 3 and Django 1.11. It has also been updated with a modifcation to support `private_key_jwt`\nbased client authentication.\n\nAs a note, `django-oidc` is basically a wrapper around the [https://github.com/OpenIDC/pyoidc](https://github.com/OpenIDC/pyoidc)\nproject that is a generic python module for using OpenID connect.\n\nTo install the version of `django-oidc` we want we'll use `pip`:\n```\n(python_3.5) mysite$ pip install git+https://github.com/koriaf/django-oidc.git\n```\n### Configure our Django project ot use `django-oidc`\nTo configure our project we'll have to\n1. Install `django-oidc` in `mysite/settings.py`\n1. Add the urls to `mysite/urls.py`\n1. Define our OP and Client\n1. Set up our private keys for client authentication\n\n#### Install `django-oidc` into our project\nWe'll 'install' the module the same way we installed our `simpleapp` in `mysite/settings.py`\n```python\nINSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'djangooidc',\n    'simpleapp'\n]\n```\nNOTE: The application does not contain the '-' when you install it, just add `djangooidc` to the `INSTALLED_APPS`\n\n#### Set up the OpenID Connect URLS\nThankfully `django-oidc` has already defined all the appropriate call back URLs, etc. We just have to tell our project \nhow to use them. This is done in the `mysite/urls.py` file.\n\n```python\nfrom django.conf.urls import include, url\nfrom django.contrib import admin\n\nurlpatterns = [\n    url(r'^simpleapp/', include('simpleapp.urls')),\n    url(r'^openid/', include('djangooidc.urls')),\n    url(r'^admin/', admin.site.urls),\n]\n```\n\n#### Define our OP and Client\nAll the configuration for using OIDC will be in the `mysite/settings.py` file\n\nFirst we add in the `AUTHENTICATION BACKENDS`, this isn't in the default `settings.py` so we'll put it after the `DATABASES`\nsetup. We need the default `ModelBackend` as well as our `OpenIdConnectBackend`.\nWe'll also set up the `LOGIN_URL` while we're here. By default this can just be `/openid` but that will bring you to a \nlogin that allows you to select which OpenID provider, or figure it out from typing in an email, etc. in a form that can be customize\n\nSince we're going to only set up a single OpenID Connect provider, and only want to use that one though,\nwe can use the pattern of `/openid/openid/\u003cprovidername\u003e`, where `\u003cprovidername\u003e` is defined when we configure the \nprovider, `mitreid` in this particular case.\n \n```python\n# DEFINE AUTHENTICATION_BACKENDS\nAUTHENTICATION_BACKENDS = [\n    'django.contrib.auth.backends.ModelBackend',\n    'djangooidc.backends.OpenIdConnectBackend'\n]\n\n# Set LOGIN_URL (for django oidc)\nLOGIN_URL = '/openid/openid/mitreid'\n```\n\nNext we'll want to set up the `PyOIDC` settings\nThe version provided here has several options commented out, so we'll only focus on the core ones here.\nAdd all of these to the `mysite/settings.py` file\n```python\n# We're disabling dynamic client registration.\nOIDC_ALLOW_DYNAMIC_OP = False\n\n# Default OIDC behavoir will be the 'code' workflow\nOIDC_DEFAULT_BEHAVIOUR = {\n    \"response_type\": \"code\",\n    \"scope\": [\"openid\", \"profile\", \"email\", \"address\", \"phone\"],\n}\n\n# Set up our providers. Here the name is 'mitreid' which we use in the login URL above\nOIDC_PROVIDERS = {\n    \"mitreid\": {\n        \"srv_discovery_url\": \"https://mitreid.org/\",\n        \"behaviour\": OIDC_DEFAULT_BEHAVIOUR,\n        \"client_registration\": {\n            \"client_id\": \"f5458edf-5163-4b3b-a965-577922719fb1\",\n            \"redirect_uris\": [\"http://localhost:8000/openid/callback/login/\"],\n            'token_endpoint_auth_method': ['private_key_jwt'],\n            \"enc_kid\": \"rsa_test\",\n            \"keyset_jwk_file\": \"file://keys/keyset.jwk\"\n        }\n    }\n}\n```\n\nThe important bit here is the Providers setup. We are defining only one provider (`mitreid`), and we are not using \ndynamic client registration. We have a `sev_discovery_url` that allows `pyoidc` to find all the endpoints for us, look \nin the comments to see how to manually specify those if needed.\n\nWe then use `client_registration` to define our client. We have the `client_id` which matches the `client id` on your \nOIDC server. The `redirect_uris` should contain only one, which is defined by `django-oidc` as `http://\u003cserver\u003e:\u003cport\u003e/openid/callback/login`.\n\nThe next three values, `token_endpoint_auth_method`, `enc_kid`, and `keyset_jwk_file` are all used to set up using\nprivate keys for client authentication rather than a `client_secret`.\n\n`token_endpoint_auth_method` is pretty self explaintory, we're using `private_key_jwt`.\n\n`enc_kid` must match the `kid` in the JWK file for the key pair used when setting up the client on your OIDC provider.\n(Here we need both the private and public key, where the provider should only have the public.)\n\n`keyset_jwk_file` specifies the keyset file that should be used. (Where the `kid` lives.)\n\n#### Set up private keys\nWe'll place the encryption keys in `/mysite/keys`. We only need a Key Set, which should look like:\n```json\n{\n  \"keys\": [\n    {\n      \"kty\": \"RSA\",\n      \"d\": \"CzY14i8NxPUAPmH9JHR5VIMezv0WOunBB0NkfZmzUGrJSn5DXGrRIs0psERyHLSBKVTpRGcp9ZlcDfMZV81e-v1a_sz9IogCNd15y4UUcpFKuAKAY0s4Fa8whu3u7iL0Zut_tKlBxKPhAtgX3Urc6neRURFvhfzD4zrOaKRbZwf446JxrqyyDSQfGUBhTkiURsvvch0GojaUS-hzuI8tRzgowC5K8jHrl8Bg__ai7iuNfHOFxH83oAlSM4fEt-Fi4FLpev2dxXhvL8sJOVN7CReDsxYWR7l1rzlzH_cER6uA2QX9xCYyqMCegdCfTEEaCGKr28LssRBiSCe6DylRgQ\",\n      \"e\": \"AQAB\",\n      \"use\": \"sig\",\n      \"kid\": \"rsa_test\",\n      \"alg\": \"RS256\",\n      \"n\": \"lTmpgjt5cyV-0v0QWRdiarUZRd6U5muDRrqHOe1UwA6lZUD68LlfvwcYnR8cInMZd3o1Tmx4cvePP8zOCEBnlVVeAamxXaRT59w2iZyXyw90u9or-R3qAMtK-eObJH29jMjRog06U-TXzBExkRcyz8c3JIlI9t1eNMESsBQsrglwGFTa_PFqLM0sGEtuCs9L-Q9ca0-9rlounVhGJMKF4BNEbNoBLeoK-fcwsx45IKo-iId_vJTrK_lTGXy4VwQnR4uHzHWOtvx9h2PVdsaZcSgHk4aIyvN8B5SB2h6DVR1_QtBLwcbY5D-JNT1fMpQqGmYmVHW-AteO82YMpIaIjw\"\n    }\n  ]\n}\n```\n\nI've also included the Key pair and just the public key versions of the jwk in this directory for convenience.\n\n**DO NOT USE THESE KEYS** - Generate your own. You can use a site like: [https://mkjwk.org/](https://mkjwk.org/) with a \nkey length of 2048, key use of `signing`, and your own `kid` that you can specify in the client definition above.\n\n### Create a protected portion of our web app\nNow that we've set up `django-oidc` it's pretty simple to lock down parts of our web application.\nAt this point we'll want to `migrate` our project to set up the database. By default Django uses `sqlite3`, but doesn't\nrecommend that for production. For our little test here, it is just fine though.\nRun the following to set up the user tables, etc.:\n```\n(python_3.5) mysite$ python manage.py migrate\n```\n\nNow we'll change our simple app template to show if we've logged in or not, and provide a link to a resource that only \nauthenticated users can get to. Due to the default processors that Django has, we'll have a `user` object passed on to the\ntemplate automatically.\n\nModify `simpleapp/templates/simpleapp.html` so it looks like:\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead lang=\"en\"\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003ctitle\u003e{% block title %}{% endblock %}\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n{% if user.is_authenticated %}\n    \u003cp\u003eWelcome, {{ user.get_username }}. Thanks for logging in.\u003c/p\u003e\n{% else %}\n    \u003cp\u003eWelcome, new user. Please log in.\u003c/p\u003e\n    \u003ca href=\"{% url 'openid_with_op_name' op_name='mitreid' %}?next={{request.path}}\"\u003eLog In\u003c/a\u003e\n{% endif %}\n\u003cbr\u003e\n\u003cp\u003eHere is a link to a \u003ca href=\"{% url 'protected' %}\"\u003eprotected resource\u003c/a\u003e\u003c/p\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nIf you look you'll see we have an if statement that determines if the user is logged in. If not, we \nprovide a login link. Because of the way authentication works on Django, we have to give it a `next=` parameter\nso it knows where to go. `{{request.path}}` resolves to the current path. The `openid_with_op_name` url will create\na link to login with a specific OIDC provider (in this case our `mitreid` one).\n\nIf we are logged in, we're just displaying the `user.get_username` value in a welcome message.\n\nNext we'll define the `protected` view that the link at the bottom goes to.\n\nFirst we'll modify `simpleapp/views.py` to create the view. add in the following imports and function.\n```python\nfrom django.contrib.auth.decorators import login_required\n\n...\n\n@login_required\ndef protected(request):\n    return render(request, \"protected.html\")\n```\n\nThat's it. The `@login_required` indicates that you can only get to this view if you are logged in.\n\nNow we'll create the template as `simpleapp/templates/protected.html`.\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead lang=\"en\"\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003ctitle\u003e{% block title %}{% endblock %}\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cp\u003e\u003ca href=\"{% url 'index' %}\"\u003eReturn\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eYou should only see this if you are logged in.\u003c/p\u003e\n\u003ctable\u003e\n    \u003ctr\u003e\u003ctd\u003euser.get_username\u003c/td\u003e\u003ctd\u003e{{user.get_username}}\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003euser.email\u003c/td\u003e\u003ctd\u003e{{user.email}}\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003euser.first_name\u003c/td\u003e\u003ctd\u003e{{user.first_name}}\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003euser.last_name\u003c/td\u003e\u003ctd\u003e{{user.last_name}}\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr\u003e\u003ctd\u003euser.username\u003c/td\u003e\u003ctd\u003e{{user.last_login}}\u003c/td\u003e\u003c/tr\u003e\n\u003c/table\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nAnd lastly we'll add `protected` to our urls. Change the `simpleapp/urls.py` to the following:\n```python\nfrom django.conf.urls import url\n\nfrom . import views\n\nurlpatterns = [\n  url(r'^$', views.index, name='index'),\n  url(r'^protected/$', views.protected, name='protected')\n]\n```\n\nFire up the application now with:\n```\n(python_3.5) mysite$ python manage.py runserver\n```\nNow when you go to [http://localhost:8000/simpleapp/](http://localhost:8000/simpleapp/) you should see that you are not \nlogged in. You can either click the login link, or just click right on the `protected` link, and you should be re-directed \nto the `mitreid.org` OIDC login page. Once logged in, you should come back to this page or see the `protected` page with \nuser information.\n\n**NOTE:** going to `openid/logout` will attempt to end the session at the OIDC provider. This is not recommended as not \nall providers support this, plus it will log the user out of other applications.\n\nYou'll find the completed version of this tutorial in the `completed` sub directory.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrmoore%2Foidc_django_demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsrmoore%2Foidc_django_demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrmoore%2Foidc_django_demo/lists"}