-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SSR support #15
Comments
@thefersh Could you elaborate more a little on your use case and why the default AuthStore doesn't work for you? You can create your own AuthStore and attach it during the client initialization. The only requirements is to be compatible with the AuthStore type interface. For example: class MyCustomAuthStore {
token = "";
model = {};
isValid = false;
save(token, model) {
// implement save logic...
}
clear() {
// implement clear logic...
}
}
const client = new PocketBase("http://localhost:8090", 'en-US', new MyCustomAuthStore()); |
@ganigeorgiev you are right. |
The above example should be framework agnostic. There shouldn't be a need for a special nextjs/remix implementation. Could you elaborate why the default implementation doesn't work in your case, so that I can understand better what actually prevents you to use it in nextjs/remix? |
Hola, He logrado implementar autentificación para SSR, esto debería ser agnóstico de librerías o frameworks como nextjs/remix. en mi caso he utilizado sveltekit.
|
If you are going with storing the auth token in a cookie, then I would recommend setting |
@oswaldohuillca in your based on https://github.com/maticzav/nookies#reference, calling this means that it's not possible to set the https://github.com/maticzav/nookies/blob/master/packages/nookies/src/index.ts#L111 if (isBrowser()) {
if (options && options.httpOnly) {
throw new Error('Can not set a httpOnly cookie in the browser.')
}
document.cookie = cookie.serialize(name, value, options) so I guess we are back to square one. I struggle to understand how one should get/set server side cookies within the |
@ollema There are several ways to pass the the ctx/response object but the issue is that this object could have completely different API depending on the node framework you are using and I'm not sure if there is a general solution to this (I haven't had a chance to research it yet). The response object could be provided during initialization as constructor argument to your custom store, as a setter/provider function (eg. import PocketBase, { BaseAuthStore } from 'pocketbase';
class CustomAuthStore extends BaseAuthStore {
constructor(resp) {
super();
this.resp = resp;
}
...
save(token, model) {
super.save(token, model);
this.resp.setHeader(...)
}
clear() {
super.clear();
this.resp.setHeader(...)
}
}
const client = new PocketBase('http://127.0.0.1:8090', 'en-US', CustomAuthStore(resp)); |
hmm, is the idea that you would create a new rather than keeping a "singleton" client export from a module? (I'm not sure if I'm using the correct terms here) |
Yes, creating a new client instance for each request/response and using the cookie AuthStore to load the auth data is the easiest way to solve this, but as mentioned there are other approaches to load the response object depending on your use case. Using a single global client/authStore instance may not always work since it could conflict with how node processes requests in the event loop (eg. startRequest1 -> wait for some db1/network1 call -> in the meantime startRequest2 -> wait for some db2/network2 call -> return db1 response -> return db2 response, etc.). I'll try to spend some time on this researching the possible approaches, but that will be after v0.5.0 release of the main repo. |
@ganigeorgiev figured I would give an update on the After doing some research it seems like this is the most "idiomatic" approach (for now, SvelteKit is still not 1.0 so things could change!): Like you suggested you would use a new client instance for each request/response. In SvelteKit, this could be handled in the
import type { Handle } from '@sveltejs/kit';
import PocketBase, { User } from 'pocketbase';
import cookie from 'cookie';
export const handle: Handle = async ({ event, resolve }) => {
const client = new PocketBase('http://127.0.0.1:8090');
event.locals.pocketbase = client;
const { token, user } = cookie.parse(event.request.headers.get('Cookie') ?? '');
if (!token || !user) {
return await resolve(event);
}
client.authStore.save(token, new User(JSON.parse(user)));
if (client.authStore.isValid) {
event.locals.user = client.authStore.model as User;
}
return await resolve(event);
}; By setting In the Now, while the If I understood it correctly, you would create server endpoints for this, for example: import type { RequestHandler } from '@sveltejs/kit';
import cookie from 'cookie';
const defaultCookieOptions = { maxAge: 30 * 24 * 60 * 60, path: '/', httpOnly: true, sameSite: true, secure: true };
export const POST: RequestHandler = async ({ request, locals }) => {
const response = new Response('{}');
const { email, password } = await request.json();
try {
const { token, user } = await locals.pocketbase.users.authViaEmail(email, password);
locals.pocketbase.authStore.save(token, user);
locals.user = user;
response.headers.append('Set-Cookie', cookie.serialize('token', token, defaultCookieOptions));
response.headers.append('Set-Cookie', cookie.serialize('user', JSON.stringify(user), defaultCookieOptions));
} catch {}
return response;
}; and import type { RequestHandler } from '@sveltejs/kit';
import cookie from 'cookie';
const defaultCookieOptions = { maxAge: -1, path: '/', httpOnly: true, sameSite: true, secure: true };
export const POST: RequestHandler = async ({ locals }) => {
const response = new Response('{}');
locals.pocketbase.authStore.clear();
locals.user = undefined;
response.headers.append('Set-Cookie', cookie.serialize('token', '', defaultCookieOptions));
response.headers.append('Set-Cookie', cookie.serialize('user', '', defaultCookieOptions));
return response;
}; It should be noted that the snippets above do not have any error handling, they can just be seen as inspiration. To update the profile of the currently logged in user, you could add an additional Final thoughts
|
@ollema I like your approach with the I would probably only move all cookie handling logic inside the hook handler so that all other server actions could operate only with the export const handle = async ({ event, resolve }) => {
const client = new PocketBase('http://127.0.0.1:8090');
event.locals.pocketbase = client;
// load auth data into the client from a request cookie
// ...
const response = await resolve(event);
// update the response cookie header(s) with the latest `client.authStore` state
// in case it was modified by some of the actions
// ...
return response;
}; For this week, I have planned after work to explore the SSR handling in the other frameworks (nextjs, nuxt2, nuxt3, remix), but I have doubts that there is a single solution that will work out of the box for all of them (even only for SvelteKit, depending which server handler you use and how you structure your app, there are different exported objects to access and update the request/response headers). Supabase seems to deal with this by providing various auth helpers but I want to avoid that because it will be hard to maintain by myself in the long run. I still have to do a more throughout research, but in general I think we can improve at least a little the SSR handling in 2 ways:
As a side note, in order to support mixed browser and server-side usage at the same time, we also probably would have to set by default the |
I've explored the available options the last couple of days, but I couldn't find a "one size fit all" solution, so I've implemented the following in the latest v0.6.0 SDK release:
The above should help (or at least to give you some idea) how to deal with SSR, but if someone still have difficulties making it work, feel free to let me know and I'll try to provide some guidance based on your use case. |
The two helper methods have greatly simplified auth for me (I am using SvelteKit). Assuming the naming of the cookie is transparent to the user, isn't there a need for an orthogonal helper method to clear the auth cookie? |
@aphilas As mentioned in the above discussion, the 2 helper methods only parse and serialize cookie strings and doesn't handle the actual cookie fetch/set because each framework uses different interface and methods for working with the server request and response objects (eg. it could be
No because if you are following the example from https://github.com/pocketbase/js-sdk#ssr-integration, the cookie will be set as "expired" on |
Oh thanks, that makes sense. I was manually clearing the cookie. |
@aphilas Also note that because by default // src/routes/logout/+server.js
//
// creates a `POST /logout` server-side endpoint
export function POST({ request, locals }) {
locals.pocketbase.authStore.clear();
// return a message or you may want to redirect to some other page
return new Response('Success logout...');
} If you want to be able to clear the cookie from both server-side and client-side, you'll have to:
client.authStore.onChange(() => {
document.cookie = client.authStore.exportToCookie({ httpOnly: false });
}); |
Thanks for the clarification.
I went for the first approach—the POST endpoint (so now they're called
actions huh : D).
…On Fri, Aug 26, 2022 at 9:24 PM Gani Georgiev ***@***.***> wrote:
@aphilas <https://github.com/aphilas> Also note that because by default
client.authStore.exportToCookie() will generate a cookie string with the
HttpOnly attribute, *the cookie can be accessed and set only server-side*
(which is recommended), so you have to create a "logout" SvelteKit server
action that calls client.authStore.clear(), eg:
// src/routes/logout/+server.js//// creates a `POST /logout` server-side endpointexport function POST({ request, locals }) {
locals.pocketbase.authStore.clear();
// return a message or you may want to redirect to some other page
return new Response('Success logout...');}
------------------------------
If you want to access the cookie from both server-side and client-side,
you'll have to:
1. set explicitly HttpOnly to false, eg. client.authStore.exportToCookie({
httpOnly: false })
2. add a onChange listener to the client-side client to set the cookie
using document.cookie (or the experimental CookieStore
<https://developer.mozilla.org/en-US/docs/Web/API/CookieStore> which
us currently supported only by Chrome/Edge), eg. something like:
client.authStore.onChange(() => {
document.cookie = client.authStore.exportToCookie({ httpOnly: false });});
—
Reply to this email directly, view it on GitHub
<#15 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AHPTR2RSW3VQGWGAFZDMP7LV3EDXNANCNFSM54D5LGZA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Hi I'm trying to follow the example for sveltekit but crashes on the hooks file the moment it calls the exportToCookie function, it throws:
my hook file:
I'm still learning so what could I be missing? 🤔 Thx in advance! |
@IHummer Could you please check what version of node.js are you using? |
@IHummer I've just checked. I'll publish shortly a v0.6.2 release making it optional since it is used only for a very edge case (when the cookie exceed 4096 and there are multi-byte characters like emojis in the serialized json). |
I was using node v16.16 xd Thanks for the support! |
@IHummer I've just released a v0.6.2 of the SDK with a fallback check when Please update your dependencies ( |
I've just checked and it's working without problems thanks!! |
@ganigeorgiev It would probably be good to add a Nuxt2 example since it is the current stable version and has many more users than Nuxt3. |
Confused about next js 13 implementation. Please Help |
Started playing with solid-start and pocketbase. Couldn't make pocketbase work inside routeData function. |
@subhasish-smiles Please open a new issue with more details about your setup and steps to reproduce (pseudo-code/code sample would be also helpful). |
I am currently exploring Pocketbase using their Pocketbase Js with Remix Js.
The problem is that I can't adapt the operation of AuthStore with each request to the server.
Suggestion: To solve the drawback can an additional parameter to set the token manually.
[Original]
Compatibilidad con SSR
Actualmente estoy explorando Pocketbase usando su Pocketbase Js con Remix Js.
El problema es que no puedo adaptar el funcionamiento de AuthStore con cada petición al servidor.
Sugerencia: Para solucionar el inconveniente pueden un parámetro adicional para poner el token manualmente
The text was updated successfully, but these errors were encountered: