I have a simple Django (Rest Framework) application. I've enabled properly the CSRF, CORS, Session middlewares. I try to debug the front-end UI written with Backbone and the sessionid and csrftoken aren't in the persistent storage of the browser. To confuse me more, when I logout, I receive the sessionid of the Anonymous user (without the pair csrftoken) and that cookie gets persisted.
I use Google Chrome. Symptoms:
- When i perform the login, I receive in the response the Set-Cookie headers for both tokens
- The tokens have different expiration dates
- The tokens appears in the chrome's response cookies tab, but not in the cookies storage
- If i logout, I receive the sessionid of the anonymous user, without csrftoken and this is persisted as a cookie.
I'm only trying to debug on 127.0.0.1:63342 with the help of Pycharm and Chrome.
Valid settings snippet:
Application definition
INSTALLED_APPS = (
# 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.sessions',
'django.contrib.contenttypes',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'south',
'tenant',
'agriculture',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
# 'rest_framework.authentication.BasicAuthentication',
),
'PAGINATE_BY': 10,
'PAGINATE_BY_PARAM': 'page_size',
}
SESSION_COOKIE_DOMAIN = CSRF_COOKIE_DOMAIN = None
SESSION_COOKIE_AGE = 60 * 60 * 24 * 30
# CORS headers settings
CORS_ORIGIN_WHITELIST = (
'localhost:63342', # List here all the white-listed access points for the API
'127.0.0.1:63342',
...,
)
CORS_ALLOW_CREDENTIALS = True
Relevant views:
class LoginView(APIView):
"""
The view will respond to the login request by using the underlying Django session authentication. In addition to the
default behavior will return rich information about the current user being logged in.
"""
serializer_class = serializers.UserSerializer
def post(self, request, *args, **kwargs):
# Get the parameters from the request
username = request.DATA['username']
password = request.DATA['password']
remember = request.DATA.get('remember', False)
logger.debug('Attempt authentication with %s : "%s"' % (username, password,))
# Attempt authentication
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
# Care for the session
login(request, user)
# se the expiration to 0 if remember wasn't requested
if not remember:
request.session.set_expiry(0)
# Return successful response
logger.debug('Login successfully')
return Response(self.serializer_class(user).data)
else:
logger.warn('User %s is de-activated' % username)
return Response(status=status.HTTP_403_FORBIDDEN)
else:
logger.debug('Unauthorized access with %s : "%s"' % (username, password,))
return Response(status=status.HTTP_401_UNAUTHORIZED)
class AuthenticateView(APIView):
"""
Based on the received session token, we will check if the session is still valid, meaning that we will check if the
user is authenticated. If the request gets to be processed, means that the session token is still valid, otherwise
we will issue an 401 status. If the session is valid, then return the user data.
"""
permission_classes = (IsAuthenticated,)
serializer_class = serializers.UserSerializer
def get(self, request, *args, **kwargs):
return Response(self.serializer_class(request.user).data)
class LogoutView(APIView):
"""
Will simply care to logout the user which was logged in. Will use the default behavior form Django, which doesn't
require that the uses is logged in.
"""
def post(self, request, *args, **kwargs):
logout(request)
return Response(status=status.HTTP_200_OK)
Response, when you should be sending an instance ofRequestContext, but more importantly - if external clients will be usingPOST, then you should not expose your CSRF token (your endpoints should be exempt). So you really should be disabled CSRF for your API. - Burhan Khalid