Skip to content
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

Authentication: How would I getClient with Clerk? #123

Closed
peterkuykendall opened this issue Nov 6, 2023 · 7 comments
Closed

Authentication: How would I getClient with Clerk? #123

peterkuykendall opened this issue Nov 6, 2023 · 7 comments

Comments

@peterkuykendall
Copy link

peterkuykendall commented Nov 6, 2023

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 #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
Copy link
Author

ah -- just saw this: #103

@phryneas
Copy link
Member

phryneas commented Nov 6, 2023

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
Copy link
Author

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
Copy link
Member

phryneas commented Nov 7, 2023

No, you misread me:

  • every Request has it's unique ApolloClient instance. Inside of your React component tree (in Client Components), calling any Apollo hooks and every time you call getClient (in RSC), you will always get the right instance for this request.
  • You can modify that instance however you like.
  • The following pattern is dangerous:
    const token = cookies().something;
    const { getClient } = registerApolloClient(() => {
      /** do not ever reference the global "token" here! */
    });
  • Instead, do this:
    const { getClient } = registerApolloClient(() => {
      const token = cookies().something;
    });
  • doing this is likely pointless: getClient().defaultContext.token = newToken; - because where would you get a new token when your ApolloClient only exists for the time of a single render?

@peterkuykendall
Copy link
Author

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

@shawngustaw
Copy link

@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
Copy link
Member

phryneas commented Jan 3, 2024

@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 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants