Django v1.7
I've been working through this book (which is fantastic, btw), and I thought I had everything working. All of the tests (functional and unit), but for some reason, every time I hit the logout
button, I am immediately logged back in. I am using a custom authentication backend (the one given in the book), which uses Mozilla Persona, as described in the book (the chapter in the link).
I have seen a few similar posts, but none of the solutions helped.
Behaviour:
Persona says no. Json was: {'status': 'failure', 'reason': 'audience mismatch: domain mismatch'}
--I accidentally discovered
this by visiting 127.0.0.1:8000
instead of localhost:8000
*). I
do not know if Persona is queried each time or if the message is
kept after each page refresh.The initial login appears to work correctly. The persona pop-up appears and goes through the steps before closing and reloading the page.
After logging out, the page will refresh, sending post information and logging back in:
[06/Nov/2015 21:25:20] "GET /accounts/logout HTTP/1.1" 302 0
[06/Nov/2015 21:25:20] "GET / HTTP/1.1" 200 795
[06/Nov/2015 21:25:21] "POST /accounts/login HTTP/1.1" 200 2
The logout is redirected back to the root page '/'.
This behaviour is persistent through restarting the server and the web browser. If I stop the server and close the browser and reopen both (entering in the web address again), the page is already logged in.
This behaviour also persists through different git branches. I am not sure when it started (because the tests still pass), but I know it worked previously. Every branch I checked has the same issue, which makes me think it's something to do with the cache or installation.
The behaviour also persists through removing all __pycache__, migrations, and the database itself.
The behaviour persists through clearing the cache. (EDIT/UPDATE: I'm still writing this, so it's not technically an update...I had previously emptied the cache only for 'Today' (Firefox), which had no effect; however, I just cleared everything, and it seems to have solved the problem. I need to do more testing; I'll update once I'm sure.)
*I do know that Persona says to use the ip over localhost
, but it doesn't seem to make a difference.
Here's a minimal working example:
placeholder.html (NOTE: I put the scripts in the body.
{% load staticfiles %}
<html>
<head>
<title>placeholder</title>
</head>
<body>
{% if user.email %}
<h3>User: {{ user.email }}</h3>
<div class="item">
Logged in as <b>{{ user.email }}</b>
</div>
<div class="item">
<a class="ui button" id="id_logout"
href="{% url 'logout' %}">Log out</a>
</div>
{% else %}
<div class="item">
<a class="ui button" id="id_login" href="#">Sign in</a>
</div>
{% endif %}
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="https://login.persona.org/include.js"></script>
<script>
var initialize = function(navigator, user, token, urls) {
console.log('called initialize');
$('#id_login').on('click', function() {
navigator.id.request();
});
navigator.id.watch({
loggedInUser: user,
onlogin: function(assertion) {
$.post(
urls.login,
{'assertion': assertion, 'csrfmiddlewaretoken': token}
)
.done(function() { window.location.reload(); })
.fail(function() { navigator.id.logout(); });
},
onlogout: function() {}
});
};
window.MyModule = window.MyModule || {
Accounts: {
initialize: initialize
}
};
</script>
<script>
/*global $, MyModule, navigator */
$(document).ready(function () {
var user = "{{ user.email }}" || null;
var token = "{{ csrf_token }}";
var urls = {
login: "{% url 'persona_login' %}",
logout: "TODO",
};
MyModule.Accounts.initialize(navigator, user, token, urls);
});
</script>
</body>
</html>
accounts/views.py (NOTE: I was using Django native auth.logout, which you will see in urls.py)
from django.contrib.auth import authenticate, login
from django.contrib.auth import logout as auth_logout
from django.http import HttpResponse
from django.shortcuts import redirect
def persona_login(request):
user = authenticate(assertion=request.POST['assertion'])
if user is not None:
login(request, user)
return HttpResponse('OK')
def logout(request, next_page):
auth_logout(request)
return redirect('/')
urls.py
from django.conf.urls import patterns, include, url
#from django.contrib import admin
urlpatterns = patterns(
'',
# url(r'^admin/', include(admin.site.urls)),
url(r'^accounts/', include('apps.accounts.urls')),
url(r'^$', 'apps.projects.views.placeholder_view',
name='placeholder'),
)
accounts/urls.py (NOTE: I was using the native logout--neither method works)
from django.conf.urls import patterns, url
# from django.contrib.auth.views import logout
urlpatterns = patterns(
'',
url(r'^login$', 'remsci.apps.accounts.views.persona_login',
name='persona_login'),
# url(r'^logout$', logout,
# {'next_page': '/'}, name='logout'),
url(r'^logout$', 'remsci.apps.accounts.views.logout',
{'next_page': '/'}, name='logout'),
authentication.py (NOTE: This is directly from the book)
import requests
from django.conf import settings
from django.contrib.auth import get_user_model
User = get_user_model()
PERSONA_VERIFY_URL = 'https://verifier.login.persona.org/verify'
import logging
log = logging.getLogger(__name__)
class PersonaAuthenticationBackend(object):
def authenticate(self, assertion):
response = requests.post(
PERSONA_VERIFY_URL,
data={'assertion': assertion, 'audience': settings.DOMAIN}
)
if response.ok and response.json()['status'] == 'okay':
email = response.json()['email']
try:
return User.objects.get(email=email)
except User.DoesNotExist:
return User.objects.create(email=email)
else:
log.warning(
'Persona says no. Json was: {}'.format(response.json()))
def get_user(self, email):
try:
return User.objects.get(email=email)
except User.DoesNotExist:
return None
EDIT/UPDATE Again, while I was writing this. It would appear that this problem has already been solved here. I have a physical copy of the book, but going back through and verifying everything using the online copy, I just found the link pointing to this code. I'll leave this up, just in case someone else is having this problem...or, I'll remove it if I can find the same problem pointing to the same solution.
From above:
It would appear that this problem has already been solved here. I have a physical copy of the book, but going back through and verifying everything using the online copy, I just found the link pointing to this code. I'll leave this up, just in case someone else is having this problem...or, I'll remove it if I can find the same problem pointing to the same solution.