diff --git a/backend/server/adventures/middleware.py b/backend/server/adventures/middleware.py index af54b68..3cd9713 100644 --- a/backend/server/adventures/middleware.py +++ b/backend/server/adventures/middleware.py @@ -11,3 +11,13 @@ class AppVersionMiddleware: response['X-AdventureLog-Version'] = '1.0.0' 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 \ No newline at end of file diff --git a/backend/server/adventures/views.py b/backend/server/adventures/views.py index 348c142..808b9aa 100644 --- a/backend/server/adventures/views.py +++ b/backend/server/adventures/views.py @@ -73,6 +73,7 @@ class AdventureViewSet(viewsets.ModelViewSet): return queryset.order_by(ordering) def get_queryset(self): + print(self.request.user) # if the user is not authenticated return only public adventures for retrieve action if not self.request.user.is_authenticated: if self.action == 'retrieve': diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index 44aa5d4..14d3983 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -47,10 +47,11 @@ INSTALLED_APPS = ( 'django.contrib.sites', 'rest_framework', 'rest_framework.authtoken', - 'dj_rest_auth', + # 'dj_rest_auth', 'allauth', 'allauth.account', - 'dj_rest_auth.registration', + 'allauth.headless', + # 'dj_rest_auth.registration', 'allauth.socialaccount', 'allauth.socialaccount.providers.facebook', 'drf_yasg', @@ -113,6 +114,7 @@ DATABASES = { } } +ACCOUNT_SIGNUP_FORM_CLASS = 'users.form_overrides.CustomSignupForm' # Internationalization # 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_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' +ACCOUNT_ADAPTER = 'users.adapters.NoNewUsersAccountAdapter' + 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' SITE_ID = 1 ACCOUNT_EMAIL_REQUIRED = True @@ -228,12 +228,14 @@ SWAGGER_SETTINGS = { 'LOGOUT_URL': 'logout', } -# For demo purposes only. Use a white list in the real world. -CORS_ORIGIN_ALLOW_ALL = True - 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()] + DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' LOGGING = { @@ -260,6 +262,7 @@ LOGGING = { }, }, } - # https://github.com/dr5hn/countries-states-cities-database/tags -COUNTRY_REGION_JSON_VERSION = 'v2.4' \ No newline at end of file +COUNTRY_REGION_JSON_VERSION = 'v2.4' + +SESSION_SAVE_EVERY_REQUEST = True \ No newline at end of file diff --git a/backend/server/main/urls.py b/backend/server/main/urls.py index fe098bd..fa3456d 100644 --- a/backend/server/main/urls.py +++ b/backend/server/main/urls.py @@ -4,7 +4,7 @@ from django.views.generic import RedirectView, TemplateView from django.conf import settings from django.conf.urls.static import static 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 drf_yasg.views import get_schema_view @@ -25,6 +25,10 @@ urlpatterns = [ path('auth/users/', PublicUserListView.as_view(), name='public-user-list'), path('auth/user//', 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'), re_path(r'^$', TemplateView.as_view( template_name="home.html"), name='home'), @@ -64,11 +68,15 @@ urlpatterns = [ re_path(r'^auth/', include('dj_rest_auth.urls')), re_path(r'^auth/registration/', 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'^accounts/profile/$', RedirectView.as_view(url='/', permanent=True), name='profile-redirect'), re_path(r'^docs/$', schema_view.with_ui('swagger', cache_timeout=0), name='api_docs'), # 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) diff --git a/backend/server/templates/account/email/password_reset_key_message.txt b/backend/server/templates/account/email/password_reset_key_message.txt deleted file mode 100644 index 42473bf..0000000 --- a/backend/server/templates/account/email/password_reset_key_message.txt +++ /dev/null @@ -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 %} \ No newline at end of file diff --git a/backend/server/users/adapters.py b/backend/server/users/adapters.py new file mode 100644 index 0000000..cf0435f --- /dev/null +++ b/backend/server/users/adapters.py @@ -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 \ No newline at end of file diff --git a/backend/server/users/admin.py b/backend/server/users/admin.py index 66d2600..db75154 100644 --- a/backend/server/users/admin.py +++ b/backend/server/users/admin.py @@ -1,3 +1,10 @@ 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) \ No newline at end of file diff --git a/backend/server/users/form_overrides.py b/backend/server/users/form_overrides.py new file mode 100644 index 0000000..266bfd0 --- /dev/null +++ b/backend/server/users/form_overrides.py @@ -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 \ No newline at end of file diff --git a/backend/server/users/forms.py b/backend/server/users/forms.py index 2bb5454..266bfd0 100644 --- a/backend/server/users/forms.py +++ b/backend/server/users/forms.py @@ -1,55 +1,17 @@ -from allauth.account.utils import (filter_users_by_email, user_pk_to_url_str, user_username) -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 django import forms -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 clean_email(self): - """ - 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'] - + 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 \ No newline at end of file diff --git a/backend/server/users/serializers.py b/backend/server/users/serializers.py index 25f5cc0..f0cd764 100644 --- a/backend/server/users/serializers.py +++ b/backend/server/users/serializers.py @@ -1,10 +1,8 @@ from rest_framework import serializers from django.contrib.auth import get_user_model -from adventures.models import Adventure, Collection -from users.forms import CustomAllAuthPasswordResetForm +from adventures.models import Collection from dj_rest_auth.serializers import PasswordResetSerializer -from rest_framework.exceptions import PermissionDenied User = get_user_model() @@ -32,77 +30,7 @@ class ChangeEmailSerializer(serializers.Serializer): 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.contrib.auth import get_user_model @@ -116,20 +44,6 @@ from rest_framework import serializers from django.conf import settings 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): """ User model w/o password @@ -203,13 +117,3 @@ class CustomUserDetailsSerializer(UserDetailsSerializer): representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}" del representation['pk'] # remove the pk field from the response 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 \ No newline at end of file diff --git a/backend/server/users/views.py b/backend/server/users/views.py index 5300d8c..ae959ac 100644 --- a/backend/server/users/views.py +++ b/backend/server/users/views.py @@ -83,3 +83,18 @@ class PublicUserDetailView(APIView): user.email = None serializer = PublicUserSerializer(user) 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) \ No newline at end of file diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 0c1f991..cbd152f 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -1,95 +1,75 @@ import type { Handle } from '@sveltejs/kit'; import { sequence } from '@sveltejs/kit/hooks'; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; -import { fetchCSRFToken, tryRefreshToken } from '$lib/index.server'; export const authHook: Handle = async ({ event, resolve }) => { try { - let authCookie = event.cookies.get('auth'); - let refreshCookie = event.cookies.get('refresh'); + let sessionid = event.cookies.get('sessionid'); + console.log('sessionid:', sessionid); - if (!authCookie && !refreshCookie) { + if (!sessionid) { + console.log('No sessionid cookie'); event.locals.user = null; return await resolve(event); } - if (!authCookie && refreshCookie) { - event.locals.user = null; - 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); - } - } + // print all cookies in the request + console.log('Cookies:', event.request.headers.get('cookie')); 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: { - Cookie: `${authCookie}` + cookie } }); if (!userFetch.ok) { - console.log('Refreshing token'); - const refreshCookie = event.cookies.get('refresh'); - - 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}` - } - }); - } - } + event.locals.user = null; + event.cookies.delete('sessionid', { path: '/' }); + return await resolve(event); } if (userFetch.ok) { const user = await userFetch.json(); 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 { event.locals.user = null; - event.cookies.delete('auth', { path: '/' }); - event.cookies.delete('refresh', { path: '/' }); + event.cookies.delete('sessionid', { path: '/' }); } } catch (error) { console.error('Error in authHook:', error); event.locals.user = null; - event.cookies.delete('auth', { path: '/' }); - event.cookies.delete('refresh', { path: '/' }); + event.cookies.delete('sessionid', { path: '/' }); } return await resolve(event); diff --git a/frontend/src/lib/components/ActivityComplete.svelte b/frontend/src/lib/components/ActivityComplete.svelte index 0636127..ef39c52 100644 --- a/frontend/src/lib/components/ActivityComplete.svelte +++ b/frontend/src/lib/components/ActivityComplete.svelte @@ -18,6 +18,7 @@ 'Content-Type': 'application/json' } }); + console.log(res); let data = await res.json(); console.log('ACTIVITIES' + data.activities); if (data && data.activities) { diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index c0dc277..3d64599 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -444,6 +444,7 @@ headers: { 'Content-Type': 'application/json' }, + credentials: 'include', body: JSON.stringify(adventure) }); let data = await res.json(); diff --git a/frontend/src/routes/activities/+page.server.ts b/frontend/src/routes/activities/+page.server.ts index 4407eda..238e6b4 100644 --- a/frontend/src/routes/activities/+page.server.ts +++ b/frontend/src/routes/activities/+page.server.ts @@ -1,5 +1,6 @@ import { redirect, type Actions } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; +import { fetchCSRFToken } from '$lib/index.server'; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; @@ -7,13 +8,16 @@ export const load = (async (event) => { if (!event.locals.user) { return redirect(302, '/login'); } + let csrfToken = await fetchCSRFToken(); let allActivities: string[] = []; - let res = await fetch(`${endpoint}/api/activity-types/types/`, { + let res = await event.fetch(`${endpoint}/api/activity-types/types/`, { headers: { - 'Content-Type': 'application/json', - Cookie: `${event.cookies.get('auth')}` - } + 'X-CSRFToken': csrfToken, + Cookie: `csrftoken=${csrfToken}` + }, + credentials: 'include' }); + console.log(res); let data = await res.json(); if (data) { allActivities = data; @@ -27,13 +31,16 @@ export const load = (async (event) => { export const actions: Actions = { getActivities: async (event) => { + let csrfToken = await fetchCSRFToken(); let allActivities: string[] = []; let res = await fetch(`${endpoint}/api/activity-types/types/`, { headers: { + 'X-CSRFToken': csrfToken, 'Content-Type': 'application/json', - Cookie: `${event.cookies.get('auth')}` + Cookie: `csrftoken=${csrfToken}` } }); + console.log(res); let data = await res.json(); if (data) { allActivities = data; diff --git a/frontend/src/routes/activities/+server.ts b/frontend/src/routes/activities/+server.ts index ebb4252..c5143dc 100644 --- a/frontend/src/routes/activities/+server.ts +++ b/frontend/src/routes/activities/+server.ts @@ -1,15 +1,19 @@ 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 endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; export const POST: RequestHandler = async (event) => { 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: { - 'Content-Type': 'application/json', - Cookie: `${event.cookies.get('auth')}` - } + 'X-CSRFToken': csrfToken, + Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}` + }, + credentials: 'include' }); let data = await res.json(); if (data) { diff --git a/frontend/src/routes/adventures/+page.server.ts b/frontend/src/routes/adventures/+page.server.ts index e0a0f12..da69fdf 100644 --- a/frontend/src/routes/adventures/+page.server.ts +++ b/frontend/src/routes/adventures/+page.server.ts @@ -4,8 +4,7 @@ const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; import type { Adventure } from '$lib/types'; import type { Actions } from '@sveltejs/kit'; -import { fetchCSRFToken, tryRefreshToken } from '$lib/index.server'; -import { checkLink } from '$lib'; +import { fetchCSRFToken } from '$lib/index.server'; 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 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}`, { 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; 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) => { let formData = await event.request.formData(); + let csrfToken = await fetchCSRFToken(); + let sessionId = event.cookies.get('sessionid'); let res = await fetch(`${serverEndpoint}/api/images/`, { method: 'POST', headers: { - Cookie: `${event.cookies.get('auth')}` + Cookie: `csrftoken=${csrfToken}; sessionid=${sessionId}`, + 'X-CSRFToken': csrfToken }, body: formData }); diff --git a/frontend/src/routes/adventures/[id]/+page.server.ts b/frontend/src/routes/adventures/[id]/+page.server.ts index aa01d2e..bba55aa 100644 --- a/frontend/src/routes/adventures/[id]/+page.server.ts +++ b/frontend/src/routes/adventures/[id]/+page.server.ts @@ -7,8 +7,9 @@ export const load = (async (event) => { const id = event.params as { id: string }; let request = await fetch(`${endpoint}/api/adventures/${id.id}/`, { headers: { - Cookie: `${event.cookies.get('auth')}` - } + Cookie: `sessionid=${event.cookies.get('sessionid')}` + }, + credentials: 'include' }); if (!request.ok) { console.error('Failed to fetch adventure ' + id.id); @@ -24,8 +25,9 @@ export const load = (async (event) => { if (adventure.collection) { let res2 = await fetch(`${endpoint}/api/collections/${adventure.collection}/`, { headers: { - Cookie: `${event.cookies.get('auth')}` - } + Cookie: `sessionid=${event.cookies.get('sessionid')}` + }, + credentials: 'include' }); collection = await res2.json(); } @@ -39,8 +41,8 @@ export const load = (async (event) => { } }) satisfies PageServerLoad; -import type { Actions } from '@sveltejs/kit'; -import { tryRefreshToken } from '$lib/index.server'; +import { redirect, type Actions } from '@sveltejs/kit'; +import { fetchCSRFToken } from '$lib/index.server'; const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; @@ -50,29 +52,7 @@ export const actions: Actions = { const adventureId = id.id; if (!event.locals.user) { - const refresh = event.cookies.get('refresh'); - 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' } - }; - } + return redirect(302, '/login'); } if (!adventureId) { return { @@ -81,12 +61,15 @@ export const actions: Actions = { }; } + let csrfToken = await fetchCSRFToken(); + let res = await fetch(`${serverEndpoint}/api/adventures/${event.params.id}`, { method: 'DELETE', headers: { - Cookie: `${event.cookies.get('auth')}`, - 'Content-Type': 'application/json' - } + Cookie: `sessionid=${event.cookies.get('sessionid')}; csrftoken=${csrfToken}`, + 'X-CSRFToken': csrfToken + }, + credentials: 'include' }); console.log(res); if (!res.ok) { diff --git a/frontend/src/routes/api/[...path]/+server.ts b/frontend/src/routes/api/[...path]/+server.ts index 981debb..ef6bbbf 100644 --- a/frontend/src/routes/api/[...path]/+server.ts +++ b/frontend/src/routes/api/[...path]/+server.ts @@ -1,69 +1,77 @@ const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; +import { fetchCSRFToken } from '$lib/index.server'; import { json } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ -export async function GET({ url, params, request, fetch, cookies }) { - // add the param format = json to the url or add additional if anothre param is already present - if (url.search) { - url.search = url.search + '&format=json'; - } else { - url.search = '?format=json'; - } - return handleRequest(url, params, request, fetch, cookies); +export async function GET(event) { + const { url, params, request, fetch, cookies } = event; + const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; + return handleRequest(url, params, request, fetch, cookies, searchParam); } /** @type {import('./$types').RequestHandler} */ 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 }) { - 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 }) { - 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 }) { - 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( url: any, params: any, request: any, fetch: any, cookies: any, + searchParam: string, requreTrailingSlash: boolean | undefined = false ) { 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('/')) { 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 authCookie = cookies.get('auth'); - - if (authCookie) { - headers.set('Cookie', `${authCookie}`); + const csrfToken = await fetchCSRFToken(); + if (!csrfToken) { + return json({ error: 'CSRF token is missing or invalid' }, { status: 400 }); } try { const response = await fetch(targetUrl, { method: request.method, - headers: headers, - body: request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined + headers: { + ...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) { - // For 204 No Content, return a response with no body return new Response(null, { status: 204, headers: response.headers diff --git a/frontend/src/routes/collections/+page.server.ts b/frontend/src/routes/collections/+page.server.ts index 9d2477f..e0cd851 100644 --- a/frontend/src/routes/collections/+page.server.ts +++ b/frontend/src/routes/collections/+page.server.ts @@ -17,10 +17,12 @@ export const load = (async (event) => { let previous = null; let count = 0; let adventures: Adventure[] = []; + let sessionId = event.cookies.get('sessionid'); let initialFetch = await fetch(`${serverEndpoint}/api/collections/?order_by=updated_at`, { headers: { - Cookie: `${event.cookies.get('auth')}` - } + Cookie: `sessionid=${sessionId}` + }, + credentials: 'include' }); if (!initialFetch.ok) { console.error('Failed to fetch visited adventures'); @@ -72,34 +74,9 @@ export const actions: Actions = { formDataToSend.append('start_date', start_date || ''); formDataToSend.append('end_date', end_date || ''); formDataToSend.append('link', link || ''); - let auth = event.cookies.get('auth'); + let sessionid = event.cookies.get('sessionid'); - 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) { + if (!sessionid) { return { status: 401, body: { message: 'Unauthorized' } @@ -119,7 +96,7 @@ export const actions: Actions = { method: 'POST', headers: { 'X-CSRFToken': csrfToken, - Cookie: auth + Cookie: `sessionid=${sessionid}; csrftoken=${csrfToken}` }, body: formDataToSend }); diff --git a/frontend/src/routes/login/+page.server.ts b/frontend/src/routes/login/+page.server.ts index fcafe72..60b2dbe 100644 --- a/frontend/src/routes/login/+page.server.ts +++ b/frontend/src/routes/login/+page.server.ts @@ -2,6 +2,7 @@ import { fail, redirect } from '@sveltejs/kit'; import type { Actions, PageServerLoad } from './$types'; import { getRandomBackground, getRandomQuote } from '$lib'; +import { fetchCSRFToken } from '$lib/index.server'; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; export const load: PageServerLoad = async (event) => { @@ -24,37 +25,29 @@ export const actions: Actions = { default: async (event) => { const formData = await event.request.formData(); const formUsername = formData.get('username'); - const formPassword = formData.get('password'); let username = formUsername?.toString().toLocaleLowerCase(); const password = formData.get('password'); const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; - const csrfTokenFetch = await event.fetch(`${serverEndpoint}/csrf/`); - if (!csrfTokenFetch.ok) { - console.error('Failed to fetch CSRF token'); - event.locals.user = null; - return fail(500, { - message: 'Failed to fetch CSRF token' - }); - } + const csrfToken = await fetchCSRFToken(); - const tokenPromise = await csrfTokenFetch.json(); - const csrfToken = tokenPromise.csrfToken; - - const loginFetch = await event.fetch(`${serverEndpoint}/auth/login/`, { + const loginFetch = await event.fetch(`${serverEndpoint}/_allauth/browser/v1/auth/login`, { method: 'POST', headers: { 'X-CSRFToken': csrfToken, - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + Cookie: `csrftoken=${csrfToken}` }, body: JSON.stringify({ username, password - }) + }), + credentials: 'include' }); + const loginResponse = await loginFetch.json(); if (!loginFetch.ok) { // get the value of the first key in the object @@ -64,25 +57,34 @@ export const actions: Actions = { message: error }); } else { - const token = loginResponse.access; - 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 - }); + const setCookieHeader = loginFetch.headers.get('Set-Cookie'); - 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, '/'); } } }; diff --git a/frontend/src/routes/settings/forgot-password/+page.server.ts b/frontend/src/routes/settings/forgot-password/+page.server.ts index 82fc304..f91db59 100644 --- a/frontend/src/routes/settings/forgot-password/+page.server.ts +++ b/frontend/src/routes/settings/forgot-password/+page.server.ts @@ -1,3 +1,4 @@ +import { fetchCSRFToken } from '$lib/index.server'; import { fail, type Actions } from '@sveltejs/kit'; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; @@ -13,10 +14,14 @@ export const actions: Actions = { 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', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'X-CSRFToken': csrfToken, + Cookie: `csrftoken=${csrfToken}` }, body: JSON.stringify({ email @@ -25,10 +30,7 @@ export const actions: Actions = { if (!res.ok) { let message = await res.json(); - - const key = Object.keys(message)[0]; - - return fail(res.status, { message: message[key] }); + return fail(res.status, message); } return { success: true }; } diff --git a/frontend/src/routes/signup/+page.server.ts b/frontend/src/routes/signup/+page.server.ts index ad24805..2541936 100644 --- a/frontend/src/routes/signup/+page.server.ts +++ b/frontend/src/routes/signup/+page.server.ts @@ -44,19 +44,23 @@ export const actions: Actions = { 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 csrfToken = tokenPromise.csrfToken; - const loginFetch = await event.fetch(`${serverEndpoint}/auth/registration/`, { + const loginFetch = await event.fetch(`${serverEndpoint}/_allauth/browser/v1/auth/signup`, { method: 'POST', headers: { 'X-CSRFToken': csrfToken, - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + Cookie: `csrftoken=${csrfToken}` }, body: JSON.stringify({ username: username, - password1: password1, - password2: password2, + password: password1, email: email, first_name, last_name @@ -65,31 +69,36 @@ export const actions: Actions = { const loginResponse = await loginFetch.json(); if (!loginFetch.ok) { - // get the value of the first key in the object - 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 - }); + return fail(loginFetch.status, loginResponse); } else { - const token = loginResponse.access; - 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: '/' - }); + const setCookieHeader = loginFetch.headers.get('Set-Cookie'); - 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, '/'); } } }; diff --git a/frontend/src/routes/worldtravel/+page.server.ts b/frontend/src/routes/worldtravel/+page.server.ts index 4fea381..fefd08d 100644 --- a/frontend/src/routes/worldtravel/+page.server.ts +++ b/frontend/src/routes/worldtravel/+page.server.ts @@ -9,11 +9,9 @@ export const load = (async (event) => { if (!event.locals.user) { return redirect(302, '/login'); } else { - const res = await fetch(`${endpoint}/api/countries/`, { + const res = await event.fetch(`${endpoint}/api/countries/`, { method: 'GET', - headers: { - Cookie: `${event.cookies.get('auth')}` - } + credentials: 'include' }); if (!res.ok) { console.error('Failed to fetch countries');