I have a multi tenant app using the library django-tenants
. When logging into the core URL with the created superuser, the login page works completely fine. When using the subdomain address to login, the page returns a 500 error
when using the correct username and password that exists and the console says Uncaught (in promise) SyntaxError: Unexpected token '<', " <!doctype "... is not valid JSON
.
When using an login username that doesn’t exist, the output is 400 Bad Request
.
The odd part, is I can access the admin panel of a subdomain, log in completely fine, change the URL to go to a part of the app – and the user is now logged in. After being logged in, I can submit forms completely fine and data is being captured, it is solely the login page on a subdomain that has an error submitting.
I am lost since subsequent data forms submit and go correctly, and there are no invalid tokens (from what I see)
views.py
def dual_login_view(request):
"""Authenticate users with both Django Authentication System
and Django Rest Framework"""
if request.method == "POST":
username = request.POST.get("login")
password = request.POST.get("password")
user = authenticate(request, username=username, password=password)
if user is not None:
# Login user and create session
login(request, user)
# Create or get API token
token, created = Token.objects.get_or_create(user=user)
response_data = {"status": "success", "token": token.key}
return JsonResponse(response_data)
else:
return JsonResponse(
{"status": "error", "message": "Username with the password doesn't exist!"}, status=400
)
return JsonResponse(
{"status": "error", "message": "Invalid request method"}, status=405
)
class CustomLoginView(LoginView):
def form_invalid(self, form):
for error in form.errors.values():
for e in error:
messages.error(self.request, e)
return super().form_invalid(form)
class UserLoginAPIView(APIView):
def post(self, request):
if not (request.user and request.user.is_authenticated):
return Response(
{"error": "User not recognized"}, status=status.HTTP_401_UNAUTHORIZED
)
try:
token, created = Token.objects.get_or_create(user=request.user)
return Response({"token": f"Token {token.key}"}, status=status.HTTP_200_OK)
except Exception as e:
print(e)
return Response(
{"error": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED
)
class UserLoginAPIView(APIView):
def post(self, request):
"""Handle user login and return an authentication token."""
username = request.data.get("username")
password = request.data.get("password")
try:
user = MSPAuthUser.objects.get(username=username)
# Verify password
if not user.check_password(password):
raise ValueError("Invalid credentials")
token, created = Token.objects.get_or_create(user=user)
return Response({"token": f"Token {token.key}"}, status=status.HTTP_200_OK)
except ObjectDoesNotExist:
return Response(
{"error": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED
)
except ValueError as e:
return Response({"error": str(e)}, status=status.HTTP_401_UNAUTHORIZED)
login.js
<script>
let form = document.querySelector('form');
form.addEventListener('submit', function(event) {
event.preventDefault();
var formData = new FormData(this);
form.classList.add('was-validated')
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
return;
}
fetch("{% url 'dual_login' %}", {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': formData.get('csrfmiddlewaretoken'),
}
}).then(response => response.json())
.then(data => {
if (data.status === 'success') {
// Store the token (e.g., in local storage or a cookie)
localStorage.setItem('token', data.token);
// Redirect or update the UI
window.location.href = '/';
} else {
Swal.fire({
icon: 'error',
title: data.message,
showConfirmButton: false,
timer: 1500,
})
}
});
});
</script>
EDIT
Adding network response:
NETWORK RESPONSE:
1 requests
757 B transferred
145 B resources
Request URL:
https://subdomain.primarydomain.com
Request Method:
POST
Status Code:
500 Internal Server Error
Remote Address:
1xx.1xx.xxx.xxx:443
Referrer Policy:
same-origin
HTTP/1.1 500 Internal Server Error
Server: nginx/1.26.0 (Ubuntu)
Date: Fri, 13 Dec 2024 18:41:39 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 145
Connection: keep-alive
Vary: Cookie, origin
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
access-control-allow-origin: https://probleu.rocket-command.com
Set-Cookie: csrftoken=Wx.....jO; expires=Fri, 12 Dec 2025 18:41:39 GMT; Max-Age=31449600; Path=/; SameSite=Lax; Secure
Strict-Transport-Security: max-age=31536000; includeSubDomains
The payload
includes username, password, as well as the CSRF token.
EDIT 2
I created a log of the application and it spat back the following error:
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/root/MSPDashboard/msp-dashboard/venv/lib/python3.12/site-packages/django/db/backends/base/base.py", line 313>
return self.connection.commit()
^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.ForeignKeyViolation: insert or update on table "authtoken_token" violates foreign key constraint "aut>
DETAIL: Key (user_id)=(d34fd88e-b7bb-4f5c-880a-43f1cb98230d) is not present in table "accounts_mspauthuser".
This line appears to be the error since the user
- does exist (photo below) just not in the
public
schema:
token, created = Token.objects.get_or_create(user=user)
So the question becomes, how can I use from rest_framework.authtoken.models to query from the proper database schema?
2
Answers
Check the network tab for API response. Maybe you are trying to parse something that is not JSON as JSON.
The
Uncaught (in promise) SyntaxError: Unexpected token '<', " <!doctype "... is not valid JSON
error occurs because theToken
call raises an uncaught exception, as you noticed. Which in turn means thetoken
object doesn’t contain a token, it contains the HTML response displaying the exception response.And you have rightly deduced that the solution is to fix the
Token
call.By the time you make the
Token
call, you’ve already authenticated the user so the next step is to look up their tenant association. Then you can use theschema_context()
context manager to direct the query to the correct schema.