Back

Handling User Authentication State Changes with Realtime

  • 0
  • Web
  • Accounts
  • Realtime
Francisco "Klogan" Barros
17 Dec, 2023, 15:15

Note

To answer this question, the vue/nuxt parts of the code can be ignored. I am not entirely sure if this would work as it currently is implemented. I am mainly trying to get get some validation, that subscriptions/unsubscriptions I made, make some sense. I am having some issues interpreting the documentations, mostly because of lack of examples for this particular case.

Context

I have recently integrated my Nuxt3 application flows with Appwrite SDKs in order to login, logout, verify and pretty much everything else. Middleware on the browser and on the server work as expected.

Problem

  1. My code constantly pokes Appwrite APIs to check if the user is authenticated before doing something. Works to some extent, but is probably not very performance friendly
  2. My code is not reactive, in other words, my interface does not change if a user changes state on the client side until, until I refresh the page.

I figured that to solve both of this problems, I should have a global store to hold account.get data. I figured the ideal way of doing this without "pushing" and "popping" state everywhere, is to use real time features to listen to state changes from the Appwrite Realtime SDK (inspired on the existing nuxt/supabase library approach - https://github.com/nuxt-modules/supabase/blob/8c035ac7c358934ef7c72fd4eb5b779da3c2f1ff/src/runtime/plugins/supabase.client.ts#L21).

Code snippet available below, in this thread.

TL;DR
The user is seeking validation and guidance on handling user authentication state changes using Realtime features with the Appwrite SDK. They want their interface to update in real-time when a user changes their state, such as logging in or logging out. The user has implemented a solution that handles subscribing and unsubscribing to the 'account' channel and triggers corresponding functions when the session is created or deleted. The user wants to validate if their current implementation makes sense and if it is the correct approach. A code snippet is provided in the thread for further reference. Solution: The user's current implementation for subscribing and unsubscribing to the 'account' channel
Francisco "Klogan" Barros
17 Dec, 2023, 15:15
TypeScript
import type { Models, RealtimeResponseEvent } from 'appwrite'

import { useAppwrite } from './useAppwrite'

type User = Models.User<Models.Preferences>
type UseAppwriteAuthStateChangedOptions = {
  onAuthenticated: (payload: User) => void
  onUnauthenticated: (payload: User) => void
  onSubscribed?: () => void
  onUnsubscribed?: () => void
}

const ACCOUNT_CHANNEL = 'account'

function isCreateSession(events: string[], _payload: unknown): _payload is User {
  return events.includes('users.*.sessions.*.create')
}

function isDeleteSession(events: string[], _payload: unknown): _payload is User {
  return events.includes('users.*.sessions.*.delete')
}

export function useAppwriteAuthStateChanged({
  onAuthenticated,
  onSubscribed,
  onUnauthenticated,
  onUnsubscribed,
}: UseAppwriteAuthStateChangedOptions) {
  const appwrite = useAppwrite()
  const unsubscribeCb = ref<() => void>(() => undefined)

  function handleUpdate({ events, payload }: RealtimeResponseEvent<unknown>) {
    if (isCreateSession(events, payload)) {
      onAuthenticated(payload)
      resubscribe()
    } else if (isDeleteSession(events, payload)) {
      onUnauthenticated(payload)
      resubscribe()
    }
  }

  function subscribe() {
    unsubscribeCb.value = appwrite.client.subscribe(ACCOUNT_CHANNEL, handleUpdate)
    onSubscribed?.()
  }

  function resubscribe() {
    unsubscribe()
    subscribe()
  }

  function unsubscribe() {
    unsubscribeCb.value()
    onUnsubscribed?.()
  }

  onMounted(() => subscribe())
  onUnmounted(() => unsubscribe())

  return {
    unsubscribe,
  }
}
Francisco "Klogan" Barros
17 Dec, 2023, 15:15

@D5

D5
17 Dec, 2023, 15:30

Okay, so from what I understand, the main "issue" is that if the user changes something on his account, you want it to to be changed in real time too and the UI updated?

Francisco "Klogan" Barros
17 Dec, 2023, 15:31

Yes, in a way.

That would be the point. For instance, eventually, I want to track verified state, among other possible properties.

For now, I was simply trying to track user went from anonymous to authenticated, e.g.: user logged in, and from authenticated to anonymous, e.g.: logged out

D5
17 Dec, 2023, 15:34

If that's not needed for a specific propose, it's better not using realtime feature from an efficiency point. From what I understand, in this case is a must, so it will be somewhat this way:

TypeScript
client.subscribe('account', response => {
    // Here you should put the events to change UI related things

    console.log(response);
});```
D5
17 Dec, 2023, 15:35

The response will contain account data with new parameters

D5
17 Dec, 2023, 15:36

If you just want to track a specific property, for example, only listen for changes in verified state, unfortunately it's not possible at this moment (you need to listen to all account changes).

Francisco "Klogan" Barros
17 Dec, 2023, 15:46

Alright, so at least the subscription part looks good. However, the docs says:

TypeScript
Realtime authenticates using an existing user session. If you authenticate after creating a subscription, the subscription will not receive updates for the newly authenticated user. You will need to re-create the subscription to work with the new user.

So I assumed, I also need unsubscribe and subscribe again if the user logs in, and logouts, am I right? If so, I will take this code for a spin later tonight 🚀

D5
17 Dec, 2023, 15:52

From what I understand, the connection will throw an error when the user logs out (and the session gets deleted)

D5
17 Dec, 2023, 15:52

I would unsubscribe prior to logout, logout and once logged in, subscribe again

Drake
17 Dec, 2023, 19:27

Storing the user in the global state is the right approach. I wouldn't use realtime, though because that seems complicated/overkill.

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