apollographql / apollo-client-nextjs

Apollo Client support for the Next.js App Router
https://www.npmjs.com/package/@apollo/experimental-nextjs-app-support
MIT License
358 stars 25 forks source link

Authentication: How would I getClient with Clerk? #123

Closed peterkuykendall closed 6 months ago

peterkuykendall commented 6 months ago

I'm only interested in the next js app folder approach at this point.

With Clerk for next.js app, I can get a token from a page, but I can't see how to get it from the environment like you do with the Next Auth approach https://github.com/apollographql/apollo-client-nextjs/issues/44

So I'd think I'd need to pass the token in with the getClient/registerApolloClient call in the examples, export const { getClient } = registerApolloClient(() => {

but I don't see how to do that because the registerApolloClient doesn't take args.

Could you share how to get the client for an Clerk token?

I'm also curious -- where you've said in other posts that registerApolloClient is a singleton, is that also the case on the server only app folder approach? And if so, how does that work in a multi-user situation where each user has different creds?

peterkuykendall commented 6 months ago

ah -- just saw this: https://github.com/apollographql/apollo-client-nextjs/issues/103

phryneas commented 6 months ago

registerApolloClient is a singleton

Yes, but per request. Your registerApolloClient makeClient function will be executed once per request, so you can also just call something like cookies in there - you just never ever ever should access the cookies outside of makeClient and pass them in via scope or parameters, since then they'll be shared across all requests.

peterkuykendall commented 6 months ago

Great, thanks for explaining. I'd appreciate some help here then.

To be totally clear: If you're saying that I shouldn't pass in the cookie outside of makeClient, am I correct in reading that I should also not be setting the cookie with the new: client.defaultContext.token = newToken; in the calling page because there's a possibility that between the setting and the makeClient, it could be set or over-written by another unrelated request?

If that's the case, then I'm also not clear how to get the jwt from inside of makeClient because I don't see how I can have access to any page, context or cookies there.

phryneas commented 6 months ago

No, you misread me:

peterkuykendall commented 6 months ago

wonderful. Thanks for clarifying how it should be organized. I'm in good shape now.

shawngustaw commented 4 months ago

@phryneas

Wondering if this looks fine to you (seems like it works for me), specifically for RSC. Thanks in advance!

import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { registerApolloClient } from '@apollo/experimental-nextjs-app-support/rsc';
import { fetchAuthSession } from 'aws-amplify/auth/server';
import { cookies } from 'next/headers';

import { env } from '~/_env';
import { runWithAmplifyServerContext } from '~/authentication/utils/serverSideAuth';

const authTokenLink = setContext(async () => {
  // Could be any async way of fetching the token, but this is with Amplify
  const session = await runWithAmplifyServerContext({
    nextServerContext: { cookies },
    operation: (contextSpec) => fetchAuthSession(contextSpec),
  });

  if (session.tokens?.idToken) {
    return {
      headers: {
        Authorization: session.tokens?.idToken?.toString(),
      },
    };
  }

  return {
    headers: {
      'X-Api-Key': env.NEXT_PUBLIC_AWS_APPSYNC_API_KEY,
    },
  };
});

export const { getClient } = registerApolloClient(() => {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: authTokenLink.concat(
      new HttpLink({
        uri: env.NEXT_PUBLIC_AWS_APPSYNC_GRAPHQL_ENDPOINT,
      }),
    ),
  });
});
phryneas commented 4 months ago

@shawngustaw that looks good to me, but I have to admit that I am not familiar with your specific way of fetching the token. Glad to hear it works 😊