apollographql / apollographql/apollo-client-integrations

Authentication & Token Rotation Question Using Apollo Client for Next.js with Auth.js

Open
#481 7 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
556
Forks
53
PR merge metrics
No merged PRs in 30d

Description

Hi! I have a question regarding using this library for Apollo Client support for Next.js App Router, along with Auth.js with JWT sessions for authentication.

I use both the RSC and SSR use cases from your documentation and generally am able to get it work with my authentication. However, I'm unable to get it synced up with my refresh token logic to get a new access token when its close to expiry unless I refresh the page entirely. So I keep hitting expiration errors because the token doesn't refresh until you do a hard refresh. Do you have any thought on how to solve this problem so that I can check the expiry on every graphql query/mutation request? Thank you!

ApolloClient.ts
```
import { auth } from "@/auth";
import { HttpLink } from "@apollo/client";
import {
ApolloClient,
InMemoryCache,
registerApolloClient,
} from "@apollo/client-integration-nextjs";

export const { getClient, query, PreloadQuery } = registerApolloClient(async () => {
const session = await auth();

return new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: process.env.API_HOST,
// you can disable result caching here if you want to
// (this does not work if you are rendering your page with `export const dynamic = "force-static"`)
// fetchOptions: { cache: "no-store" },
headers: session?.user?.access ? {Authorization: `JWT ${session.user.access}`} : undefined,
}),
});
});
```

ApolloWrapper.tsx
```
"use client";

import { HttpLink } from "@apollo/client";
import {
ApolloClient,
ApolloNextAppProvider,
InMemoryCache,
} from "@apollo/client-integration-nextjs";

// have a function to create a client for you
function makeClient(token: string, host: string) {
const httpLink = new HttpLink({
// this needs to be an absolute url, as relative urls cannot be used in SSR
uri: host,
// you can disable result caching here if you want to
// (this does not work if you are rendering your page with `export const dynamic = "force-static"`)
// fetchOptions: { cache: 'no-store' },
headers: token ? {Authorization: `JWT ${token}`} : undefined,
});

return new ApolloClient({
cache: new InMemoryCache(),
link: httpLink,
});
}

interface ApolloWrapperProps {
token: string;
host: string;
}

// you need to create a component to wrap your app in
export function ApolloWrapper({ children, token, host }: React.PropsWithChildren) {
return (
makeClient(token, host)}>
{children}

);
}
```

jwt callback in auth.ts
```
jwt: async ({ token, user }) => {
if (user) {
token.id = user.id;
token.access = user.access;
token.refresh = user.refresh;
} else if (token.access) {
// if refresh token is expired, return an error so we "sign out"
const decodedRefresh = jwtDecode(token.refresh as string, {header: false});
if (decodedRefresh && decodedRefresh.exp && Date.now() / 1000 >= decodedRefresh.exp) {
token.error = "RefreshAccessTokenError";
return { ...token, error: "RefreshAccessTokenError" };
}

// if access token is expired, refresh it
const decodedAccess = jwtDecode(token.access as string, {header: false});
if (!decodedAccess || (decodedAccess.exp && Date.now() / 1000 >= decodedAccess.exp) || (decodedAccess.exp && Date.now() / 1000 + REFRESH_TOKEN_LEGROOM >= decodedAccess.exp)) {
const result = await refreshAccessToken(token.refresh as string);
if (result) {
token.access = result;
}
}
}
return token;
},
```

layout.tsx
```
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const session = await auth();
let firstName;
let lastName;
if (session && !session.error) {
const { data } = await query({ query: CURRENT_USER });
firstName = data.currentUser.firstName;
lastName = data.currentUser.lastName;
}

if (session && !session.error) {
return (

{children}

);
} else {
return (


{children}


);
}

}
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Trace the server and client setup in ApolloClient.ts, ApolloWrapper.tsx, auth.ts, and layout.tsx, focusing on how the JWT callback and Apollo clients obtain tokens. There are no named tests or entry points; progress would require deciding and documenting the supported per-request refresh behavior for the RSC and SSR cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, nextjs, typescript
Domain
api, authentication
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.