Back

Understanding how authentication works when retrieving an user instance.

  • 0
  • Auth
  • Web
  • Cloud
Katriel
29 Aug, 2024, 23:41

So, here is the problem, I want to get an user based on their credentials, the process a initially followed:

  1. Pass user email/password from a html form
  2. In server side, using node-appwrite with an api_key, make session token out .secret from 1, save it in a cookie
  3. I don't think I can use this cookie on my server instance, right? I get this: 'API key and session used in the same request. Use either setSession or setKey. Learn about which authentication method to use in the SSR docs: https://appwrite.io/docs/products/auth/server-side-rendering'

Is the correct approach just create another new Client() instance from Appwrite, just to grab this user information? And effectively checking permissions and such just when I'm making requests on DB, by always using a new Client() with an API key?

My objective with this is that: any data that is being saved on a Appwrite has to be sanitized in server side, for example, lets say that I want to save an username for a game character, it can't have any special symbols, only letter and numbers, without the server side validation, people can pass anything by just grabbing their .secret cookie and making direct requests for Appwrite.

PS I'm aware that the allowed characters in Appwrite are: alphanumeric, hyphen, non-leading underscore, period, but it's just to get an idea.

TL;DR
The developer is looking for clarification on how authentication works when retrieving a user instance. They're discussing creating new client instances, setting cookies, and handling authentication in SSR. The solution involves creating multiple Client() instances and using session cookies for authentication. Solution: - Create a new Client() instance with session token from the cookie to access user information. - Follow the server-side validation process to sanitize data before saving it in Appwrite.
Katriel
29 Aug, 2024, 23:41

Here is some code:

TypeScript
// server instance, has an api key and full access, but can't use a session to grabe an user.

import { Account, Client, Databases } from 'node-appwrite';
import { API_KEY } from '$env/static/private';
import { PUBLIC_ENDPOINT, PUBLIC_PROJECT } from '$env/static/public';

const serverSession = new Client();

serverSession
    .setEndpoint(PUBLIC_ENDPOINT)
    .setProject(PUBLIC_PROJECT)
    .setKey(API_KEY);

const account = new Account(serverSession);
const databases = new Databases(serverSession);

export { account, databases, serverSession }
TypeScript
// part of my login code, email and passwords comes from the frontend.
import { account } from '$lib/server/appwrite';

// ... more verification code ...

const { email, password } = result.data;
const res = await account.createEmailPasswordSession(email, password);

cookies.set('session', res.secret, {
    path: '/',
    httpOnly: true,
    sameSite: 'strict'
});
TypeScript
// a hook, this run at each request, I can't grab the user here
const auth: Handle = async ({ event, resolve }) => {
    const session = event.cookies.get('session');

    if (!session) {
        if (event.url.pathname !== '/login') throw redirect(303, '/login');

        return await resolve(event);
    }

    try {
        serverSession.setSession(session);
        const user = await account.get();

        console.log(user);
        event.locals.user = user;
    } catch (error) {
        event.cookies.delete('session', { path: '/' });
        if (event.url.pathname !== '/login') throw redirect(303, '/login');
    }

    return await resolve(event);
};
faye
29 Aug, 2024, 23:46

What framework are you using for your website?

faye
29 Aug, 2024, 23:46

it seems like you are using next.js?

Katriel
29 Aug, 2024, 23:46

Sveltekit

faye
29 Aug, 2024, 23:46

ahh

faye
29 Aug, 2024, 23:48

So, what you should do is the following:

  • Login -> create cookie
  • With cookie -> get value from cookie on the server, which should only have the value, use .setSession(tokenHere) as your client
faye
29 Aug, 2024, 23:48

I see you are using .setKey

faye
29 Aug, 2024, 23:49

For example in my project (next.js, but same principle):

TypeScript
const headersList = headers()
  const cookieHeader = headersList.get('cookie')
  const cookies = cookieHeader ? cookieHeader.split('; ') : []
  const sessionCookie = cookies.find((cookie) =>
    cookie.startsWith(
      `a_session_${process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID}`
    )
  )
  const client = new Client()
    .setEndpoint(`${process.env.NEXT_PUBLIC_API_URL}/v1`)
    .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)

  if (sessionCookie) {
    client.setSession(sessionCookie.split('=')[1])
  }
Katriel
29 Aug, 2024, 23:51

what you are saying is, from what I said:

Is the correct approach just create another new Client()... etc, etc, etc

is this correct?

Katriel
29 Aug, 2024, 23:51

you are saying i shoudl create a new client instance just to grab this data

faye
29 Aug, 2024, 23:51

You should grab the cookie and then set a client instance

faye
29 Aug, 2024, 23:53

The thing about SSR, is nothing is saved client side.

So what I do:

TypeScript
export async function createSessionServerClient() {
  const headersList = headers()
  const cookieHeader = headersList.get('cookie')
  const cookies = cookieHeader ? cookieHeader.split('; ') : []
  const sessionCookie = cookies.find((cookie) =>
    cookie.startsWith(
      `a_session_${process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID}`
    )
  )
  const client = new Client()
    .setEndpoint(`${process.env.NEXT_PUBLIC_API_URL}/v1`)
    .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)

  if (sessionCookie) {
    client.setSession(sessionCookie.split('=')[1])
  }

  return {
    get account() {
      return new Account(client)
    },
    get teams() {
      return new Teams(client)
    },
    get databases() {
      return new Databases(client)
    },
    get storage() {
      return new Storage(client)
    },
    get functions() {
      return new Functions(client)
    },
    get messaging() {
      return new Messaging(client)
    },
    get locale() {
      return new Locale(client)
    },
    get avatars() {
      return new Avatars(client)
    },
  }
}

And then on every page, let's say you want to use database: const { databases } = await createSessionServerClient()

faye
29 Aug, 2024, 23:54

So for each request you are making with "databases", you make sure to set the client, which the token you basically get from your cookie

Katriel
29 Aug, 2024, 23:55

huhum...

Katriel
29 Aug, 2024, 23:55

so, i should have something like, sessionClient and adminClient, each a different new Client() instance, being adminClient one that has setKey

faye
29 Aug, 2024, 23:55

Yes

Katriel
29 Aug, 2024, 23:55

i see

faye
29 Aug, 2024, 23:56

This is how my entire appwrite-session.ts file looks like:

https://paste.techscode.com/adonasidinepuxe.ts

faye
29 Aug, 2024, 23:57

createSessionClient is the same as createSessionServerClient, but it uses request as a parameter for the /api routes, which is basically CSR -> SSR -> SSR, but that's kind of a special case..

faye
29 Aug, 2024, 23:57

but you can see the createAdminSession and createSessionServerClient

Katriel
29 Aug, 2024, 23:58

i see, this makes sense, I had some misconceptions, but this cleared it

Katriel
29 Aug, 2024, 23:58

thanks a lot

faye
30 Aug, 2024, 00:00

Great to hear!

If this solved your issue, please add [SOLVED] at the beginning of the title.

Happy appwriting! :appwriterocket:

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