- Notifications
You must be signed in to change notification settings - Fork12
⚒️ Encrypted "stateless" cookie sessions for SvelteKit
License
pixelmund/svelte-kit-cookie-session
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
⚒️ Encrypted "stateless" cookie sessions for SvelteKit
ThisSvelteKit backend utility allows you to create a session to be stored in the browser cookies via an encrypted seal. This provides strong client/"stateless" sessions.
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 thanexpress-session where the cookie contains a session ID to then be used to map data on the server-side.
- Upgrading
- Installation
- Usage
- Initializing
- Secret Rotation
- Setting the Session
- Accessing the Session
- Destroying the Session
- Refreshing the Session
- Configure Expiry Date
- Save unsaved session with the initial data
By default the cookie has an ⏰ expiration time of 7 days, set via [expires] which should be anumber in eitherdays | hours | minutes | seconds configurable by theexpires_in option.
The internal encryption library changed to the@noble/ciphers which is up to 35% faster than the previous implementation. The encryption should also now be even more secure.Because of the change of the encryption library we have an major version bump. You now have to provide a secret with an exact length of 32 characters or bytes. You can usePassword Generator to generate strong secrets.
Install intodependencies
npm i svelte-kit-cookie-sessionyarn add svelte-kit-cookie-sessionpnpm add svelte-kit-cookie-session
Update yourapp.d.ts file to look something like:
importtype{Session}from'svelte-kit-cookie-session';typeSessionData={views:number;};// See https://kit.svelte.dev/docs/types#app// for information about these interfacesdeclare global{namespaceApp{// interface Error {}interfaceLocals{session:Session<SessionData>;}interfacePageData{// can add any properties here, return it from your root layoutsession:SessionData;}// interface Platform {}}}export{};
You can find some examples in the src/routes/tests folderTests.
The secret is a private key or list of private keys you must pass at runtime, it should be32 characters long. UsePassword Generator to generate strong secrets.
src/hooks.server.ts
import{handleSession}from'svelte-kit-cookie-session';// You can do it like this, without passing a own handle functionexportconsthandle=handleSession({// Optional initial state of the session, default is an empty object {}// init: (event) => ({// views: 0// }),// chunked: true // Optional, default is false - if true, the session will be chunked into multiple cookies avoiding the browser limit for cookiessecret:'SOME_COMPLEX_SECRET_32_CHARSLONG'});// Or pass your handle function as second argument to handleSessionexportconsthandle=handleSession({secret:'SOME_COMPLEX_SECRET_32_CHARSLONG'},({ event, resolve})=>{// event.locals is populated with the session `event.locals.session`// Do anything you want herereturnresolve(event);});
In case you're usingsequence(), do this
constsessionHandler=handleSession({secret:'SOME_COMPLEX_SECRET_32_CHARSLONG'});exportconsthandle=sequence(sessionHandler,({ resolve, event})=>{// event.locals is populated with the session `event.locals.session`// event.locals is also populated with all parsed cookies by handleSession, it would cause overhead to parse them again - `event.locals.cookies`.// Do anything you want herereturnresolve(event);});
is supported. 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:
- rotate secrets for better security every two (or more, or less) weeks
- change the secret you previously used because it leaked somewhere (😱)
Then you can use multiple secrets:
Week 1:
exportconsthandle=handleSession({secret:'SOME_COMPLEX_SECRET_32_CHARSLONG'});
Week 2:
exportconsthandle=handleSession({secret:[{id:2,secret:'SOME_OTHER_COMPLEX_SECR_32_CHARS'},{id:1,secret:'SOME_COMPLEX_SECRET_32_CHARSLONG'}]});
Notes:
idis required so that we do not have to try every secret in the list when decrypting (theidis part of the cookies value).- The secret used to encrypt session data is always the first one in the array, so when rotating to put a new secret, it must be first in the array list
- Even if you do not provide an array at first, you can always move to array based secret afterwards, knowing that your first password (
string) was given{id:1}automatically.
Setting the session can be done in two ways, either via theset method or via theupdate method.
If the session already exists, the data gets updated but the expiration time stays the same
src/routes/counter/+page.server.js
/**@type {import('@sveltejs/kit').Actions} */exportconstactions={default:async({ locals})=>{const{ counter=0}=locals.session.data;awaitlocals.session.set({counter:counter+1});return{};}};
Sometimes you don't want to get the session data first only to increment a counter or some other value, that's where the update method comes in to play.
src/routes/counter/+page.server.ts
/**@type {import('@sveltejs/kit').Actions} */exportconstactions={default:async({ locals, request})=>{awaitlocals.session.update(({ count})=>({count:count ?count+1 :0}));return{};}};
After initializing the session, your locals will be filled with a session object, we automatically set the cookie if you set the session via locals.session.set({}) to something and receive the current data via locals.session.data only.
src/routes/+layout.server.js
/**@type {import('@sveltejs/kit').LayoutServerLoad} */exportfunctionload({ locals, request}){return{session:locals.session.data};}
src/routes/+page.svelte
<script>import {page }from'$app/stores';$: session=$page.data.session;</script>
src/routes/auth/login/+page.server.js
/**@type {import('@sveltejs/kit').PageData} */exportfunctionload({ parent, locals}){const{ session}=awaitparent();// or// locals.session.data.session;// Already logged in:if(session.userId){throwredirect(302,'/')}return{};}
src/routes/logout/+page.server.js
/**@type {import('@sveltejs/kit').Actions} */exportconstactions={default:async()=>{awaitlocals.session.destroy();return{};}};
src/routes/refresh/+page.server.js
/**@type {import('@sveltejs/kit').Actions} */exportconstactions={default:async()=>{awaitlocals.session.refresh(/** Optional new expiration time in days */);return{};}};
You can also specify a percentage from 1 to 100 which refreshes the session when a percentage of the expiration date is met.
Note this currently only fires if a session is already existing
handleSession({rolling:true// or 1-100 for percentage o the expiry date met,});
You can configure the expiry date of the session cookie via theexpires option. It should be anumber in eitherdays | hours | minutes | seconds.
handleSession({expires:160,// 160 minutesexpires_in:'minutes',// minutes | hours | days | seconds});
You can save unsaved sessions with the initial data via thesaveUninitialized option. It should be aboolean and the default isfalse.
handleSession({init:()=>({views:0}),saveUninitialized:true,});
About
⚒️ Encrypted "stateless" cookie sessions for SvelteKit
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.