I am writing a Django framework based on Django Rest Framework with Redis enabled for authenticated sessions. That way my downstream micro-sevices can use the same session / cookie information and jwt token from the user_service.
Basically i'm trying to have a single microservice that controls user authentication with jwt tokens so my react app can authenticate against the user service and use the token/ session for all downstream services.
I have three services: user_service - contains all the user information and sets the authenticated session friend_service - uses user_service session and jwt for authentication tracking_service - uses user_service session and jwt token for authentication
The Problem: The downstream services even via the Django Rest Framework UI the session isn't shared.
What I've Discovered: Redis has the session token: :1:django.contrib.sessions.cached3bq2jfv2r88hcdqezlfmgng1hbyauzf The web browser cookie for webapp is set to: 3bq2jfv2r88hcdqezlfmgng1hbyauzf The settings files, and Django secret key are identical between the services
This was working originally with just one downstream microservice but when I added the second microservice tracking_service it stopped working for all microservices. Everything I've read says you just have to make sure the cache setup is correct. Which I've done redis is getting the information but the sessions aren't working.
# user_services settings.py
"""
Django settings for user_service project.
Generated by 'django-admin startproject' using Django 4.2.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
from datetime import timedelta
import sys
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-n(=y0cb^colps6vjei%_s5lla81d3-l-kn$g#c_y(s0^-5zq$-'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ["webapp.io"]
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.sessions',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.messages',
'django.contrib.staticfiles',
'user_service_api',
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'drf_yasg',
'django_q',
]
Q_CLUSTER = {
'name': 'DjangoORM',
'workers': 4,
'timeout': 90,
'retry': 120,
'queue_limit': 50,
'bulk': 10,
'orm': 'default',
'broker': 'sqs', # Specify the broker as SQS
'sqs': {
'aws_access_key_id': '<AWS_ACCESS_KEY>', # Default for LocalStack
'aws_secret_access_key': '<AWS_SECRET_KEY>', # Default for LocalStack
'aws_region': 'us-west-2',
},
}
# Use Redis as session engine
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
#SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
SESSION_CACHE_ALIAS = "default"
SESSION_COOKIE_NAME = 'webapp'
SESSION_COOKIE_DOMAIN = 'webapp.io'
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
#SESSION_COOKIE_HTTPONLY = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
# Use the cache-based session serializer
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
# Application definition
# Set up cache settings
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://localhost:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), # Token expires in 60 minutes
'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # Change as per your requirement
}
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
ROOT_URLCONF = 'user_service.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'user_service.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'level': 'INFO', # You can set this to DEBUG for more verbose output
'class': 'logging.StreamHandler',
'stream': sys.stdout, # Log to stdout
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO', # You can set to DEBUG, INFO, WARNING, ERROR, or CRITICAL
'propagate': True,
},
# Add more loggers here if needed (e.g., for your Django apps)
},
}
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
`
**friend_service settings.py**
`
"""
Django settings for friend_service project.
Generated by 'django-admin startproject' using Django 4.2.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
from datetime import timedelta
import sys
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-n(=y0cb^colps6vjei%_s5lla81d3-l-kn$g#c_y(s0^-5zq$-'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ["webapp.io"]
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.sessions',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.messages',
'django.contrib.staticfiles',
'friend_service_api',
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'drf_yasg',
'django_q',
]
Q_CLUSTER = {
'name': 'DjangoORM',
'workers': 4,
'timeout': 90,
'retry': 120,
'queue_limit': 50,
'bulk': 10,
'orm': 'default',
'broker': 'sqs', # Specify the broker as SQS
'sqs': {
'aws_access_key_id': '<AWS_ACCESS_KEY>', # Default for LocalStack
'aws_secret_access_key': '<AWS_SECRET_KEY>', # Default for LocalStack
'aws_region': 'us-west-2',
},
}
# Use Redis as session engine
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
#SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
SESSION_CACHE_ALIAS = "default"
SESSION_COOKIE_NAME = 'webapp'
SESSION_COOKIE_DOMAIN = 'webapp.io'
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
#SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
#SESSION_COOKIE_HTTPONLY = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
# Set up cache settings
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://localhost:6379/1", # Update with your Redis server address and port
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
# Use the cache-based session serializer
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
# Application definition
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), # Token expires in 60 minutes
'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # Change as per your requirement
}
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
ROOT_URLCONF = 'friend_service.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'friend_service.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'level': 'INFO', # You can set this to DEBUG for more verbose output
'class': 'logging.StreamHandler',
'stream': sys.stdout, # Log to stdout
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO', # You can set to DEBUG, INFO, WARNING, ERROR, or CRITICAL
'propagate': True,
},
# Add more loggers here if needed (e.g., for your Django apps)
},
}
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
`
**tracking_service settings.py
**
`"""
Django settings for tracking_service project.
Generated by 'django-admin startproject' using Django 4.2.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
from datetime import timedelta
import sys
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-n(=y0cb^colps6vjei%_s5lla81d3-l-kn$g#c_y(s0^-5zq$-'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ["webapp.io"]
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.sessions',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.messages',
'django.contrib.staticfiles',
'tracking_service_api',
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'drf_yasg',
'django_q',
]
Q_CLUSTER = {
'name': 'DjangoORM',
'workers': 4,
'timeout': 90,
'retry': 120,
'queue_limit': 50,
'bulk': 10,
'orm': 'default',
'broker': 'sqs', # Specify the broker as SQS
'sqs': {
'aws_access_key_id': '<ACCESS_KEY_ID>', # Default for LocalStack
'aws_secret_access_key': '<SECRET_KEY_ID>', # Default for LocalStack
'aws_region': 'us-west-2',
},
}
# Use Redis as session engine
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
#SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
SESSION_CACHE_ALIAS = "default"
SESSION_COOKIE_NAME = 'webapp'
SESSION_COOKIE_DOMAIN = 'webapp.io'
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
#SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
#SESSION_COOKIE_HTTPONLY = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
# Set up cache settings
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://localhost:6379/1", # Update with your Redis server address and port
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), # Token expires in 60 minutes
'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # Change as per your requirement
}
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
ROOT_URLCONF = 'tracking_service.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'tracking_service.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'level': 'INFO', # You can set this to DEBUG for more verbose output
'class': 'logging.StreamHandler',
'stream': sys.stdout, # Log to stdout
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO', # You can set to DEBUG, INFO, WARNING, ERROR, or CRITICAL
'propagate': True,
},
# Add more loggers here if needed (e.g., for your Django apps)
},
}
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
I've tried every variation that I can find on the internet to do this. It was working until I added the third microservice so not sure what caused it to break.
I've validated the cookie in the browser when on the UI for all three microservices is the same and recorded in redis db for the session. The only one it works on is the user_service it doesn't get shared to the downstream apps although it should based on everything I've read and the fact that it was working before. I don't know why its not working now I've tried everything.
I'm expecting all micro services to use the user_service session / jwt token.