pixelmund / svelte-kit-cookie-session Goto Github PK
View Code? Open in Web Editor NEW⚒️ Encrypted "stateless" cookie sessions for SvelteKit
License: MIT License
⚒️ Encrypted "stateless" cookie sessions for SvelteKit
License: MIT License
As said in your doc, the session is initialized with the hook function getContext({ headers })
.
The recent versions of the sveltekit (1.0.0-next.99 ++) hooks feature don't support this function.
How can we adapt our code to continue to use the svelte-kit-cookie-session package?
Everything works fine with sveltekit version v1.0.0-next.98 and before.
Please consider adding support for Cookies Having Independent Partitioned State (CHIPS) by adding the possibility to provide cookie.partitioned
option expressed as boolean value.
This is relevant for enabling setting the session cookies as CHIPS and get ready for the upcoming privacy sandboxing of third-party cookies in Chrome.
Thank you for your excellent job on this utility library!
While package works fine with SvelteKit/Vite, it can't be imported as an ESM package with node.
If I understand correctly, there are three changes to make this work:
"type": "module"
to package.json, so that it's seen as an ESM package."exports": "./dist/index.js"
to package.json, to set what is being exported.My use case for this is handling a WebSocket connection outside the SvelteKit middleware using adapter-node.
Setting up a project with SvelteKit Session Cookie and Houdini i ran into some problems and it took me a while to find a working setup.
If is setup +layout.svelte.ts as recommended:
/** @type {import('./$types').LayoutServerLoad} */
export const load = ({ locals }) => {
return {
session: locals.session.data // You can also use your old `getSession` function if you wish.
};
};
The houdini client receives an empty session object.
In order to solve this, I have to extent hooks.server.ts
import { handleSession } from 'svelte-kit-cookie-session';
import { sequence } from '@sveltejs/kit/hooks';
import { setSession } from '$houdini'
const sessionHandler = handleSession({
secret: 'YOUR_SECRET_KEY'
})
export const handle = sequence(sessionHandler, ({ event, resolve }) => {
setSession(event, event.locals.session.data) //otherwise houdini will not get the session
return resolve(event)
})
Now queries work, but for mutations the client is still receiving empty session objects.
To fix this i have to manually reset __houdini__session in +layout.server.ts:
import { GQL_ActiveDonation } from "$houdini";
/** @type {import('./$types').LayoutServerLoad} */
export const load = async (event) => {
const session = event.locals.session.data || null
return {
session: session,
__houdini__session__: session
}
}
};
Any ideas why the recommended implementation isn't working!?
As it Is only browser side: would this also work with svelte only (without sveltekit)
SvelteKit 415 had this extremely breaking change.
Any plans to update to either make your own version of a session, a la Lucia, or another replacement?
Hi there, I'm getting:
Cannot find module '/home/phocks/sites/quote.ga/node_modules/svelte-kit-cookie-session/dist/esm/core' imported from /home/phocks/sites/quote.ga/node_modules/svelte-kit-cookie-session/dist/esm/handle.js
Something to do with the esm modules or something?
I am trying to make the session cookie work with subdomains. Right now it's setting the cookie with 'subdomain.localhost', and I would like to set it to the root domain so I can support sessions over subdomains.
How can I do this? :)
Love this package so far!
Please, is it safe/secure to pass sensitive session data to page like this?
/** @type {import('@sveltejs/kit').LayoutServerLoad} */
export function load({ locals, request }) {
return {
session: locals.session.data
};
}
Or what is the recommended approach to use a "token" from session in client side requests (fetch) to external API?
Thank you.
Hi,
i'm trying to build my svelte app with the node adapter, but it errors, because i want to set the session secret via an environment variable when launching the server:
src/hooks.server.js
:
import { handleSession } from 'svelte-kit-cookie-session';
import { env } from '$env/dynamic/private';
const sessionSecret = env.VITE_SESSION_SECRET
export const handle = handleSession({
secret: sessionSecret
});
Error when running npm run build
:
Error: Please provide at least one secret
at normalizeConfig (file:///redacted/node_modules/svelte-kit-cookie-session/utils.js:12:15)
at new CookieSession (file:///redacted/node_modules/svelte-kit-cookie-session/core.js:14:24)
at Object.handle (file:///redacted/node_modules/svelte-kit-cookie-session/handle.js:4:25)
at respond (file:///redacted/.svelte-kit/output/server/index.js:2205:42)
at async visit (file:///redacted/node_modules/@sveltejs/kit/src/core/prerender/prerender.js:190:20)
Is it possible to fix that or do i have to think about a different way where i dont have to build the app with a hardcoded sessionsecret?
At the time writing this library there where some problems with the cookie
package and esm
. These should probably be fixed now so we can get rid of the copy pasted one.
I checked the source codes a bit but can't find a way set expiration period in minutes. The only supported option is to set expiry in # of days. If setting cookies in length shorter than a day is not supported, is there a plan to do so?
First of all, thank you for this amazing svelte-kit-cookie-session package!
When I use it in dev mode, everything works perfectly.
But when I build my app and start it, the session deletion fires this error message:
TypeError: 'set' on proxy: trap returned falsish for property 'destroy'
After some testing I found the location of the error in initialize.ts
line 104:
if (sessionCookie.length === 0) return false;
Setting the return value to true
resolves the issue.
There might be reasons why someone might want to update the secret used in session cookies without invalidating sessions. Maybe the previous secret got exposed because someone checked it into Git and pushed it to GitHub, and although nobody has exploited it yet, the secret needs to be changed. Or maybe someone wants to set a policy for their site to rotate the session secret once a month, just on general principles. Either way, it would be useful to have a way to keep two session secrets, previousSecret
and currentSecret
.
The way I see it working is this. If a session fails to decrypt with currentSecret
, decryption is attempted with previousSecret
(unless it's undefined) and if that succeeds, the session is re-encrypted with currentSecret
and a new cookie is issued. In addition, there would need to be a function provided called rotateSecret
or some such, which would move currentSecret
to previousSecret
and then store a new value in currentSecret
.
Doing secret rotation this way would mean that secrets cannot be rotated faster than the expiration time of any given session cookie (otherwise you'd need more than one previousSecret
, i.e. if secrets were rotated every day but cookies were valid for 7 days, you'd need to been 7 previous secrets), but that would be fine in any conceivable scenario that I can think of.
Setup everything according to docs and it works like a charm on chrome, firefox and edge.
However the cookie will not be set on safari. Any ideas why or how to solve this issue?
I know it's not released yet officially - at least based on the GitHub releases, but the update has a weird error, causing failed login attempts.
Error: Not found: /api/v2/user/apps/appData/test1/logs
at resolve (file:///app/server/app.js:1678:20)
at async file:///app/server/app.js:1741:16
at async Object.handle (file:///app/node_modules/svelte-kit-cookie-session/dist/esm/handle.js:7:26)
at async respond (file:///app/server/app.js:1610:22)
at async Array.ssr (file:///app/handler-81c538e0.js:783:19)
Hey, this is likely a case of "I'm doing it wrong" but I wanted to open an issue and see if that is the case or if this is potentially an actual issue.
I am using this library to manage my user authentication session after authenticating with an oauth2 provider. Everything does "work", but with one major caveat.
After going through the login flow, the user does not appear to be logged in. The cookie is set correctly but SvelteKit doesn't seem to notice that. The final step handles the oauth2 callback, sets the cookie and then returns a 302 redirect to send the browser back to the root page of the app. At this point it looks like the user is not logged in. If I refresh the page with cmd/ctrl + r
or even cmd/ctrl + shift + r
the page refreshes, but the user still remains not logged in. However, oddly, if I select the browser address bar and press enter to refresh the page, SvelteKit suddenly reads the cookie correctly and shows the user as being logged in.
This is consistent across Firefox, Chrome, and Safari. I have tried disabling cache as well but the app still has the same behavior.
I do have a working workaround which is to return a meta redirect tag from that final step rather than doing a 302 redirect. That seems to force the browser to fully refresh properly.
Here is the relevant pieces of code in a gist:
https://gist.github.com/KayoticSully/433dbb9eb98f51c0a2f7dac73d53d8e4
This could very well be working as intended. I am new to SvelteKit and this is my first project using it. I may be making assumptions that are wrong.
Logout works perfectly fine too, it is just the login flow/redirect that seems to be problematic.
Is the use-case I am describing something that I should be able to do?
If so, what could be going on here?
Sorry for the long read, but if you have any insight or pointers I would greatly appreciate it! While I do have a workaround I really want to understand why this is happening.
It would be super cool to have a hook in handleSession
for setting the initial session state:
const generateInitialState = () => {
return {
something: 'default'
}
}
export const handle = handleSession({
init: generateInitialState
});
This would enable us to make sure the shape of the object we're storing in sessiondata is always what we expect (and not "undefined")
Hi @pixelmund,
first of all, thanks for this excellent library.
However, I found, that when using it to save a shopping cart for example, you can run out of space quickly. If the cookie gets too large, it is ignored by the browser. Maybe chunked cookies would be an option?
I played around with the plugin and and works smoothly. Nice work!
But I have a question about the encryption. All the session data is exposed to the pages in clear text in early readable in the code when looking for "session". Could an attacker not by comparing encrypted and plaintext values compute the encryption key if he just takes enough time?
I tried to google it with AES encryption but results were inconclusive besides this this
Hi @pixelmund, thanks for doing this project!
I'm trying to get it to run on my project and I get the following error:
18:45:23 [vite] error while updating dependencies:
Error: Build failed with 1 error:
node_modules/.pnpm/[email protected]/node_modules/svelte-kit-cookie-session/dist/utils/crypto/make-crypter.js:9:75: error: Could not read from file: /[Project Path]/crypto
Any idea what could cause this? Thanks in advance!
Edit: sorry for the initial confusing title -- GitHub mobile is kind of weird.
Hey there! I hate asking questions via GitHub issues, but I wasn't sure how else to get in touch. 🙂 I wanted to double check: Is the method of encrypting the cookie considered cryptographically secure? I use OAuth, which requires me to manage access tokens, which I would normally stick into my session on the server side. Using this library, that means they'd be sent to the client (which is traditionally unsafe, but should be fine so long as they're part of an "unbreakable enough" encryption that I can rotate regularly). It looks like they are, but I just need to make sure!
Currently, svelte-kit-cookie-session uses salteen as the encryption implementation. Looking at the salteen source, I see it's encrypting the data by XORing the cookie contents with the secret. XOR encryption is speedy, and fine during development, but trivial to crack (especially if the contents are quite a bit longer than the secret), so it's not safe to use in production. I'd like to be able to choose the encryption method used for the session cookies, e.g. to use @hapi/iron
(or some other library) rather than salteen.
It seems that setting rolling
to true
does not refresh the expiration date.
When using session.refresh()
it seems to work fine.
From what I can tell, this condition always fails because sessionData is always undefined:
if (userOptions?.rolling && !sessionState.invalidDate && sessionData) {
session.refresh();
}
Just updated svelte-kit-cookie-session from 1.2.4 to 1.3.0, and now I'm getting the following Vite error whenever I run pnpm run dev
:
1:10:31 PM [vite] new dependencies found: svelte-kit-cookie-session, updating...
> node_modules/svelte-kit-cookie-session/dist/utils/crypto/make-crypter.js:9:75: error: Could not read from file: (REPOROOT)/crypto
9 │ import { pbkdf2Sync, createDecipheriv, createCipheriv, randomBytes, } from "crypto";
╵ ~~~~~~~~
(I replaced my repo root with the text (REPOROOT)
above since I'm running this from a directory nested about five deep in my folder hierarchy, and without that replacement the error message was even more annoyingly long than it already is.)
Running with npm
instead of pnpm
I get the same error. However, if I run pnpm run build
followed by pnpm run preview
then everything works.
I know I need to somehow tell Vite that svelte-kit-cookie-session
is only Node-based and should therefore be allowed to import Node packages like crypto
, but I'm not yet familiar enough with Svelte-Kit to know how to do that off the top of my head. Which leads me to report it here, because you might want to add something to the svelte-kit-cookie-session README about how to work around this Vite behavior. (And I'll update this issue if I figure it out.)
In my own project i decided to decouple my backend from sveltekit and i would love to use the same library/api as a middleware/connect handler. I don't know if this should be included in this or if i should create a separate one.
Not sure what's best practice here, or if making it async is worth it. Performance benchmarks shows around 700ms
for initializing and decoding 25000
sessions (20000
) for encoding.
Here, we see that the "cookie" object is optional.
But in most libraries, there would be nice JSDoc comments here that describe each individual option and the default value that the library chooses for you. (The JSDoc comments would cause mouse-over documentation to appear in VSCode so that I wouldn't have to leave my code editor to search the documentation.)
Can this be added? It's unclear what I need to specify and/or change in order to make using this product secure.
Will this be compatible with svelte 4?
I want to thank you for this library! It helps a lot in my side-projects! ❤️
Keep it up! 💪🏻
For some reason, we've seen an influx of questions about how to properly handle user session after an OAuth login with SvelteKit. While this isn't exclusive to svelte-kit-cookie-session
, your users are also unknowingly affected because of the SameSite=Strict
default.
The issue with SameSite=Strict
is that it is too restrictive for the vast majority of use-cases. It prevents browsers from sending cookies on all requests that originated from a third-party domain, even if indirectly. This is the case for OAuth logins: third-party services usually issue 302
redirects to your callback endpoints where you're likely to further redirect the user to a specific page—but since the redirect chain originated outside your domain, the browser will not send the cookies upon arrival at the target page.
While it does provide more security than Lax
, its usefulness is dubious for most people. With Strict
, if a third-party domain even links to your site with a vanilla <a href="https://www.yoursite.com">Your site</a>
anchor, your users will be logged out when navigating through those links as the cookies will be blocked.
Please, consider switching to SameSite=Lax
by default as it enables most of the safety guards while not breaking common use-cases.
Now that the encryption implementation is using Node's crypto
module, it should be pretty simple to allow Buffer or Uint8Array instances to be used as encryption keys: as far as I can tell, the only changes that would be needed would be:
encryptionKey
parameters to encrypt
and decrypt
in utils/crypto.ts
secret
in SessionOptions
.secret instanceof Uint8Array
. (Note that Buffer is a subclass of Uint8Array so that check would be sufficient for both types).I've looked through the Node.js implementation of pbkdf2Sync
and it seems to use the entire string, not just the first N characters of the string, as a source of entropy. Which means that this won't (AFAICT) be a security improvement, just a nice-to-have feature, because converting a hex-encoded string into a byte array will still produce exactly the same amount of entropy to feed into the PBKDF2 function. So I considered not making this feature request. In the end, though, I decided to make the request, because the amount of work it will take is minimal and it will smooth out the DX for some developers. For example, if someone uses a secret that they read from a file rather than from an environment variable, being able to pass a Buffer or a Uint8Array will mean they can just pass the results from fsPromises.readFile(secretFilename)
directly into handleSession
, without needing to have any file encoding. Which in turn means that they can create their secret file by simply doing dd if=/dev/random of=secret.bin bs=32 count=1
and not have to worry about encoding the file to a valid string at all.
I think it would need to use webcrypto. Not sure.
I can post the error message I get later or tomorrow.
When using the (latests) version 3.3.0 of the package, it seems the build or packaging process is broken because the whole app breaks with the following message:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package '$app' imported from /<my_project>/node_modules/svelte-kit-cookie-session/utils.js
at new NodeError (node:internal/errors:387:5)
at packageResolve (node:internal/modules/esm/resolve:852:9)
at moduleResolve (node:internal/modules/esm/resolve:901:20)
at defaultResolve (node:internal/modules/esm/resolve:1115:11)
at nextResolve (node:internal/modules/esm/loader:163:28)
at ESMLoader.resolve (node:internal/modules/esm/loader:837:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
at link (node:internal/modules/esm/module_job:75:36)
If I inspect the package, indeed the new imports remain but the packaged structure does not match the paths:
I'd like to initialize my session id in the sessionHandler in hooks.server.ts
:
export const handle = handleSession({
key: 'session',
init: (event) => ({
id: crypto.randomUUID(),
}),
...
In my top-level +layout.server.ts
I have:
export const load = (async ({url, cookies, locals}) => {
....
return {
locale,
locales,
session: locals.session.data,
}
})
Even though locals.session.data
contains a freshly created id, the cookie is not set. The handler doesn't return a "Set-Cookie" header.
It only sets a cookie if I add a dummy assignment, such as:
await locals.session.update(({id}) => ({id}))
but this updates the cookie on each request. (Might be required for rolling=true anyway.)
I would like to have the cookie set initially without such dummy assignment.
Btw.: thanks for such a great project! 👍
Hi I was struggling to find why the session was not initializing in hooks. By default svelte has a hooks configuration in src/hooks.server.js. And not in src/hooks as the installation guide points out
Looks like the underlying cookie library already has support for it, but the "secure" option isn't currently exposed.
Hello! I'm looking into integrating this library into a project of mine, and I must start by saying that besides the package in itself which looks neat, the README is very well written and easy to follow. Thank you very much for the work!
I was wondering, what would prevent a malicious attacker from just getting a copy of the stateless session cookies produced by this package, and use it to access protected resources indefinitely in the future, regardless of the browser-enforced expiration?
I might be wrong, but from what I understand, expires
is only used to set the Cookie Max Age, and it is not part of the encrypted/signed payload.
So even though the Cookie payload is encrypted, any cookie will remain valid for as long as the server uses the same session secret, even after the actual non-malicious user generated new cookies.
One mitigation I see today is through the rotation of secrets, but this is impractical for low-activity applications that do not get deployed often, and have low enough traffic to not have to rotate their encryption secrets.
Another way to prevent this problem would be to add an expires
attribute to the encrypted payload, but it feels a lot like re-importing concepts from JWTs, and suggests we might need to import other concepts from the JWT world like the issuer
, the notBefore
date, the subject
, the audience
, etc... (see here)
Or maybe there is some mechanism that I missed that prevents all that?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.