#[SOLVED] email password session doesn't work in SSR

39 messages · Page 1 of 1 (latest)

bleak briar
#

So, I'm using NextJS@13 for my project and handling the login on the server side. because of that, I need to hit all the endpoints manually and set the cookies manually as well. but even after setting the cookies manually (following Almost-SSR], the session doesn't work on the user end. I can see the session being created in the appwrite console. but when I try to get the currently logged-in user, it returns null. don't know what's the problem here.
Below is the main code that handles login on the server side

    const apiPath = "/account/sessions/email";

    const { callEndpoint } = await import("@/AppwriteServices/utils");
    const response = await callEndpoint(
      "POST",
      apiPath,
      { "content-type": "application/json" },
      { email, password }
    );

    const responseData = await response.json();

    const cookieString = response.headers.get("set-cookie");

    const { setSessionCookie } = await import("@/AppwriteServices/utils");
    await setSessionCookie(cookieString);

    return NextResponse.json(
      { message: "Logged In successfully", user: responseData },
      { status: 200 }
    );

the provided codes might not be enough to debug the issue. lmk what further info you need

#

the callEndpoint function is just a helper function that calls any appwrite endpoint with necessary headers and payload. Below is the code

    const { getEnv } = await import("@/lib/utils");
    const Appwrite_Project_ID = getEnv("APPWRITE_PROJECT_ID");
    const configEndpoint = getEnv("APPWRITE_ENDPOINT");

    method = method.toUpperCase() as typeof method;
    const url = new URL(configEndpoint + endpoint);

    const cookieValue = await getCookieValue();

    const authCookies = {
      [`a_session_${Appwrite_Project_ID.toLowerCase()}`]: cookieValue,
    };

    headers = {
      "x-sdk-name": "Web",
      "x-sdk-platform": "client",
      "x-sdk-language": "web",
      "x-sdk-version": "13.0.0",
      "X-Appwrite-Response-Format": "1.4.0",
      "X-Appwrite-Project": Appwrite_Project_ID,
      "X-Fallback-Cookies": JSON.stringify(authCookies),
      ...headers,
    };

    const options: RequestInit | undefined = {
      method,
      headers,
      credentials: "include",
    };

    if (method === "GET") {
      const { flatten } = await import("@/lib/utils");

      for (const [key, value] of Object.entries(flatten(payload))) {
        url.searchParams.append(key, value);
      }
    } else {
      switch (headers["content-type"]) {
        case "application/json":
          options.body = JSON.stringify(payload);
          break;

        case "multipart/form-data":
          let formData = new FormData();

          for (const key in payload) {
            if (Array.isArray(payload[key])) {
              payload[key].forEach((value: any) => {
                formData.append(key + "[]", value);
              });
            } else {
              formData.append(key, payload[key]);
            }
          }

          options.body = formData;
          delete headers["content-type"];
          break;
      }
    }

    return await fetch(url.toString(), options);
#

the setSessionCookie function receives the cookie string returned from the api response, parses them, and sets them using nextjs cookie function. Below is the code

async function setSessionCookie(cookieString: string | null) {
  if (!cookieString) return;

  const { getEnv } = await import("@/lib/utils");
  const { cookies } = await import("next/headers");

  const APP_URI = getEnv("APP_URI");
  const APPWRITE_ENDPOINT = getEnv("APPWRITE_ENDPOINT");

  const ssrHostname = APP_URI === "localhost" ? APP_URI : "." + APP_URI;
  const appwriteHostname =
    APPWRITE_ENDPOINT === "localhost"
      ? APPWRITE_ENDPOINT
      : "." + APPWRITE_ENDPOINT;

  const cookiesStr = cookieString.split(appwriteHostname).join(ssrHostname);

  const cookiesArray = setCookies.splitCookiesString(cookiesStr);
  const cookiesParsed = cookiesArray.map((cookie: any) =>
    setCookies.parseString(cookie)
  );

  const cookieStore = cookies();
  for (const cookie of cookiesParsed) {
    cookieStore.set(cookie.name, cookie.value, {
      domain: cookie.domain,
      secure: cookie.secure,
      sameSite: cookie.sameSite as any,
      path: cookie.path,
      maxAge: cookie.maxAge,
      httpOnly: cookie.httpOnly,
      expires: cookie.expires,
    });
  }
}
#

let me know if you need anything more

#

below is the code that i'm using to get the currently logged-in user

const getCurrentUser = async <
  Preferences extends Models.Preferences
>(): Promise<Models.User<Preferences> | null> => {
  try {
    const { callEndpoint } = await import("./utils");

    const apiPath = "/account";

    const response = await callEndpoint("GET", apiPath, {
      "content-type": "application/json",
    });

    if (!response.ok) {
      return null;
    }

    const currentUser = await response.json();

    return currentUser;
  } catch (error) {
    console.log(error);

    throw new Error((<Error>error).message);
  }
};
uncut creek
#

Sincerely, If you don't need SEO in that section, I think the best way to avoid headaches is by performing auth client sided

#

Anyways wait if someone else has some ideas about this, I'm not sure how this could be solved

bleak briar
# uncut creek Sincerely, If you don't need SEO in that section, I think the best way to avoid ...

I don't need SEO in this section. but the issue is that I need to check the user's login status in the middleware to implement protected routes. and to do that, we need to implement the authentication server-side, cuz nothing client-side will work in the middleware. If you have any other approach to solve this problem while keeping all the headaches and middleware in mind, i would love to hear that

uncut creek
bleak briar
# uncut creek Anyways wait if someone else has some ideas about this, I'm not sure how this co...

I'm confused as to why this is ain't working. because I've followed the same approach in many other projects of mine. but with other login methods. it's the first time I'm using email-password method. It worked with MagicURL. even with OAuth2. everything worked perfectly. even in the "Almost-SSR" project that I mentioned in the original post. there it worked with anonymous login. i can't understand what's the issue with email-pass method

old hound
bleak briar
old hound
bleak briar
old hound
bleak briar
old hound
bleak briar
# old hound The cookie

yes it is. here's the code that set's the cookie

  const cookieStore = cookies();

  for (const cookie of cookiesParsed) {
    cookieStore.set(cookie.name, cookie.value, {
      domain: cookie.domain,
      secure: cookie.secure,
      sameSite: cookie.sameSite as any,
      path: cookie.path,
      maxAge: cookie.maxAge,
      httpOnly: cookie.httpOnly,
      expires: cookie.expires,
    });
  }
#

another thing I just noticed is that I can see all the appwrite cookies from any other route, but can't from middleware. that's weird

old hound
bleak briar
# old hound How are you extracting the cookie?

a simple example of getting all the cookies

    const { cookies } = await import("next/headers");
    const cookieStore = cookies();
    cookieStore.getAll().forEach((cookie) => {
      console.log(cookie);
    });
bleak briar
# old hound What's your middleware code?

just a simple check for auth status

export async function middleware(request: NextRequest) {
  const currentUser = await getCurrentUser();
  const path = request.nextUrl.pathname;

  /*
    If the user is not logged in
    and is trying to access an authenticated route,
    redirect to the home page
  */
  if (!currentUser && AUTH_ROUTES.includes(path)) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  /*
    If the user is logged in
    and is trying to access an unauthenticated route,
    redirect to dashboard
    */
  if (currentUser && UNAUTH_ROUTES.includes(path)) {
    return NextResponse.redirect(new URL("/dashboard", request.url));
  }
}
bleak briar
old hound
bleak briar
#

well well well. ig you were right in the text u removed some min ago. the middleware doesn't have access to the appwrite cookies. i just tried removing the domain option from the code that sets my cookies and it started working magically

old hound
bleak briar
#

tho i'm still confused, why domain was preventing it from being read by middleware, while all other routes were reading it successfully

old hound
old hound
#

and then the cookie domain should be set for your nextjs app

bleak briar
bleak briar
old hound
bleak briar