1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-21 05:49:37 +02:00

Initial migration to new session based auth system with AllAuth

This commit is contained in:
Sean Morley 2024-11-29 14:41:13 -05:00
parent 7defdac3a8
commit 9bc20be70e
24 changed files with 313 additions and 773 deletions

View file

@ -11,3 +11,13 @@ class AppVersionMiddleware:
response['X-AdventureLog-Version'] = '1.0.0' response['X-AdventureLog-Version'] = '1.0.0'
return response return response
# make a middlewra that prints all of the request cookies
class PrintCookiesMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print(request.COOKIES)
response = self.get_response(request)
return response

View file

@ -73,6 +73,7 @@ class AdventureViewSet(viewsets.ModelViewSet):
return queryset.order_by(ordering) return queryset.order_by(ordering)
def get_queryset(self): def get_queryset(self):
print(self.request.user)
# if the user is not authenticated return only public adventures for retrieve action # if the user is not authenticated return only public adventures for retrieve action
if not self.request.user.is_authenticated: if not self.request.user.is_authenticated:
if self.action == 'retrieve': if self.action == 'retrieve':

View file

@ -47,10 +47,11 @@ INSTALLED_APPS = (
'django.contrib.sites', 'django.contrib.sites',
'rest_framework', 'rest_framework',
'rest_framework.authtoken', 'rest_framework.authtoken',
'dj_rest_auth', # 'dj_rest_auth',
'allauth', 'allauth',
'allauth.account', 'allauth.account',
'dj_rest_auth.registration', 'allauth.headless',
# 'dj_rest_auth.registration',
'allauth.socialaccount', 'allauth.socialaccount',
'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.facebook',
'drf_yasg', 'drf_yasg',
@ -113,6 +114,7 @@ DATABASES = {
} }
} }
ACCOUNT_SIGNUP_FORM_CLASS = 'users.form_overrides.CustomSignupForm'
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.7/topics/i18n/ # https://docs.djangoproject.com/en/1.7/topics/i18n/
@ -157,16 +159,6 @@ TEMPLATES = [
}, },
] ]
REST_AUTH = {
'SESSION_LOGIN': True,
'USE_JWT': True,
'JWT_AUTH_COOKIE': 'auth',
'JWT_AUTH_HTTPONLY': False,
'REGISTER_SERIALIZER': 'users.serializers.RegisterSerializer',
'USER_DETAILS_SERIALIZER': 'users.serializers.CustomUserDetailsSerializer',
'PASSWORD_RESET_SERIALIZER': 'users.serializers.MyPasswordResetSerializer'
}
DISABLE_REGISTRATION = getenv('DISABLE_REGISTRATION', 'False') == 'True' DISABLE_REGISTRATION = getenv('DISABLE_REGISTRATION', 'False') == 'True'
DISABLE_REGISTRATION_MESSAGE = getenv('DISABLE_REGISTRATION_MESSAGE', 'Registration is disabled. Please contact the administrator if you need an account.') DISABLE_REGISTRATION_MESSAGE = getenv('DISABLE_REGISTRATION_MESSAGE', 'Registration is disabled. Please contact the administrator if you need an account.')
@ -181,8 +173,16 @@ STORAGES = {
AUTH_USER_MODEL = 'users.CustomUser' AUTH_USER_MODEL = 'users.CustomUser'
ACCOUNT_ADAPTER = 'users.adapters.NoNewUsersAccountAdapter'
FRONTEND_URL = getenv('FRONTEND_URL', 'http://localhost:3000') FRONTEND_URL = getenv('FRONTEND_URL', 'http://localhost:3000')
# HEADLESS_FRONTEND_URLS = {
# "account_confirm_email": "https://app.project.org/account/verify-email/{key}",
# "account_reset_password_from_key": "https://app.org/account/password/reset/key/{key}",
# "account_signup": "https://app.org/account/signup",
# }
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
SITE_ID = 1 SITE_ID = 1
ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_EMAIL_REQUIRED = True
@ -228,12 +228,14 @@ SWAGGER_SETTINGS = {
'LOGOUT_URL': 'logout', 'LOGOUT_URL': 'logout',
} }
# For demo purposes only. Use a white list in the real world.
CORS_ORIGIN_ALLOW_ALL = True
from os import getenv from os import getenv
CORS_ALLOWED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()]
CSRF_TRUSTED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()] CSRF_TRUSTED_ORIGINS = [origin.strip() for origin in getenv('CSRF_TRUSTED_ORIGINS', 'http://localhost').split(',') if origin.strip()]
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
LOGGING = { LOGGING = {
@ -260,6 +262,7 @@ LOGGING = {
}, },
}, },
} }
# https://github.com/dr5hn/countries-states-cities-database/tags # https://github.com/dr5hn/countries-states-cities-database/tags
COUNTRY_REGION_JSON_VERSION = 'v2.4' COUNTRY_REGION_JSON_VERSION = 'v2.4'
SESSION_SAVE_EVERY_REQUEST = True

View file

@ -4,7 +4,7 @@ from django.views.generic import RedirectView, TemplateView
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from adventures import urls as adventures from adventures import urls as adventures
from users.views import ChangeEmailView, IsRegistrationDisabled, PublicUserListView, PublicUserDetailView from users.views import ChangeEmailView, IsRegistrationDisabled, PublicUserListView, PublicUserDetailView, UserMetadataView
from .views import get_csrf_token from .views import get_csrf_token
from drf_yasg.views import get_schema_view from drf_yasg.views import get_schema_view
@ -25,6 +25,10 @@ urlpatterns = [
path('auth/users/', PublicUserListView.as_view(), name='public-user-list'), path('auth/users/', PublicUserListView.as_view(), name='public-user-list'),
path('auth/user/<uuid:user_id>/', PublicUserDetailView.as_view(), name='public-user-detail'), path('auth/user/<uuid:user_id>/', PublicUserDetailView.as_view(), name='public-user-detail'),
path('auth/user-metadata/', UserMetadataView.as_view(), name='user-metadata'),
path('csrf/', get_csrf_token, name='get_csrf_token'), path('csrf/', get_csrf_token, name='get_csrf_token'),
re_path(r'^$', TemplateView.as_view( re_path(r'^$', TemplateView.as_view(
template_name="home.html"), name='home'), template_name="home.html"), name='home'),
@ -64,11 +68,15 @@ urlpatterns = [
re_path(r'^auth/', include('dj_rest_auth.urls')), re_path(r'^auth/', include('dj_rest_auth.urls')),
re_path(r'^auth/registration/', re_path(r'^auth/registration/',
include('dj_rest_auth.registration.urls')), include('dj_rest_auth.registration.urls')),
re_path(r'^account/', include('allauth.urls')), # re_path(r'^account/', include('allauth.urls')),
re_path(r'^admin/', admin.site.urls), re_path(r'^admin/', admin.site.urls),
re_path(r'^accounts/profile/$', RedirectView.as_view(url='/', re_path(r'^accounts/profile/$', RedirectView.as_view(url='/',
permanent=True), name='profile-redirect'), permanent=True), name='profile-redirect'),
re_path(r'^docs/$', schema_view.with_ui('swagger', re_path(r'^docs/$', schema_view.with_ui('swagger',
cache_timeout=0), name='api_docs'), cache_timeout=0), name='api_docs'),
# path('auth/account-confirm-email/', VerifyEmailView.as_view(), name='account_email_verification_sent'), # path('auth/account-confirm-email/', VerifyEmailView.as_view(), name='account_email_verification_sent'),
path("accounts/", include("allauth.urls")),
# Include the API endpoints:
path("_allauth/", include("allauth.headless.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -1,13 +0,0 @@
{% extends "account/email/base_message.txt" %}
{% load i18n %}
{% block content %}{% autoescape off %}{% blocktrans %}You're receiving this email because you or someone else has requested a password reset for your user account.
It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %}
{{ frontend_url }}/settings/forgot-password/confirm?token={{ temp_key }}&uid={{ user_pk }}
{% if username %}
{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %}{% endif %}{% endautoescape %}{% endblock content %}

View file

@ -0,0 +1,10 @@
from allauth.account.adapter import DefaultAccountAdapter
from django.conf import settings
class NoNewUsersAccountAdapter(DefaultAccountAdapter):
"""
Disable new user registration.
"""
def is_open_for_signup(self, request):
is_disabled = getattr(settings, 'DISABLE_REGISTRATION', False)
return not is_disabled

View file

@ -1,3 +1,10 @@
from django.contrib import admin from django.contrib import admin
# Register your models here from django.contrib.sessions.models import Session
class SessionAdmin(admin.ModelAdmin):
def _session_data(self, obj):
return obj.get_decoded()
list_display = ['session_key', '_session_data', 'expire_date']
admin.site.register(Session, SessionAdmin)

View file

@ -0,0 +1,17 @@
from django import forms
class CustomSignupForm(forms.Form):
first_name = forms.CharField(max_length=30, required=True)
last_name = forms.CharField(max_length=30, required=True)
def signup(self, request, user):
# Delay the import to avoid circular import
from allauth.account.forms import SignupForm
# No need to call super() from CustomSignupForm; use the SignupForm directly if needed
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
# Save the user instance
user.save()
return user

View file

@ -1,55 +1,17 @@
from allauth.account.utils import (filter_users_by_email, user_pk_to_url_str, user_username) from django import forms
from allauth.utils import build_absolute_uri
from allauth.account.adapter import get_adapter
from allauth.account.forms import default_token_generator
from allauth.account import app_settings
from django.conf import settings
from allauth.account.forms import ResetPasswordForm as AllAuthPasswordResetForm class CustomSignupForm(forms.Form):
first_name = forms.CharField(max_length=30, required=True)
last_name = forms.CharField(max_length=30, required=True)
class CustomAllAuthPasswordResetForm(AllAuthPasswordResetForm): def signup(self, request, user):
# Delay the import to avoid circular import
def clean_email(self): from allauth.account.forms import SignupForm
"""
Invalid email should not raise error, as this would leak users
for unit test: test_password_reset_with_invalid_email
"""
email = self.cleaned_data["email"]
email = get_adapter().clean_email(email)
self.users = filter_users_by_email(email, is_active=True)
return self.cleaned_data["email"]
def save(self, request, **kwargs):
email = self.cleaned_data['email']
token_generator = kwargs.get('token_generator', default_token_generator)
for user in self.users:
temp_key = token_generator.make_token(user)
path = f"custom_password_reset_url/{user_pk_to_url_str(user)}/{temp_key}/"
url = build_absolute_uri(request, path)
frontend_url = settings.FRONTEND_URL
# remove ' from frontend_url
frontend_url = frontend_url.replace("'", "")
#Values which are passed to password_reset_key_message.txt
context = {
"frontend_url": frontend_url,
"user": user,
"password_reset_url": url,
"request": request,
"path": path,
"temp_key": temp_key,
'user_pk': user_pk_to_url_str(user),
}
if app_settings.AUTHENTICATION_METHOD != app_settings.AuthenticationMethod.EMAIL:
context['username'] = user_username(user)
get_adapter(request).send_mail(
'account/email/password_reset_key', email, context
)
return self.cleaned_data['email']
# No need to call super() from CustomSignupForm; use the SignupForm directly if needed
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
# Save the user instance
user.save()
return user

View file

@ -1,10 +1,8 @@
from rest_framework import serializers from rest_framework import serializers
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from adventures.models import Adventure, Collection from adventures.models import Collection
from users.forms import CustomAllAuthPasswordResetForm
from dj_rest_auth.serializers import PasswordResetSerializer from dj_rest_auth.serializers import PasswordResetSerializer
from rest_framework.exceptions import PermissionDenied
User = get_user_model() User = get_user_model()
@ -32,77 +30,7 @@ class ChangeEmailSerializer(serializers.Serializer):
return value return value
class RegisterSerializer(serializers.Serializer):
username = serializers.CharField(
max_length=get_username_max_length(),
min_length=allauth_account_settings.USERNAME_MIN_LENGTH,
required=allauth_account_settings.USERNAME_REQUIRED,
)
email = serializers.EmailField(required=allauth_account_settings.EMAIL_REQUIRED)
password1 = serializers.CharField(write_only=True)
password2 = serializers.CharField(write_only=True)
first_name = serializers.CharField(required=False)
last_name = serializers.CharField(required=False)
def validate_username(self, username):
username = get_adapter().clean_username(username)
return username
def validate_email(self, email):
email = get_adapter().clean_email(email)
if allauth_account_settings.UNIQUE_EMAIL:
if email and EmailAddress.objects.is_verified(email):
raise serializers.ValidationError(
_('A user is already registered with this e-mail address.'),
)
return email
def validate_password1(self, password):
return get_adapter().clean_password(password)
def validate(self, data):
if data['password1'] != data['password2']:
raise serializers.ValidationError(_("The two password fields didn't match."))
# check if a user with the same email already exists
if User.objects.filter(email=data['email']).exists():
raise serializers.ValidationError("This email is already in use.")
return data
def custom_signup(self, request, user):
pass
def get_cleaned_data(self):
return {
'username': self.validated_data.get('username', ''),
'password1': self.validated_data.get('password1', ''),
'email': self.validated_data.get('email', ''),
'first_name': self.validated_data.get('first_name', ''),
'last_name': self.validated_data.get('last_name', ''),
}
def save(self, request):
# Check if registration is disabled
if getattr(settings, 'DISABLE_REGISTRATION', False):
raise PermissionDenied("Registration is currently disabled.")
# If registration is not disabled, proceed with the original logic
adapter = get_adapter()
user = adapter.new_user(request)
self.cleaned_data = self.get_cleaned_data()
user = adapter.save_user(request, user, self, commit=False)
if "password1" in self.cleaned_data:
try:
adapter.clean_password(self.cleaned_data['password1'], user=user)
except DjangoValidationError as exc:
raise serializers.ValidationError(
detail=serializers.as_serializer_error(exc)
)
user.save()
self.custom_signup(request, user)
setup_user_email(request, user, [])
return user
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@ -116,20 +44,6 @@ from rest_framework import serializers
from django.conf import settings from django.conf import settings
import os import os
# class AdventureSerializer(serializers.ModelSerializer):
# image = serializers.SerializerMethodField()
# class Meta:
# model = Adventure
# fields = ['id', 'user_id', 'type', 'name', 'location', 'activity_types', 'description',
# 'rating', 'link', 'image', 'date', 'trip_id', 'is_public', 'longitude', 'latitude']
# def get_image(self, obj):
# if obj.image:
# public_url = os.environ.get('PUBLIC_URL', '')
# return f'{public_url}/media/{obj.image.name}'
# return None
class UserDetailsSerializer(serializers.ModelSerializer): class UserDetailsSerializer(serializers.ModelSerializer):
""" """
User model w/o password User model w/o password
@ -203,13 +117,3 @@ class CustomUserDetailsSerializer(UserDetailsSerializer):
representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}" representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}"
del representation['pk'] # remove the pk field from the response del representation['pk'] # remove the pk field from the response
return representation return representation
class MyPasswordResetSerializer(PasswordResetSerializer):
def validate_email(self, value):
# use the custom reset form
self.reset_form = CustomAllAuthPasswordResetForm(data=self.initial_data)
if not self.reset_form.is_valid():
raise serializers.ValidationError(self.reset_form.errors)
return value

View file

@ -83,3 +83,18 @@ class PublicUserDetailView(APIView):
user.email = None user.email = None
serializer = PublicUserSerializer(user) serializer = PublicUserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
class UserMetadataView(APIView):
permission_classes = [IsAuthenticated]
@swagger_auto_schema(
responses={
200: openapi.Response('User metadata'),
400: 'Bad Request'
},
operation_description="Get user metadata."
)
def get(self, request):
user = request.user
serializer = PublicUserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)

View file

@ -1,95 +1,75 @@
import type { Handle } from '@sveltejs/kit'; import type { Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks'; import { sequence } from '@sveltejs/kit/hooks';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
import { fetchCSRFToken, tryRefreshToken } from '$lib/index.server';
export const authHook: Handle = async ({ event, resolve }) => { export const authHook: Handle = async ({ event, resolve }) => {
try { try {
let authCookie = event.cookies.get('auth'); let sessionid = event.cookies.get('sessionid');
let refreshCookie = event.cookies.get('refresh'); console.log('sessionid:', sessionid);
if (!authCookie && !refreshCookie) { if (!sessionid) {
console.log('No sessionid cookie');
event.locals.user = null; event.locals.user = null;
return await resolve(event); return await resolve(event);
} }
if (!authCookie && refreshCookie) { // print all cookies in the request
event.locals.user = null; console.log('Cookies:', event.request.headers.get('cookie'));
const token = await tryRefreshToken(event.cookies.get('refresh') || '');
if (token) {
authCookie = token;
event.cookies.set('auth', authCookie, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
path: '/'
});
} else {
return await resolve(event);
}
}
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
let userFetch = await event.fetch(`${serverEndpoint}/auth/user/`, { const cookie = event.request.headers.get('cookie') || '';
let userFetch = await event.fetch(`${serverEndpoint}/auth/user-metadata/`, {
headers: { headers: {
Cookie: `${authCookie}` cookie
} }
}); });
if (!userFetch.ok) { if (!userFetch.ok) {
console.log('Refreshing token'); event.locals.user = null;
const refreshCookie = event.cookies.get('refresh'); event.cookies.delete('sessionid', { path: '/' });
return await resolve(event);
if (refreshCookie) {
const csrfToken = await fetchCSRFToken();
if (!csrfToken) {
console.error('Failed to fetch CSRF token');
event.locals.user = null;
return await resolve(event);
}
const refreshFetch = await event.fetch(`${serverEndpoint}/auth/token/refresh/`, {
method: 'POST',
headers: {
'X-CSRFToken': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ refresh: refreshCookie })
});
if (refreshFetch.ok) {
const refresh = await refreshFetch.json();
event.cookies.set('auth', 'auth=' + refresh.access, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
path: '/'
});
userFetch = await event.fetch(`${serverEndpoint}/auth/user/`, {
headers: {
'X-CSRFToken': csrfToken,
Cookie: `auth=${refresh.access}`
}
});
}
}
} }
if (userFetch.ok) { if (userFetch.ok) {
const user = await userFetch.json(); const user = await userFetch.json();
event.locals.user = user; event.locals.user = user;
const setCookieHeader = userFetch.headers.get('Set-Cookie');
console.log('setCookieHeader:', setCookieHeader);
if (setCookieHeader) {
// Regular expression to match sessionid cookie and its expiry
const sessionIdRegex = /sessionid=([^;]+).*?expires=([^;]+)/;
const match = setCookieHeader.match(sessionIdRegex);
if (match) {
const sessionId = match[1];
const expiryString = match[2];
const expiryDate = new Date(expiryString);
console.log('Session ID:', sessionId);
console.log('Expiry Date:', expiryDate);
// Set the sessionid cookie
event.cookies.set('sessionid', sessionId, {
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: true,
expires: expiryDate
});
}
}
} else { } else {
event.locals.user = null; event.locals.user = null;
event.cookies.delete('auth', { path: '/' }); event.cookies.delete('sessionid', { path: '/' });
event.cookies.delete('refresh', { path: '/' });
} }
} catch (error) { } catch (error) {
console.error('Error in authHook:', error); console.error('Error in authHook:', error);
event.locals.user = null; event.locals.user = null;
event.cookies.delete('auth', { path: '/' }); event.cookies.delete('sessionid', { path: '/' });
event.cookies.delete('refresh', { path: '/' });
} }
return await resolve(event); return await resolve(event);

View file

@ -18,6 +18,7 @@
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
}); });
console.log(res);
let data = await res.json(); let data = await res.json();
console.log('ACTIVITIES' + data.activities); console.log('ACTIVITIES' + data.activities);
if (data && data.activities) { if (data && data.activities) {

View file

@ -444,6 +444,7 @@
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
credentials: 'include',
body: JSON.stringify(adventure) body: JSON.stringify(adventure)
}); });
let data = await res.json(); let data = await res.json();

View file

@ -1,5 +1,6 @@
import { redirect, type Actions } from '@sveltejs/kit'; import { redirect, type Actions } from '@sveltejs/kit';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import { fetchCSRFToken } from '$lib/index.server';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
@ -7,13 +8,16 @@ export const load = (async (event) => {
if (!event.locals.user) { if (!event.locals.user) {
return redirect(302, '/login'); return redirect(302, '/login');
} }
let csrfToken = await fetchCSRFToken();
let allActivities: string[] = []; let allActivities: string[] = [];
let res = await fetch(`${endpoint}/api/activity-types/types/`, { let res = await event.fetch(`${endpoint}/api/activity-types/types/`, {
headers: { headers: {
'Content-Type': 'application/json', 'X-CSRFToken': csrfToken,
Cookie: `${event.cookies.get('auth')}` Cookie: `csrftoken=${csrfToken}`
} },
credentials: 'include'
}); });
console.log(res);
let data = await res.json(); let data = await res.json();
if (data) { if (data) {
allActivities = data; allActivities = data;
@ -27,13 +31,16 @@ export const load = (async (event) => {
export const actions: Actions = { export const actions: Actions = {
getActivities: async (event) => { getActivities: async (event) => {
let csrfToken = await fetchCSRFToken();
let allActivities: string[] = []; let allActivities: string[] = [];
let res = await fetch(`${endpoint}/api/activity-types/types/`, { let res = await fetch(`${endpoint}/api/activity-types/types/`, {
headers: { headers: {
'X-CSRFToken': csrfToken,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Cookie: `${event.cookies.get('auth')}` Cookie: `csrftoken=${csrfToken}`
} }
}); });
console.log(res);
let data = await res.json(); let data = await res.json();
if (data) { if (data) {
allActivities = data; allActivities = data;

View file

@ -1,15 +1,19 @@
import { json } from '@sveltejs/kit'; import { json } from '@sveltejs/kit';
import type { RequestHandler } from '../data/$types'; import type { RequestHandler } from '@sveltejs/kit';
import { fetchCSRFToken } from '$lib/index.server';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
export const POST: RequestHandler = async (event) => { export const POST: RequestHandler = async (event) => {
let allActivities: string[] = []; let allActivities: string[] = [];
let res = await fetch(`${endpoint}/api/activity-types/types/`, { let csrfToken = await fetchCSRFToken();
let sessionId = event.cookies.get('sessionid');
let res = await event.fetch(`${endpoint}/api/activity-types/types/`, {
headers: { headers: {
'Content-Type': 'application/json', 'X-CSRFToken': csrfToken,
Cookie: `${event.cookies.get('auth')}` Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}`
} },
credentials: 'include'
}); });
let data = await res.json(); let data = await res.json();
if (data) { if (data) {

View file

@ -4,8 +4,7 @@ const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
import type { Adventure } from '$lib/types'; import type { Adventure } from '$lib/types';
import type { Actions } from '@sveltejs/kit'; import type { Actions } from '@sveltejs/kit';
import { fetchCSRFToken, tryRefreshToken } from '$lib/index.server'; import { fetchCSRFToken } from '$lib/index.server';
import { checkLink } from '$lib';
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
@ -29,12 +28,13 @@ export const load = (async (event) => {
const page = event.url.searchParams.get('page') || '1'; const page = event.url.searchParams.get('page') || '1';
const is_visited = event.url.searchParams.get('is_visited') || 'all'; const is_visited = event.url.searchParams.get('is_visited') || 'all';
let initialFetch = await fetch( let initialFetch = await event.fetch(
`${serverEndpoint}/api/adventures/filtered?types=${typeString}&order_by=${order_by}&order_direction=${order_direction}&include_collections=${include_collections}&page=${page}&is_visited=${is_visited}`, `${serverEndpoint}/api/adventures/filtered?types=${typeString}&order_by=${order_by}&order_direction=${order_direction}&include_collections=${include_collections}&page=${page}&is_visited=${is_visited}`,
{ {
headers: { headers: {
Cookie: `${event.cookies.get('auth')}` Cookie: `sessionid=${event.cookies.get('sessionid')}`
} },
credentials: 'include'
} }
); );
@ -61,371 +61,15 @@ export const load = (async (event) => {
}) satisfies PageServerLoad; }) satisfies PageServerLoad;
export const actions: Actions = { export const actions: Actions = {
create: async (event) => {
const formData = await event.request.formData();
const type = formData.get('type') as string;
const name = formData.get('name') as string;
const location = formData.get('location') as string | null;
let date = (formData.get('date') as string | null) ?? null;
const description = formData.get('description') as string | null;
const activity_types = formData.get('activity_types')
? (formData.get('activity_types') as string).split(',')
: null;
const rating = formData.get('rating') ? Number(formData.get('rating')) : null;
let link = formData.get('link') as string | null;
let latitude = formData.get('latitude') as string | null;
let longitude = formData.get('longitude') as string | null;
let collection = formData.get('collection') as string | null;
let is_public = formData.get('is_public') as string | null | boolean;
if (is_public) {
is_public = true;
} else {
is_public = false;
}
// check if latitude and longitude are valid
if (latitude && longitude) {
if (isNaN(Number(latitude)) || isNaN(Number(longitude))) {
return {
status: 400,
body: { error: 'Invalid latitude or longitude' }
};
}
}
// round latitude and longitude to 6 decimal places
if (latitude) {
latitude = Number(latitude).toFixed(6);
}
if (longitude) {
longitude = Number(longitude).toFixed(6);
}
const image = formData.get('image') as File;
if (!type || !name) {
return {
status: 400,
body: { error: 'Missing required fields' }
};
}
if (date == null || date == '') {
date = null;
}
if (link) {
link = checkLink(link);
}
const formDataToSend = new FormData();
formDataToSend.append('type', type);
formDataToSend.append('name', name);
formDataToSend.append('location', location || '');
formDataToSend.append('date', date || '');
formDataToSend.append('description', description || '');
formDataToSend.append('latitude', latitude || '');
formDataToSend.append('longitude', longitude || '');
formDataToSend.append('is_public', is_public.toString());
if (!isNaN(Number(collection))) {
if (collection !== null) {
formDataToSend.append('collection', collection);
}
}
if (activity_types) {
// Filter out empty and duplicate activity types, then trim each activity type
const cleanedActivityTypes = Array.from(
new Set(
activity_types
.map((activity_type) => activity_type.trim())
.filter((activity_type) => activity_type !== '' && activity_type !== ',')
)
);
// Append each cleaned activity type to formDataToSend
cleanedActivityTypes.forEach((activity_type) => {
formDataToSend.append('activity_types', activity_type);
});
}
formDataToSend.append('rating', rating ? rating.toString() : '');
formDataToSend.append('link', link || '');
// formDataToSend.append('image', image);
// log each key-value pair in the FormData
for (let pair of formDataToSend.entries()) {
console.log(pair[0] + ', ' + pair[1]);
}
let auth = event.cookies.get('auth');
if (!auth) {
const refresh = event.cookies.get('refresh');
if (!refresh) {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
let res = await tryRefreshToken(refresh);
if (res) {
auth = res;
event.cookies.set('auth', auth, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
path: '/'
});
} else {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
}
if (!auth) {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
const csrfToken = await fetchCSRFToken();
if (!csrfToken) {
return {
status: 500,
body: { message: 'Failed to fetch CSRF token' }
};
}
const res = await fetch(`${serverEndpoint}/api/adventures/`, {
method: 'POST',
headers: {
'X-CSRFToken': csrfToken,
Cookie: auth
},
body: formDataToSend
});
let new_id = await res.json();
if (!res.ok) {
const errorBody = await res.json();
return {
status: res.status,
body: { error: errorBody }
};
}
let id = new_id.id;
let user_id = new_id.user_id;
let image_url = new_id.image;
let link_url = new_id.link;
if (image && image.size > 0) {
let imageForm = new FormData();
imageForm.append('image', image);
imageForm.append('adventure', id);
let imageRes = await fetch(`${serverEndpoint}/api/images/`, {
method: 'POST',
headers: {
Cookie: `${event.cookies.get('auth')}`
},
body: imageForm
});
let data = await imageRes.json();
console.log(data);
}
return { id, user_id, image_url, link };
},
edit: async (event) => {
const formData = await event.request.formData();
const adventureId = formData.get('adventureId') as string;
const type = formData.get('type') as string;
const name = formData.get('name') as string;
const location = formData.get('location') as string | null;
let date = (formData.get('date') as string | null) ?? null;
const description = formData.get('description') as string | null;
let activity_types = formData.get('activity_types')
? (formData.get('activity_types') as string).split(',')
: null;
const rating = formData.get('rating') ? Number(formData.get('rating')) : null;
let link = formData.get('link') as string | null;
let latitude = formData.get('latitude') as string | null;
let longitude = formData.get('longitude') as string | null;
let is_public = formData.get('is_public') as string | null | boolean;
if (is_public) {
is_public = true;
} else {
is_public = false;
}
// check if latitude and longitude are valid
if (latitude && longitude) {
if (isNaN(Number(latitude)) || isNaN(Number(longitude))) {
return {
status: 400,
body: { error: 'Invalid latitude or longitude' }
};
}
}
// round latitude and longitude to 6 decimal places
if (latitude) {
latitude = Number(latitude).toFixed(6);
}
if (longitude) {
longitude = Number(longitude).toFixed(6);
}
const image = formData.get('image') as File;
// console.log(activity_types);
if (!type || !name) {
return {
status: 400,
body: { error: 'Missing required fields' }
};
}
if (date == null || date == '') {
date = null;
}
if (link) {
link = checkLink(link);
}
const formDataToSend = new FormData();
formDataToSend.append('type', type);
formDataToSend.append('name', name);
formDataToSend.append('location', location || '');
formDataToSend.append('date', date || '');
formDataToSend.append('description', description || '');
formDataToSend.append('latitude', latitude || '');
formDataToSend.append('longitude', longitude || '');
formDataToSend.append('is_public', is_public.toString());
let csrfToken = await fetchCSRFToken();
if (activity_types) {
// Filter out empty and duplicate activity types, then trim each activity type
const cleanedActivityTypes = Array.from(
new Set(
activity_types
.map((activity_type) => activity_type.trim())
.filter((activity_type) => activity_type !== '' && activity_type !== ',')
)
);
// Append each cleaned activity type to formDataToSend
cleanedActivityTypes.forEach((activity_type) => {
formDataToSend.append('activity_types', activity_type);
});
} else {
let res = await fetch(`${serverEndpoint}/api/adventures/${adventureId}/`, {
method: 'PATCH',
headers: {
Cookie: `${event.cookies.get('auth')}`,
'X-CSRFToken': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ activity_types: [] })
});
if (!res.ok) {
const errorBody = await res.json();
return {
status: res.status,
body: { error: errorBody }
};
}
}
formDataToSend.append('rating', rating ? rating.toString() : '');
formDataToSend.append('link', link || '');
if (image && image.size > 0) {
formDataToSend.append('image', image);
}
let auth = event.cookies.get('auth');
if (!auth) {
const refresh = event.cookies.get('refresh');
if (!refresh) {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
let res = await tryRefreshToken(refresh);
if (res) {
auth = res;
event.cookies.set('auth', auth, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
path: '/'
});
} else {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
}
if (!auth) {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
if (!csrfToken) {
return {
status: 500,
body: { message: 'Failed to fetch CSRF token' }
};
}
const res = await fetch(`${serverEndpoint}/api/adventures/${adventureId}/`, {
method: 'PATCH',
headers: {
'X-CSRFToken': csrfToken,
Cookie: auth
},
body: formDataToSend
});
if (!res.ok) {
const errorBody = await res.json();
return {
status: res.status,
body: { error: errorBody }
};
}
let adventure = await res.json();
let image_url = adventure.image;
let link_url = adventure.link;
return { image_url, link_url };
},
image: async (event) => { image: async (event) => {
let formData = await event.request.formData(); let formData = await event.request.formData();
let csrfToken = await fetchCSRFToken();
let sessionId = event.cookies.get('sessionid');
let res = await fetch(`${serverEndpoint}/api/images/`, { let res = await fetch(`${serverEndpoint}/api/images/`, {
method: 'POST', method: 'POST',
headers: { headers: {
Cookie: `${event.cookies.get('auth')}` Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}`,
'X-CSRFToken': csrfToken
}, },
body: formData body: formData
}); });

View file

@ -7,8 +7,9 @@ export const load = (async (event) => {
const id = event.params as { id: string }; const id = event.params as { id: string };
let request = await fetch(`${endpoint}/api/adventures/${id.id}/`, { let request = await fetch(`${endpoint}/api/adventures/${id.id}/`, {
headers: { headers: {
Cookie: `${event.cookies.get('auth')}` Cookie: `sessionid=${event.cookies.get('sessionid')}`
} },
credentials: 'include'
}); });
if (!request.ok) { if (!request.ok) {
console.error('Failed to fetch adventure ' + id.id); console.error('Failed to fetch adventure ' + id.id);
@ -24,8 +25,9 @@ export const load = (async (event) => {
if (adventure.collection) { if (adventure.collection) {
let res2 = await fetch(`${endpoint}/api/collections/${adventure.collection}/`, { let res2 = await fetch(`${endpoint}/api/collections/${adventure.collection}/`, {
headers: { headers: {
Cookie: `${event.cookies.get('auth')}` Cookie: `sessionid=${event.cookies.get('sessionid')}`
} },
credentials: 'include'
}); });
collection = await res2.json(); collection = await res2.json();
} }
@ -39,8 +41,8 @@ export const load = (async (event) => {
} }
}) satisfies PageServerLoad; }) satisfies PageServerLoad;
import type { Actions } from '@sveltejs/kit'; import { redirect, type Actions } from '@sveltejs/kit';
import { tryRefreshToken } from '$lib/index.server'; import { fetchCSRFToken } from '$lib/index.server';
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
@ -50,29 +52,7 @@ export const actions: Actions = {
const adventureId = id.id; const adventureId = id.id;
if (!event.locals.user) { if (!event.locals.user) {
const refresh = event.cookies.get('refresh'); return redirect(302, '/login');
let auth = event.cookies.get('auth');
if (!refresh) {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
let res = await tryRefreshToken(refresh);
if (res) {
auth = res;
event.cookies.set('auth', auth, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
path: '/'
});
} else {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
} }
if (!adventureId) { if (!adventureId) {
return { return {
@ -81,12 +61,15 @@ export const actions: Actions = {
}; };
} }
let csrfToken = await fetchCSRFToken();
let res = await fetch(`${serverEndpoint}/api/adventures/${event.params.id}`, { let res = await fetch(`${serverEndpoint}/api/adventures/${event.params.id}`, {
method: 'DELETE', method: 'DELETE',
headers: { headers: {
Cookie: `${event.cookies.get('auth')}`, Cookie: `sessionid=${event.cookies.get('sessionid')}; csrftoken=${csrfToken}`,
'Content-Type': 'application/json' 'X-CSRFToken': csrfToken
} },
credentials: 'include'
}); });
console.log(res); console.log(res);
if (!res.ok) { if (!res.ok) {

View file

@ -1,69 +1,77 @@
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
import { fetchCSRFToken } from '$lib/index.server';
import { json } from '@sveltejs/kit'; import { json } from '@sveltejs/kit';
/** @type {import('./$types').RequestHandler} */ /** @type {import('./$types').RequestHandler} */
export async function GET({ url, params, request, fetch, cookies }) { export async function GET(event) {
// add the param format = json to the url or add additional if anothre param is already present const { url, params, request, fetch, cookies } = event;
if (url.search) { const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
url.search = url.search + '&format=json'; return handleRequest(url, params, request, fetch, cookies, searchParam);
} else {
url.search = '?format=json';
}
return handleRequest(url, params, request, fetch, cookies);
} }
/** @type {import('./$types').RequestHandler} */ /** @type {import('./$types').RequestHandler} */
export async function POST({ url, params, request, fetch, cookies }) { export async function POST({ url, params, request, fetch, cookies }) {
return handleRequest(url, params, request, fetch, cookies, true); const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
return handleRequest(url, params, request, fetch, cookies, searchParam, true);
} }
export async function PATCH({ url, params, request, fetch, cookies }) { export async function PATCH({ url, params, request, fetch, cookies }) {
return handleRequest(url, params, request, fetch, cookies, true); const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
return handleRequest(url, params, request, fetch, cookies, searchParam, true);
} }
export async function PUT({ url, params, request, fetch, cookies }) { export async function PUT({ url, params, request, fetch, cookies }) {
return handleRequest(url, params, request, fetch, cookies, true); const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
return handleRequest(url, params, request, fetch, cookies, searchParam, true);
} }
export async function DELETE({ url, params, request, fetch, cookies }) { export async function DELETE({ url, params, request, fetch, cookies }) {
return handleRequest(url, params, request, fetch, cookies, true); const searchParam = url.search ? `${url.search}&format=json` : '?format=json';
return handleRequest(url, params, request, fetch, cookies, searchParam, true);
} }
// Implement other HTTP methods as needed (PUT, DELETE, etc.)
async function handleRequest( async function handleRequest(
url: any, url: any,
params: any, params: any,
request: any, request: any,
fetch: any, fetch: any,
cookies: any, cookies: any,
searchParam: string,
requreTrailingSlash: boolean | undefined = false requreTrailingSlash: boolean | undefined = false
) { ) {
const path = params.path; const path = params.path;
let targetUrl = `${endpoint}/api/${path}${url.search}`; let targetUrl = `${endpoint}/api/${path}`;
// Ensure the path ends with a trailing slash
if (requreTrailingSlash && !targetUrl.endsWith('/')) { if (requreTrailingSlash && !targetUrl.endsWith('/')) {
targetUrl += '/'; targetUrl += '/';
} }
// Append query parameters to the path correctly
targetUrl += searchParam; // This will add ?format=json or &format=json to the URL
const headers = new Headers(request.headers); const headers = new Headers(request.headers);
const authCookie = cookies.get('auth'); const csrfToken = await fetchCSRFToken();
if (!csrfToken) {
if (authCookie) { return json({ error: 'CSRF token is missing or invalid' }, { status: 400 });
headers.set('Cookie', `${authCookie}`);
} }
try { try {
const response = await fetch(targetUrl, { const response = await fetch(targetUrl, {
method: request.method, method: request.method,
headers: headers, headers: {
body: request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined ...Object.fromEntries(headers),
'X-CSRFToken': csrfToken,
Cookie: `csrftoken=${csrfToken}`
},
body:
request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined,
credentials: 'include' // This line ensures cookies are sent with the request
}); });
if (response.status === 204) { if (response.status === 204) {
// For 204 No Content, return a response with no body
return new Response(null, { return new Response(null, {
status: 204, status: 204,
headers: response.headers headers: response.headers

View file

@ -17,10 +17,12 @@ export const load = (async (event) => {
let previous = null; let previous = null;
let count = 0; let count = 0;
let adventures: Adventure[] = []; let adventures: Adventure[] = [];
let sessionId = event.cookies.get('sessionid');
let initialFetch = await fetch(`${serverEndpoint}/api/collections/?order_by=updated_at`, { let initialFetch = await fetch(`${serverEndpoint}/api/collections/?order_by=updated_at`, {
headers: { headers: {
Cookie: `${event.cookies.get('auth')}` Cookie: `sessionid=${sessionId}`
} },
credentials: 'include'
}); });
if (!initialFetch.ok) { if (!initialFetch.ok) {
console.error('Failed to fetch visited adventures'); console.error('Failed to fetch visited adventures');
@ -72,34 +74,9 @@ export const actions: Actions = {
formDataToSend.append('start_date', start_date || ''); formDataToSend.append('start_date', start_date || '');
formDataToSend.append('end_date', end_date || ''); formDataToSend.append('end_date', end_date || '');
formDataToSend.append('link', link || ''); formDataToSend.append('link', link || '');
let auth = event.cookies.get('auth'); let sessionid = event.cookies.get('sessionid');
if (!auth) { if (!sessionid) {
const refresh = event.cookies.get('refresh');
if (!refresh) {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
let res = await tryRefreshToken(refresh);
if (res) {
auth = res;
event.cookies.set('auth', auth, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
path: '/'
});
} else {
return {
status: 401,
body: { message: 'Unauthorized' }
};
}
}
if (!auth) {
return { return {
status: 401, status: 401,
body: { message: 'Unauthorized' } body: { message: 'Unauthorized' }
@ -119,7 +96,7 @@ export const actions: Actions = {
method: 'POST', method: 'POST',
headers: { headers: {
'X-CSRFToken': csrfToken, 'X-CSRFToken': csrfToken,
Cookie: auth Cookie: `sessionid=${sessionid}; csrftoken=${csrfToken}`
}, },
body: formDataToSend body: formDataToSend
}); });

View file

@ -2,6 +2,7 @@ import { fail, redirect } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types'; import type { Actions, PageServerLoad } from './$types';
import { getRandomBackground, getRandomQuote } from '$lib'; import { getRandomBackground, getRandomQuote } from '$lib';
import { fetchCSRFToken } from '$lib/index.server';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
export const load: PageServerLoad = async (event) => { export const load: PageServerLoad = async (event) => {
@ -24,37 +25,29 @@ export const actions: Actions = {
default: async (event) => { default: async (event) => {
const formData = await event.request.formData(); const formData = await event.request.formData();
const formUsername = formData.get('username'); const formUsername = formData.get('username');
const formPassword = formData.get('password');
let username = formUsername?.toString().toLocaleLowerCase(); let username = formUsername?.toString().toLocaleLowerCase();
const password = formData.get('password'); const password = formData.get('password');
const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
const csrfTokenFetch = await event.fetch(`${serverEndpoint}/csrf/`);
if (!csrfTokenFetch.ok) { const csrfToken = await fetchCSRFToken();
console.error('Failed to fetch CSRF token');
event.locals.user = null;
return fail(500, {
message: 'Failed to fetch CSRF token'
});
}
const tokenPromise = await csrfTokenFetch.json(); const loginFetch = await event.fetch(`${serverEndpoint}/_allauth/browser/v1/auth/login`, {
const csrfToken = tokenPromise.csrfToken;
const loginFetch = await event.fetch(`${serverEndpoint}/auth/login/`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-CSRFToken': csrfToken, 'X-CSRFToken': csrfToken,
'Content-Type': 'application/json' 'Content-Type': 'application/json',
Cookie: `csrftoken=${csrfToken}`
}, },
body: JSON.stringify({ body: JSON.stringify({
username, username,
password password
}) }),
credentials: 'include'
}); });
const loginResponse = await loginFetch.json(); const loginResponse = await loginFetch.json();
if (!loginFetch.ok) { if (!loginFetch.ok) {
// get the value of the first key in the object // get the value of the first key in the object
@ -64,25 +57,34 @@ export const actions: Actions = {
message: error message: error
}); });
} else { } else {
const token = loginResponse.access; const setCookieHeader = loginFetch.headers.get('Set-Cookie');
const tokenFormatted = `auth=${token}`;
const refreshToken = `${loginResponse.refresh}`;
event.cookies.set('auth', tokenFormatted, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
path: '/',
secure: false
});
event.cookies.set('refresh', refreshToken, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
path: '/',
secure: false
});
return redirect(302, '/'); console.log('setCookieHeader:', setCookieHeader);
if (setCookieHeader) {
// Regular expression to match sessionid cookie and its expiry
const sessionIdRegex = /sessionid=([^;]+).*?expires=([^;]+)/;
const match = setCookieHeader.match(sessionIdRegex);
if (match) {
const sessionId = match[1];
const expiryString = match[2];
const expiryDate = new Date(expiryString);
console.log('Session ID:', sessionId);
console.log('Expiry Date:', expiryDate);
// Set the sessionid cookie
event.cookies.set('sessionid', sessionId, {
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: true,
expires: expiryDate
});
}
}
redirect(302, '/');
} }
} }
}; };

View file

@ -1,3 +1,4 @@
import { fetchCSRFToken } from '$lib/index.server';
import { fail, type Actions } from '@sveltejs/kit'; import { fail, type Actions } from '@sveltejs/kit';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
@ -13,10 +14,14 @@ export const actions: Actions = {
return fail(400, { message: 'missing_email' }); return fail(400, { message: 'missing_email' });
} }
let res = await fetch(`${endpoint}/auth/password/reset/`, { let csrfToken = await fetchCSRFToken();
let res = await fetch(`${endpoint}/_allauth/browser/v1/auth/password/request`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken,
Cookie: `csrftoken=${csrfToken}`
}, },
body: JSON.stringify({ body: JSON.stringify({
email email
@ -25,10 +30,7 @@ export const actions: Actions = {
if (!res.ok) { if (!res.ok) {
let message = await res.json(); let message = await res.json();
return fail(res.status, message);
const key = Object.keys(message)[0];
return fail(res.status, { message: message[key] });
} }
return { success: true }; return { success: true };
} }

View file

@ -44,19 +44,23 @@ export const actions: Actions = {
return fail(500, { message: 'Failed to fetch CSRF token' }); return fail(500, { message: 'Failed to fetch CSRF token' });
} }
if (password1 !== password2) {
return fail(400, { message: 'Passwords do not match' });
}
const tokenPromise = await csrfTokenFetch.json(); const tokenPromise = await csrfTokenFetch.json();
const csrfToken = tokenPromise.csrfToken; const csrfToken = tokenPromise.csrfToken;
const loginFetch = await event.fetch(`${serverEndpoint}/auth/registration/`, { const loginFetch = await event.fetch(`${serverEndpoint}/_allauth/browser/v1/auth/signup`, {
method: 'POST', method: 'POST',
headers: { headers: {
'X-CSRFToken': csrfToken, 'X-CSRFToken': csrfToken,
'Content-Type': 'application/json' 'Content-Type': 'application/json',
Cookie: `csrftoken=${csrfToken}`
}, },
body: JSON.stringify({ body: JSON.stringify({
username: username, username: username,
password1: password1, password: password1,
password2: password2,
email: email, email: email,
first_name, first_name,
last_name last_name
@ -65,31 +69,36 @@ export const actions: Actions = {
const loginResponse = await loginFetch.json(); const loginResponse = await loginFetch.json();
if (!loginFetch.ok) { if (!loginFetch.ok) {
// get the value of the first key in the object return fail(loginFetch.status, loginResponse);
const firstKey = Object.keys(loginResponse)[0] || 'error';
const error =
loginResponse[firstKey][0] || 'Failed to register user. Check your inputs and try again.';
return fail(400, {
message: error
});
} else { } else {
const token = loginResponse.access; const setCookieHeader = loginFetch.headers.get('Set-Cookie');
const tokenFormatted = `auth=${token}`;
const refreshToken = `${loginResponse.refresh}`;
event.cookies.set('auth', tokenFormatted, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes
path: '/'
});
event.cookies.set('refresh', refreshToken, {
httpOnly: true,
sameSite: 'lax',
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
path: '/'
});
return redirect(302, '/'); console.log('setCookieHeader:', setCookieHeader);
if (setCookieHeader) {
// Regular expression to match sessionid cookie and its expiry
const sessionIdRegex = /sessionid=([^;]+).*?expires=([^;]+)/;
const match = setCookieHeader.match(sessionIdRegex);
if (match) {
const sessionId = match[1];
const expiryString = match[2];
const expiryDate = new Date(expiryString);
console.log('Session ID:', sessionId);
console.log('Expiry Date:', expiryDate);
// Set the sessionid cookie
event.cookies.set('sessionid', sessionId, {
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: true,
expires: expiryDate
});
}
}
redirect(302, '/');
} }
} }
}; };

View file

@ -9,11 +9,9 @@ export const load = (async (event) => {
if (!event.locals.user) { if (!event.locals.user) {
return redirect(302, '/login'); return redirect(302, '/login');
} else { } else {
const res = await fetch(`${endpoint}/api/countries/`, { const res = await event.fetch(`${endpoint}/api/countries/`, {
method: 'GET', method: 'GET',
headers: { credentials: 'include'
Cookie: `${event.cookies.get('auth')}`
}
}); });
if (!res.ok) { if (!res.ok) {
console.error('Failed to fetch countries'); console.error('Failed to fetch countries');