#Unable to update jwt token to session

1 messages · Page 1 of 1 (latest)

simple nebula
#

I need to make a API request to a downstream service from Remix, the request requires to pass JWT. If the service returns 401. we will need to use the refresh token and try to generate new JWT and make the request again.

I have tested the script below, it successfully generates a new token and made request to the downstream service, but when I want to update the session with the new JWT + refresh token, it doesn't seem to work. The subsequent requests still use the old token.

Here's my code:

export async function authoriseFetch(
  request: Request,
  url: RequestInfo,
  init?: RequestInit
): Promise<Response> {
  const session = await getSession(request.headers.get("Cookie"));
  const token = session.get("token");
  const refreshToken = session.get("refreshToken");

  if (!token) {
    throw redirect("/auth/login");
  }

  const fetchFn = (tk: string) => {
    return fetch(url, {
      ...init,
      headers: {
        ...init?.headers,
        Authorization: `Bearer ${tk}`,
      },
    });
  };

  let response = await fetchFn(token);

  // try to refresh the token if it's expired
  if (response.status === 401) {
    const { token, refresh_token } = await getRefreshToken(refreshToken);

    // try to call again with the new token
    response = await fetchFn(token);
    if (response.status >= 400) {
      throw redirect("/auth/login");
    }

    const cookieHeader = await setToken(token, refresh_token);
    return new Response(response.body, {
      ...response,
      headers: {
        ...response.headers,
        "Set-Cookie": cookieHeader,
      },
    });
  }

  return response;
}

export const setToken = async (token: string, refreshToken: string) => {
  const session = await getSession();
  session.set("token", token);
  session.set("refreshToken", refreshToken);
  return commitSession(session);
};

ANy ideas?

simple nebula
#

and here's my account.tsx page

import { useRouteLoaderData } from "@remix-run/react";
import { User } from "~/model/user";

export default function Account() {
  const user = useRouteLoaderData("root") as User;

  return (
    <div className="px-4 sm:px-6 lg:px-8">
      <div>
        <h1 className="my-4 text-base font-semibold leading-6 text-gray-900">
          My Account
        </h1>
        <div>
          Name: {user.first_name} {user.last_name}
        </div>
      </div>
      <div className="mt flow-root">
        <h2 className="my-4 text-base font-semibold leading-6 text-gray-900">
          My plan
        </h2>
      </div>
    </div>
  );
}```
#

when I add loader function and useLoaderData() it works.

pastel mulch
#

When are you calling authoriseFetch ?

simple nebula
#

I call it in all routes, for example, when you visit /account, it will call authoriseFetch() to get user profile from a service written in Golang.

Basically what i would like to implement is to automatically generate new JWT using refresh token everytime the JWT is expired, then store this in session to reuse later.

#

Does anyone know any project or sample code that handles this

simple nebula
#

I am very new to Remix so I am not sure if my implementation above is not following the Remix pattern

pastel mulch
#

return the headers object from your authoriseFetch function and in your loaders too

#

For example:

simple nebula
#

Yeah that's what I've been doing.

export async function authoriseFetch(
  request: Request,
  url: RequestInfo,
  init?: RequestInit
): Promise<Response> {
  const { token, refreshToken } = await getToken(request);

  console.log("old token", refreshToken);
  if (!token) {
    throw redirect("/auth/login");
  }

  const fetchFn = (tk: string) => {
    return fetch(url, {
      ...init,
      headers: {
        ...init?.headers,
        Authorization: `Bearer ${tk}`,
        "User-Agent": request.headers.get("User-Agent") || "",
      },
    });
  };

  let response = await fetchFn(token);

  // try to refresh the token if it's expired
  if (response.status === 401) {
    const { token, refresh_token } = await getRefreshToken(refreshToken);

    // try to call again with the new token
    response = await fetchFn(token);
    if (response.status >= 400) {
      throw redirect("/auth/login");
    }

    const cookieHeader = await setToken(token, refresh_token);
    return new Response(response.body, {
      ...response,
      headers: {
        ...response.headers,
        "Set-Cookie": cookieHeader,
      },
    });
  }

  return response;
}
pastel mulch
#

Paste one of the loader that is calling it

simple nebula
#
export async function getProfile(request: Request): Promise<Response> {
  return authoriseFetch(request, `${baseAPIURL}/auth/me`, {
    method: "GET",
  });
}```
#

export async function loader({ request }: LoaderFunctionArgs) {
  

  const response = await getProfile(request);
  return response.json();
}