Skip to content
Init is coming / May 19 - 23
Back

I do SSR auth. What's the best way to set up an authenticated client-side Realtime connection?

  • 0
  • Auth
  • Web
  • Realtime
  • Cloud
deusMarte
22 Oct, 2024, 16:19

I am currently handling authentication entirely server-side via an /api/auth endpoint, so I don't have any Appwrite client instances running client-side.

However, Realtime requires being authenticated on the client side to stream events the user has access to.

My challenge is how to leverage the SSR auth I already have in place to securely authenticate a client-side Appwrite instance to subscribe to Realtime events. So far, I see two possible approaches:

  1. Return the session cookie in my server's /api/auth endpoint response and use it to authenticate the client-side Appwrite instance.
  2. Reimplement client-side authentication, separate from my current SSR setup, to authenticate the Appwrite client directly.

For the first approach, I'm concerned that exposing the session cookie to JavaScript (even if it was originally set as httpOnly) opens up security vulnerabilities to XSS attacks or malicious browser extensions. For the second approach, it feels like a step backward in terms of developer experience. Additionally, maintaining both SSR and client-side authentication seems to violate DRY principles.

What’s the best approach to securely set up an authenticated client-side Appwrite Realtime connection without giving up on SSR auth?

TL;DR
Developers are struggling with setting up an authenticated client-side Realtime connection while doing Server-Side Rendering (SSR) auth. They are debating whether to return the session cookie on the `/api/auth` endpoint response or separate client-side authentication. One suggestion is to create a cookie with a specified domain. The primary concern is the security risks associated with exposing the cookie to client-side JavaScript. Solution: The proposed cookie solution could help, ensuring the cookie is sent to both the main app and Appwrite subdomain. This would enable authenticated access and Realtime subscriptions on the client-side, but be cautious with the security implications of exposing the
Steven
22 Oct, 2024, 16:32

i would suggest creating a cookie that will be sent to both your backend and Cloud. So in order to do that, assuming your have:

  • appwrite endpoint: appwrite.myapp.com
  • app: myapp.com

you'll need to create a cookie with

  • name: a_session_<project id>
  • domain: .myapp.com

I'm pretty sure the cookie will be sent to both your myapp.com and appwrite.myapp.com

deusMarte
22 Oct, 2024, 16:49

Hi Steven, thank you for your response and suggestion.

I do currently have an implementation that gives me access to the session cookie on my backend and on the client. I am just concerned about the potential security risks of returning the session cookie to the client as part of my endpoint response (which theoretically makes it JavaScript-accessible, unlike returning it as a secure set-cookie header configured to be httpOnly).

Another concern I have is that having an authenticated client-side Appwrite client instance (necessary to subscribe to Realtime events) eliminates the control I have over filtering and transforming the data I return to users through my Nuxt server functions. If clients can communicate directly with my Appwrite database without a server endpoint as an intermediary, the only control I have over what they get are explicit Appwrite resource permissions, which are a great baseline, but not as flexible as custom functions. But, I guess that's a different topic altogether. The only issue I currently have with the sharing of the session cookie approach is the risks it opens up.

Steven
22 Oct, 2024, 16:56

you can make it so it's not accessible by javascript client-side

deusMarte
22 Oct, 2024, 18:36

Do you mind sharing a pointer on how to go about that, please?

Steven
22 Oct, 2024, 18:51

there are options on the cookie. like the httpOnly

deusMarte
22 Oct, 2024, 19:18

Yes, but if I do that then I can't access the cookie with my client-side code, and so I can't use it to authenticate the client-side Appwrite client. That is precisely the reason I opened this thread. If the cookie is set to httpOnly, it is secure, but can't be used client-side. On the other hand, if I return it as part of my API auth response payload, that breaches the security protections of httpOnly, no?

Or maybe I'm wrong? Like, do you know if the data objects server endpoints in Next or Nuxt return can be accessed by malicious browser extensions or via an XSS attack? (assuming the fetch requests happen over https and the fetched data is never rendered, just used by Next or Nuxt internally)

Steven
22 Oct, 2024, 19:21

you wouldn't access it with client-side code. the browser should send the cookie automatically

deusMarte
22 Oct, 2024, 19:56

That's right. Currently, on my api/auth endpoint, I get the cookie from the request to determine if a user is already authenticated, and use it to set a session in a server-side Appwrite client instance.

TypeScript
// this is a server-side util:
export function createSessionClient(event: H3Event<EventHandlerRequest>) {
  const runtimeConfig = useRuntimeConfig(event)

  const client = new Client()
    .setEndpoint(runtimeConfig.public.appwriteEndpoint)
    .setProject(runtimeConfig.public.appwriteProject)

  const session = getCookie(event, SESSION_COOKIE)

  if (session)
    client.setSession(session)

  const account = new Account(client)
  const databases = new Databases(client)

  return { account, databases }
}

Now, to subscribe to channels with Appwrite Realtime, I need to set up the connection using a client-side Appwrite client. I would like to use the same client.setSession(SESSION_COOKIE) technique, but to do that I need to have access to the session cookie client-side. It is not enough that the browser automatically sends the cookie along with my request this time because, unlike my server endpoint, Appwrite doesn't automatically instantiate a new client instance and set its session based on the cookies the browser sent along. So, I need to authenticate the client-side Appwrite client instance myself, and to do so, I am currently returning the session cookie along with the response payload of my server auth API endpoint. Again, the problem with that approach is that, as far as I know, it bypasses the security protections of the cookie (even if it was initially set as httpOnly), and exposes it to potential extraction by malicious client-side code, no?

Steven
22 Oct, 2024, 20:22

I would like to use the same client.setSession(SESSION_COOKIE) technique instead of doing client.setSession() the idea is to have the cookie automatically send because that's how cookies work

Appwrite doesn't automatically instantiate a new client instance and set its session based on the cookies the browser sent along If set up as I suggested, the cookie should automatically be sent to the request to Appwrite and be authenticated

exposes it to potential extraction by malicious client-side code, no? Honestly, if you had an XSS vulnerability, you have way bigger problems

deusMarte
23 Oct, 2024, 01:35

Thank you for your explanation. I believe I misunderstood your original suggestion, my apologies.

I'm currently developing my app in localhost, have no custom domain yet, and am using Appwrite's default endpoint (https://cloud.appwrite.io/v1').

In my api/auth server endpoint, I have a login function where I am currently setting the session cookie:

TypeScript
// inside an event handler in /server/api/auth.ts:
  const logIn = async (email: string, password: string) => {
    const session = await authAccount.createEmailPasswordSession(email, password)
    setCookie(event, SESSION_COOKIE, session.secret, {
      httpOnly: true,
      secure: true,
      sameSite: 'strict',
      expires: new Date(session.expire),
      path: '/',
    })
    // ...update payload response object with user id and email
  }

I would like to follow your suggestion of configuring the cookie with domain: '.myapp.com', but since I don't have a domain yet, and I am using Appwrite's default endpoint, I am not sure how to proceed. I thought of calling setCookie twice, once as it is now, and another with domain: 'cloud.appwrite.io' but, if I remember correctly, it's not possible to set cookies for a third-party domain.

I guess for now, I could just add the session cookie to the payload response object and use it with setSession() to authenticate the client-side Appwrite client instance that will establish the Realtime subscription. And, once I have a custom domain, then configure Appwrite's endpoint to live at appwrite.mydomain.com, and update the cookie definition in my logIn function to be automatically sent to .mydomain.com, as you suggested.

Is this reasoning correct, or am I perhaps misunderstanding the challenge of configuring the session cookie for localhost and cloud.appwrite.io?

Thank you for your time and support.

Steven
23 Oct, 2024, 01:37

Maybe you can update your hosts file to fake the hostname for local development

deusMarte
23 Oct, 2024, 01:47

Thank you for the pointer! I'll look into doing that. Thanks again.

maximilianls
4 Nov, 2024, 17:36

Sorry to revive this thread, but what solution did you eventually go with? Im facing the same exact type of issues, and even client.setSession with the cookie i return from my nextjs backend api route doesnt work. I can see that realtime connections work with collections with read "any" but not when requiring a valid session. @deusMarte

Reply

Reply to this thread by joining our Discord

Reply on Discord

Need support?

Join our Discord

Get community support by joining our Discord server.

Join Discord

Get premium support

Join Appwrite Pro and get email support from our team.

Learn more