I wanted to create my Oauth2 provider for authentication in my django applications. For the test I used Django OAuth Toolkit and created 2 separate projects on different ports:
http://127.0.0.1:8001 – client
http://127.0.0.1:8000 – provider
Here are all the endpoints for the provider that are redirected to for login and access confirmation:
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
path('admin/', admin.site.urls),
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
path('accounts/login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),
The code for the login page:
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
</body>
</html>
On the provider’s side everything goes normally and I get to the page where I confirm the user and I am redirected to a special page of the callback client.
Client endpoints:
from django.contrib import admin
from django.urls import path
from client import views
urlpatterns = [
path('admin/', admin.site.urls),
path('client/login/', views.login, name='login'),
path('client/callback/', views.callback, name='callback'),
]
Client Views:
import os
import string
import random
import base64
import hashlib
from django.http import HttpResponse
from django.shortcuts import redirect, render
from requests_oauthlib import OAuth2Session
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
CLIENT_ID = 'hR0E2tAkf8mLqOwNpPPun0dTBDU44JF8z8SgJfnJ'
CLIENT_SECRET = 'hNi3IugtuqtEFa5GonCW4ilQTnsjz2nUE1jf2WeoxaMQNZHt1pzszLGeGtpYsNXV7VHouFF79C7XMOmSJRcXrY4OEPoJuTQyIng6ONjjt4tPvJfpPJpKMFPX9MPgUpps'
REDIRECT_URI = 'http://127.0.0.1:8001/client/callback/'
AUTHORIZATION_URL = 'http://127.0.0.1:8000/o/authorize/'
TOKEN_URL = 'http://127.0.0.1:8000/o/token/'
def generate_code_verifier():
verifier = ''.join(random.choices(string.ascii_letters + string.digits, k=128))
return base64.urlsafe_b64encode(verifier.encode('utf-8')).rstrip(b'=').decode('utf-8')
def generate_code_challenge(verifier):
code_challenge = hashlib.sha256(verifier.encode('utf-8')).digest()
return base64.urlsafe_b64encode(code_challenge).rstrip(b'=').decode('utf-8')
def login(request):
oauth = OAuth2Session(CLIENT_ID, redirect_uri=REDIRECT_URI)
code_verifier = generate_code_verifier()
code_challenge = generate_code_challenge(code_verifier)
authorization_url, state = oauth.authorization_url(
AUTHORIZATION_URL,
code_challenge=code_challenge,
code_challenge_method='S256'
)
request.session['code_verifier'] = code_verifier
print(f"Saved code_verifier in session: {request.session['code_verifier']}")
return redirect(authorization_url)
def callback(request):
code = request.GET.get('code')
code_verifier = request.session.get('code_verifier')
if code_verifier is None:
return HttpResponse("Code verifier is missing in the session.")
print(f"Retrieved code_verifier from session: {code_verifier}")
oauth = OAuth2Session(CLIENT_ID, redirect_uri=REDIRECT_URI)
try:
token = oauth.fetch_token(
TOKEN_URL,
code=code,
client_secret=CLIENT_SECRET,
code_verifier=code_verifier
)
access_token = token.get('access_token')
return render(request, 'callback.html', {'access_token': access_token})
except Exception as e:
print(f"Error fetching token: {e}")
return HttpResponse(f"Error fetching token: {e}")
In the method that is responsible for login on the client side I get code_verifier and it is even displayed in the console, but when I go to callback.html I cannot get it and accordingly I get: “Code verifier is missing in the session”.
I don’t know what I did wrong and why it doesn’t save and I can’t retrieve it? If you need more information I will give it to you.