quarta-feira, junho 07, 2017

Django OAuth server and Flask OAuth consumer with Django Outh Toolkit

Hi everyone!

I'm starting the integration of an app that will serve OAuth authentication method in a Django project. Django OAuth Toolkit appeared as a good solution, however the documentation was a little confused to me and I had a hard way of testing to understand how it works.

For this tutorial I'm using



The Server

First of all, create a Django project:

django-admin.py startproject my_oauth_server

After that, let's follow the steps at the oficial tutorial (assuming you have already installed the packages above):

Add oauth2_provider and corsheader in your settings.py

    # ...

Add the toolkit urls:

urlpatterns = patterns(
    url(r'^admin/', include(admin.site.urls)),
    url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
    # ...

And the middleware:

    # ...
    # ...

And add this in settings:


Now let's create the authentication system for our Django:

django-admin.py startapp my_authentication

By the simplest way, create the view:

from django.http.response import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.contrib.auth import authenticate, login, logout

# Create your views here.

def login_page(request):
    next_page = request.GET.get('next', '/')
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        login(request, user)
        return HttpResponseRedirect(next_page)

    return render(request, 'login.html', {'next': next_page})

def logout_page(request):
    return HttpResponseRedirect('/')

def home(request):
    return HttpResponse("Logged in: %s" % request.user.is_authenticated())

Now create the template login.html:

<form method="post">    {% csrf_token %}
    <input type="hidden" name="next" value="{{ next }}" />    <p>        username
        <input type="text" name="username" />    </p>    <p>        password <input type="password" name="password" />    </p>    <button type="submit" >Login</button></form>

Actually, it's stupid simple, just to have an exemple. The "next" input receives the value from the url. This is required to were go after login. 
Finally, add your app urls:

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls), 
    url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),  
    url(r'^$', 'authorization_server.views.home'), 
    url(r'^accounts/login/$', 'authorization_server.views.login_page'), 
    url(r'^logout/$', 'authorization_server.views.logout_page'),]

Just run the migrate command and create an user to authorize using oauth. Create an an app at:
/o/applications. Copy the client id and client secret. Put the redirect url to /consumer at your address (in my case, the consumer will run at port 9100

The consumer

I got this exemple and applyed to my project. you can see the code bellow:

from flask import Flask
from flask import request

app = Flask(__name__)

CLIENT_ID = "Vkh2wibaQZLaxrvhYQUq8I5WWdqRLmQJXQc2YxPB"CLIENT_SECRET = "czsU5umPX2mrLqKTlLV6YNSgbPLQKxdp2WsOlWJkIYJjXP4rSrnwEwZJlAkCo7fjlCCPo3ReLMGazjeMWO5ujtjAJEOLIsTWcrE18PxwiioQMOQxTvEQQliMSCOLxEnl"REDIRECT_URI = "http://localhost:9100/consumer"
from flask import Flask

app = Flask(__name__)

# Left as an exercise to the reader.# You may want to store valid states in a database or memcache,# or perhaps cryptographically sign them and verify upon retrieval.def save_created_state(state):
def is_valid_state(state):
    return True

import requests
import requests.auth

def get_token(code):
    client_auth = requests.auth.HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET)
    post_data = {"grant_type": "authorization_code",                 "code": code,                 "redirect_uri": REDIRECT_URI}
    response = requests.post("http://localhost:9000/o/token",                             auth=client_auth,                             data=post_data)
    token_json = response.json()
    return token_json["access_token"]

from flask import abort, request
def reddit_callback():
    error = request.args.get('error', '')
    if error:
        return "Error: " + error
    state = request.args.get('state', '')
    if not is_valid_state(state):
        # Uh-oh, this request wasn't started by us!        abort(403)
    code = request.args.get('code')
    # We'll change this next line in just a moment    return "got a code! %s" % code

if __name__ == '__main__':
    app.run(debug=True, port=9100)

def get_username(access_token):
    headers = {"Authorization": "bearer " + access_token}
    response = requests.get("http://localhost:9000/user_data", headers=headers)
    me_json = response.json()
    return me_json['username']

Run the flask server. Here I used the port 9000 to the server and 9100 for the consumer. The authentication url:


Use the right attribute values for client id. With this you can log users in your application with oauth and consume that.