Svemix provides you with ⚒️ encrypted "stateless" cookie sessions, how this works is create a session to be stored in the browser cookies via a encrypted seal. The seal stored on the client contains the session data, not your server, making it a "stateless" session from the server point of view. This is a different take than express-session where the cookie contains a session ID to then be used to map data on the server-side.
Svemix has adopted svelte-kit-cookie-session (which is also developed by me) with some tweaks and modifications / optimizations.
The secret is a private key or list of private keys you must pass at runtime, it should be at least
32 characterslong. Use Password Generator to generate strong secrets.
tsimport type {GetSession } from '@sveltejs/kit';import {handleSession } from 'svemix/session';export constgetSession :GetSession = ({locals }) => {returnProperty 'session' does not exist on type 'Locals'.2339Property 'session' does not exist on type 'Locals'.locals .. session data ;};export consthandle =handleSession ({// This should come from an secret environment variable and never be exposed on github.secret :process .env ['COOKIE_SECRET'] as string,// Pass the getSession function, default uses all data inside locals.session.datagetSession },// Optional own handle function can be passed hereasync function ({event ,resolve }) {// event.locals is populated with the session `event.locals.session` and `event.locals.cookies`;constresponse = awaitresolve (event );returnresponse ;});
It allows you to change the secret used to sign and encrypt sessions while still being able to decrypt sessions that were created with a previous secret.
This is useful if you want to:
Then you can use multiple secrets:
Week 1:
tsexport constCannot find name 'handleSession'.2304Cannot find name 'handleSession'.handle =({ handleSession secret : 'SOME_COMPLEX_SECRET_AT_LEAST_32_CHARS'});
Week 2:
tsexport constCannot find name 'handleSession'.2304Cannot find name 'handleSession'.handle =({ handleSession secret : [{id : 2,secret : 'SOME_OTHER_COMPLEX_SECRET_AT_LEAST_32_CHARS'},{id : 1,secret : 'SOME_COMPLEX_SECRET_AT_LEAST_32_CHARS'}]});
Notes:
id is required so that we do not have to try every secret in the list when decrypting (the id is part of the cookies value).string) was given {id:1} automatically.If you're updating or destroying the session inside one of your actions, the Form Component automatically updates the client store as well. This is really helpful because typically the session would only be set on a full page reload / server side rendering.
If the session already exists, the data get's updated but the expiration time stays the same
There are two ways to update the session via: session.update and via session.set;
<script context="module" lang="ts" ssr>
import { redirect } from 'svemix/server';
import { authenticateUser } from '$lib/auth';
import type { Action } from 'svemix';
import type { User } from '@prisma/client';
interface ActionData {
email?: string;
password?: string;
}
export const action: Action<ActionData> = async function ({ request, locals }) {
// @ts-ignore
const body = await request.formData();
const email = body.get('email');
const password = body.get('password');
try {
const { user, errors } = await authenticateUser(email, password);
if (errors.email || errors.password) {
return {
values: {
email,
password
},
errors
};
}
await locals.session.set({ isLoggedIn: true, user })
return redirect('/', 302);
} catch (error) {
return {
values: {
email,
password
},
formError: error.message
};
}
};
</script>After initializing the session, your locals will be filled with a session JS Proxy, this Proxy automatically sets the cookie if you use locals.session.set or locals.session.update and receive the current data via locals.session.data only.
<script context="module" lang="ts" ssr>
import type { Loader } from 'svemix';
import { redirect } from 'svemix/server';
export const loader: Loader<any> = function ({ locals }) {
// Redirect the user to an different page
// The loader runs everytime the session stores updates with one of your actions
// This results in no need to full page reloads.
if (locals.session.data?.isLoggedIn) {
return redirect('/', 302);
}
return {};
};
</script><script context="module" lang="ts" ssr>
import type { Action } from 'svemix';
export const action: Action<any, any, Locals> = async function ({ locals, request }) {
// @ts-ignore
const body = await request.formData();
const _action = body.get('_action');
if (_action === 'logout') {
await locals.session.destroy();
}
return {};
};
</script>
<script lang="ts">
import { session } from '$app/stores';
import { Form } from 'svemix';
</script>
{#if $session.isLoggedIn}
<Form>
<input type="hidden" name="_action" value="logout" />
<button type="submit">Logout</button>
</Form>
{:else}
<a href="/auth/login"> Sign in </a>
{/if}To define global types for your session you can edit your app.d.ts and add something like this:
ts// app.d.tsinterfaceSessionData {views : number;}// See https://kit.svelte.dev/docs#typescript// for information about these interfacesdeclare namespaceApp {interfaceLocals {session : import('svemix/session').Session <SessionData >;cookies :Record <string, string>;}interfacePlatform {}interfaceSession extendsSessionData {}interfaceStuff {}}