-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to attach Auth0 access token to headers inside Apollo client #100
Comments
It appears that the Auth0 code you're using [getSession, getAccessToken] is designed to run on the server. Even if you could await it, I don't think it would work. Instead of trying to retrieve the AT in the ApolloProvider, I would grab the access token in the application root (layout.tsx) and pass it down through the props to the ApolloProvider. // layout.tsx
import { getAccessToken } from "@auth0/nextjs-auth0";
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const token = await getAccessToken();
return (
<html lang="en">
<body>
<UserProvider>
<ApolloWrapper token={token} >
{children}
</ApolloWrapper>
</UserProvider>
</body>
</html>
);
} Your ApolloProvider now has access to the AT, which you can use in your authLink. This works well until the AT expires. Since the code in the root only runs once, you never get a chance to call getAccessToken again to use your refresh token. I ended up passing the AT expiration down to the ApolloProvider as well, checking for that in authLink, and then doing a router.refresh() if needed (which executes getAccessToken). This seems ugly, and hopefully, someone has a better way to handle this. This is a stripped-down version of the ApolloProvider: "use client";
import {
ApolloLink,
HttpLink
} from "@apollo/client";
import {
ApolloNextAppProvider,
NextSSRInMemoryCache,
NextSSRApolloClient,
SSRMultipartLink
} from "@apollo/experimental-nextjs-app-support/ssr";
import { onError } from "@apollo/client/link/error";
import { RetryLink } from "@apollo/client/link/retry";
import { useRouter } from "next/navigation";
const ApolloWrapper = ({ token, children }) => {
const router = useRouter();
const httpLink = new HttpLink({
uri: process.env.API_URL
});
const authLink = new ApolloLink((operation, forward) => {
if (token?.accessToken) {
try {
const expireDate = new Date(token.expiresAt * 1000);
if (expireDate < new Date()) {
console.debug("[GraphQL debug] Access token expired, refreshing:", expireDate);
router.refresh();
}
operation.setContext(({ headers }) => ({
headers: {
authorization: `Bearer ${token?.accessToken}`,
...headers
}
}));
} catch (error) {
console.log(error);
}
}
return forward(operation);
});
const authHttpLink = ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) =>
console.error(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
}
if (networkError) {
console.error("[Network error]:", networkError);
}
}),
authLink,
new RetryLink(),
typeof window === "undefined"
? ApolloLink.from([
new SSRMultipartLink({
stripDefer: false,
cutoffDelay: 200,
}),
httpLink,
])
: httpLink
]);
const makeClient = () => (
new NextSSRApolloClient({
name: "web-ssr",
version: "1.2",
link: authHttpLink,
cache: new NextSSRInMemoryCache()
//connectToDevTools: devTools,
})
);
return (
<ApolloNextAppProvider makeClient={makeClient}>
{children}
</ApolloNextAppProvider>
);
};
export default ApolloWrapper; The full version of the app root, including the token expiration. I couldn't find a way to get this from Auth0, so I needed to parse the token to get it. import { getAccessToken, getSession } from "@auth0/nextjs-auth0";
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const session = await getSession();
let token;
if (session) {
const at = await getAccessToken();
if (at?.accessToken) {
const decoded = jwt_decode(at.accessToken) as { exp: number };
token = {
accessToken: at.accessToken,
accessTokenDecoded: decoded,
user: session.user,
expiresAt: decoded.exp
};
}
}
return (
<html lang="en">
<body>
<UserProvider>
<ApolloWrapper token={token} >
{children}
</ApolloWrapper>
</UserProvider>
</body>
</html>
);
} |
Thanks for the response @mvandergrift. Unfortunately whenever I try to make my "Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead." Any idea why this happens? Seems like I'm unable to make anything async. |
It looks like you've built your project in the /pages directory instead of the /app directory, so you're not using the AppRouter. Async components are only available within the AppRouter framework. Hopefully, it's as easy as moving some files over and renaming a couple of pages. Otherwise, Vercel has a great guide on migration to the app router: https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration |
I'm doing some housekeeping so I'm closing some older issues that haven't seen activity in a while. |
Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better. |
FYI: As far as I read from Auth0 community discussion, |
Details shown here. Would love to get some help here, been struggling with this for a while now.
https://stackoverflow.com/questions/77164538/attach-auth0-access-token-as-request-header-for-graphql-apolloclient-in-nextjs-a
The text was updated successfully, but these errors were encountered: