Merge pull request #7 from juanifioren/v0.3.x

V0.3.x
This commit is contained in:
Wojciech Bartosiak 2016-10-04 19:11:54 +02:00 committed by GitHub
commit f98b117a0e
23 changed files with 249 additions and 192 deletions

View file

@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
### [Unreleased]
##### Added
- Polish translations.
- Examples section in documentation.
##### Fixed
- CORS in discovery and userinfo endpoint.
- Client type public bug when created using the admin.
### [0.3.7] - 2016-08-31
##### Added

View file

@ -31,6 +31,7 @@ Contents:
sections/userconsent
sections/oauth2
sections/settings
sections/examples
sections/contribute
..

View file

@ -31,7 +31,7 @@ Improve Documentation
We use `Sphinx <http://www.sphinx-doc.org/>`_ for generate this documentation. I you want to add or modify something just:
* Install Sphinx ``pip install sphinx`` and this theme ``pip install sphinx-rtd-theme``.
* Install Sphinx (``pip install sphinx``) and the auto-build tool (``pip install sphinx-autobuild``).
* Move inside the docs folder. ``cd docs/``
* Generate the HTML. ``make html``
* Open ``docs/_build/html/index.html`` on a browser.
* Generate and watch docs by running ``sphinx-autobuild . _build/``.
* Open ``http://127.0.0.1:8000`` on a browser.

View file

@ -0,0 +1,94 @@
.. _examples:
Examples
########
Pure JS client using Implicit Flow
==================================
Testing OpenID Connect flow can be as simple as putting one file with a few functions on the client and calling the provider. Let me show.
**01. Setup the provider**
You can use the example project code to run your OIDC Provider at ``localhost:8000``.
Go to the admin site and create a public client with a response_type ``id_token token`` and a redirect_uri ``http://localhost:3000``.
.. note::
Remember to create at least one **RSA Key** for the server. ``python manage.py creatersakey``
**02. Create the client**
As relying party we are going to use a JS library created by Nat Sakimura. `Here is the article <https://nat.sakimura.org/2014/12/10/making-a-javascript-openid-connect-client/>`_.
**index.html**::
<!DOCTYPE html>
<html>
<head>
<title>OIDC RP</title>
</head>
<body>
<center>
<h1>OpenID Connect RP Example</h1>
<button id="login-button">Login</button>
</center>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="https://www.sakimura.org/test/openidconnect.js"></script>
<script type="text/javascript">
$(function() {
var clientInfo = {
client_id : '',
redirect_uri : 'http://localhost:3000'
};
OIDC.setClientInfo(clientInfo);
var providerInfo = OIDC.discover('http://localhost:8000');
OIDC.setProviderInfo(providerInfo);
OIDC.storeInfo(providerInfo, clientInfo);
// Restore configuration information.
OIDC.restoreInfo();
// Get Access Token
var token = OIDC.getAccessToken();
// Make userinfo request using access_token.
if (token !== null) {
$.get('http://localhost:8000/userinfo/?access_token='+token, function( data ) {
alert('USERINFO: '+ JSON.stringify(data));
});
}
// Make an authorization request if the user click the login button.
$('#login-button').click(function (event) {
OIDC.login({
scope : 'openid profile email',
response_type : 'id_token token'
});
});
});
</script>
</body>
</html>
.. note::
Remember that you must set your client_id (line 21).
**03. Make an authorization request**
By clicking the login button an authorization request has been made to the provider. After you accept it, the provider will redirect back to your previously registered ``redirect_uri`` with all the tokens requested.
**04. Requesting user information**
Now having the access_token in your hands you can request the user information by making a request to the ``/userinfo`` endpoint of the provider.
In this example we display information in the alert box.

View file

@ -1,6 +1,6 @@
# Example Project
![Example Project](http://i.imgur.com/IK3OZjx.png)
![Example Project](https://s17.postimg.org/4jjj8lavj/Screen_Shot_2016_09_07_at_15_58_43.png)
Run your own OIDC provider in a second. This is a Django app with all the necessary things to work with `django-oidc-provider` package.
@ -31,6 +31,7 @@ Run your provider.
```bash
$ python manage.py migrate
$ python manage.py creatersakey
$ python manage.py createsuperuser
$ python manage.py runserver
```

View file

@ -2,8 +2,8 @@
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "provider_app.settings")
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myapp.settings')
from django.core.management import execute_from_command_line

View file

@ -20,7 +20,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'provider_app',
'myapp',
'oidc_provider',
]
@ -50,9 +50,9 @@ TEMPLATES = [
},
]
ROOT_URLCONF = 'provider_app.urls'
ROOT_URLCONF = 'myapp.urls'
WSGI_APPLICATION = 'provider_app.wsgi.application'
WSGI_APPLICATION = 'myapp.wsgi.application'
# Database

View file

@ -0,0 +1,46 @@
{% load i18n staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{% trans 'OpenID Provider Example' %}</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.2/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="{% static 'css/custom.css' %}">
</head>
<nav class="navbar navbar-fixed-top navbar-light bg-faded">
<div class="container">
<a class="navbar-brand m-b-0" href="{% url 'home' %}">{% trans 'OpenID Provider Example' %}</a>
<form class="form-inline pull-xs-right">
<ul class="nav navbar-nav">
{% if user.is_authenticated %}
{% if user.is_superuser %}
<li class="nav-item"><a class="nav-link" href="{% url 'admin:index' %}"><i class="fa fa-external-link" aria-hidden="true"></i> {% trans 'Admin' %}</a></li>
{% endif %}
<li class="nav-item"><a class="nav-link" href="{% url 'logout' %}"><i class="fa fa-times" aria-hidden="true"></i> {% trans 'Logout' %}</a></li>
{% else %}
<li class="nav-item"><a class="nav-link" href="{% url 'login' %}"><i class="fa fa-sign-in" aria-hidden="true"></i> {% trans 'Login' %}</a></li>
<li class="nav-item"><a class="nav-link" href="https://github.com/juanifioren/django-oidc-provider" target="_blank"><i class="fa fa-github-alt"></i> {% trans 'View on Github' %}</a></li>
{% endif %}
</ul>
</form>
</div>
</nav>
<div class="container">
{% block content %}{% endblock %}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.2/js/bootstrap.min.js"></script>
</body>
</html>

View file

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% load i18n staticfiles %}
{% block content %}
<section class="text-xs-center">
<div class="container">
<h1 class="jumbotron-heading">{% trans 'Welcome' %}{% if user.is_authenticated %} {{ user.username }}{% endif %}!</h1>
<p class="lead text-muted">{% trans 'This is an example of an OpenID Connect 1.0 Provider. Built with the <a href="https://www.djangoproject.com/" target="_BLANK"><u>Django Framework</u></a> and <a href="https://github.com/juanifioren/django-oidc-provider" target="_BLANK"><u>django-oidc-provider</u></a> package.' %}</p>
<p><a href="{% url 'admin:index' %}oidc_provider/client/" class="btn btn-primary btn-lg">{% trans 'Create your clients' %}</a></p>
</div>
</section>
{% endblock %}

View file

@ -0,0 +1,29 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
{% if form.errors %}
<div class="alert alert-danger" role="alert">
{% trans 'Your username and password didn\'t match. Please try again.' %}
</div>
{% endif %}
<fieldset class="form-group">
<input class="form-control form-control-lg" type="text" name="username" placeholder="{% trans 'Username' %}">
</fieldset>
<fieldset class="form-group">
<input class="form-control form-control-lg" type="password" name="password" placeholder="{% trans 'Password' %}">
</fieldset>
<fieldset class="form-group">
<input class="btn btn-primary btn-lg btn-block" type="submit" value="{% trans 'Enter' %}" />
</fieldset>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,25 @@
{% extends 'base.html' %}
{% load i18n staticfiles %}
{% block content %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h2>{% trans 'Request for Permission' %}</h2>
<p class="lead">Client <i>{{ client.name }}</i> would like to access this information of you.</p>
<form method="post" action="{% url 'oidc_provider:authorize' %}">
{% csrf_token %}
{{ hidden_inputs }}
<ul>
{% for scope in scopes %}
<li><strong>{{ scope.name }}</strong> <br><i class="text-muted">{{ scope.description }}</i></li>
{% endfor %}
</ul>
<br>
<input type="submit" class="btn btn-primary btn-block btn-lg" name="allow" value="{% trans 'Accept' %}" />
<input type="submit" class="btn btn-secondary btn-block" value="{% trans 'Decline' %}" />
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h2>{{ error }}</h2>
<p class="lead">{{ description }}</p>
</div>
</div>
{% endblock %}

View file

@ -1,5 +1,5 @@
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "provider_app.settings")
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myapp.settings')
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

View file

@ -1,15 +0,0 @@
body {
background-color: #536dfe;
display: flex;
min-height: 100vh;
flex-direction: column;
}
#main-container {
flex: 1 0 auto;
padding-top: 40px;
}
footer {
padding-top: 0px !important;
}

View file

@ -1,51 +0,0 @@
{% load i18n %}
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>OpenID Provider Example</title>
<link rel="stylesheet" href="http://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css">
<link rel="stylesheet" type="text/css" href="{% static 'css/custom.css' %}">
</head>
<div class="navbar-fixed">
<nav class="white">
<div class="nav-wrapper">
<a href="{% url 'home' %}" class="brand-logo center black-text">OpenID Provider</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
{% if user.is_authenticated %}
{% if user.is_superuser %}
<li><a href="{% url 'admin:index' %}" class="black-text">{% trans 'Admin' %}</a></li>
{% endif %}
<li><a href="{% url 'logout' %}" class="black-text">X</a></li>
{% else %}
<li><a href="{% url 'login' %}" class="black-text"><i class="material-icons left">input</i> {% trans 'Login' %}</a></li>
{% endif %}
</ul>
</div>
</nav>
</div>
<div id="main-container" class="container">
{% block content %}{% endblock %}
</div>
<footer class="page-footer white">
<div class="footer-copyright">
<div class="container black-text"><a href="https://www.linkedin.com/in/juanifioren" target="_BLANK">Created by Juan Ignacio Fiorentino</a><a class="right" href="https://github.com/juanifioren/django-oidc-provider" target="_BLANK">View on Github</a></div>
</div>
</footer>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/js/materialize.min.js"></script>
</body>
</html>

View file

@ -1,28 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
{% block content %}
<div class="row">
<div class="col s12 m10 offset-m1">
<div class="card hoverable">
<div class="card-content">
<h1 class="center-align flow-text indigo-text text-lighten-1">Example of an OpenID Connect 1.0 Provider. Built with the <a href="https://www.djangoproject.com/" target="_BLANK"><u>Django Framework</u></a> and <a href="https://github.com/juanifioren/django-oidc-provider" target="_BLANK"><u>django-oidc-provider</u></a> package.</h1>
<p class="flow-text">Start by creating your clients <a href="{% url 'admin:index' %}oidc_provider/client/">here</a>.</p>
<p class="flow-text">Also check that you've created at least one server key, do it <a href="{% url 'admin:index' %}oidc_provider/rsakey/">here</a>.</p>
<div class="collection with-header">
<div class="collection-header"><h4>Server Endpoints</h4></div>
<a href="{% url 'oidc_provider:provider_info' %}" class="collection-item indigo-text text-lighten-1">{% url 'oidc_provider:provider_info' %}</a>
<a href="{% url 'oidc_provider:jwks' %}" class="collection-item indigo-text text-lighten-1">{% url 'oidc_provider:jwks' %}</a>
<a href="{% url 'oidc_provider:authorize' %}" class="collection-item indigo-text text-lighten-1">{% url 'oidc_provider:authorize' %}</a>
<a href="{% url 'oidc_provider:token' %}" class="collection-item indigo-text text-lighten-1">{% url 'oidc_provider:token' %}</a>
<a href="{% url 'oidc_provider:userinfo' %}" class="collection-item indigo-text text-lighten-1">{% url 'oidc_provider:userinfo' %}</a>
<a href="{% url 'oidc_provider:logout' %}" class="collection-item indigo-text text-lighten-1">{% url 'oidc_provider:logout' %}</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,40 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col s12 m8 offset-m2 l6 offset-l3">
<div class="card hoverable">
<div class="card-content">
<div class="row">
{% if form.errors %}
<h5 class="red-text text-darken-1 flow-text center-align">Your username and password didn't match. Please try again.</h5>
{% endif %}
<form class="col s12" method="post" action="{% url 'login' %}">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<div class="row">
<div class="input-field col s12">
<i class="material-icons prefix">account_circle</i>
<input id="username" name="username" type="text" class="validate">
<label for="username">{% trans 'Username' %}</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<i class="material-icons prefix">lock</i>
<input id="password" name="password" type="password" class="validate">
<label for="password">{% trans 'Password' %}</label>
</div>
</div>
<input class="waves-effect waves-light btn-large right green" type="submit" value="{% trans 'Enter' %}" />
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,27 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col s12 m8 offset-m2 l6 offset-l3">
<h4 class="grey-text text-lighten-3">Request for Permission</h4>
<div class="card hoverable">
<div class="card-content">
<p class="flow-text">Client <i>{{ client.name }}</i> would like to access this information of you.</p>
<form method="post" action="{% url 'oidc_provider:authorize' %}">
{% csrf_token %}
{{ hidden_inputs }}
<ul class="collection">
{% for scope in params.scope %}
<li class="collection-item">{{ scope | capfirst }}</li>
{% endfor %}
</ul>
<input class="waves-effect waves-light btn grey" type="submit" value="Cancel" />
<input name="allow" class="waves-effect waves-light btn green right" type="submit" value="Authorize" />
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,16 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col s12 m8 offset-m2 l6 offset-l3">
<div class="card hoverable">
<div class="card-content">
<h4 class="center-align red-text text-lighten-1">{{ error }}</h4>
<p>{{ description }}</p>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -39,7 +39,7 @@ class ClientForm(ModelForm):
elif (self.cleaned_data['client_type'] == 'confidential') and instance.client_secret:
secret = instance.client_secret
else:
if (instance.client_type == 'confidential'):
if (self.cleaned_data['client_type'] == 'confidential'):
secret = md5(uuid4().hex.encode()).hexdigest()
return secret
@ -47,7 +47,7 @@ class ClientForm(ModelForm):
@admin.register(Client)
class ClientAdmin(admin.ModelAdmin):
form = ClientForm
list_display = ['name', 'client_id', 'response_type', 'date_created']
readonly_fields = ['date_created']
@ -56,14 +56,14 @@ class ClientAdmin(admin.ModelAdmin):
@admin.register(Code)
class CodeAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return False
@admin.register(Token)
class TokenAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return False

View file

@ -171,6 +171,7 @@ def userinfo(request, *args, **kwargs):
dic.update(extra_claims.create_response_dic())
response = JsonResponse(dic, status=200)
response['Access-Control-Allow-Origin'] = '*'
response['Cache-Control'] = 'no-store'
response['Pragma'] = 'no-cache'
@ -203,7 +204,10 @@ class ProviderInfoView(View):
dic['token_endpoint_auth_methods_supported'] = ['client_secret_post',
'client_secret_basic']
return JsonResponse(dic)
response = JsonResponse(dic)
response['Access-Control-Allow-Origin'] = '*'
return response
class JwksView(View):