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

"No current user" error when using custom tokenProvider with Next.JS server components #470

Open
3 tasks done
HieronymusLex opened this issue Dec 12, 2023 · 5 comments
Open
3 tasks done
Labels
feature-request New feature or request GraphQL

Comments

@HieronymusLex
Copy link

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

GraphQL API

Amplify Categories

api

Environment information

# Put output below this line
Binaries:
    Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node
    npm: 10.2.3 - ~/.nvm/versions/node/v18.19.0/bin/npm

npmPackages:
aws-amplify: ^6.0.6 => 6.0.6 
next: 14.0.4 => 14.0.4 
next-auth: ^5.0.0-beta.4 => 5.0.0-beta.4 

Describe the bug

I have an AppSync API configured to use OIDC based auth. My project does not use the Auth module or Cognito

Per the Custom Token Providers section in the docs, I have configured a tokenProvider which obtains the tokens from the next-auth package (which in turn is using my 3rd auth provider). I now want to use these to authenticate requests to AppSync.

The tokenProvider does not work with server rendered components which call the graphql API using cookiesClient.graphql where the cookiesClient is obtained from generateServerClientUsingCookies - instead a No current user error is thrown (caused by this code).

From debugging, I can see that when InternalGraphQLAPIClass._headerBasedAuth calls amplify.Auth.fetchAuthSession it calls getTokens and the authOptions on the AuthClass singleton are null at this point. Important this issue only occurs in server rendered components. The token provider works as expected in client rendered components.

I'm unsure but my current thought is that this is occurring because the library options are not being passed through correctly in the adapter-nextjs code - the code here indicates that only the resourcesConfig is being considered, not the library options. In my scenario resourcesConfig.Auth is undefined, but it even so on L41 it is always setting the token provider to the cognito user pool token provider anyways

Expected behavior

The custom token provider should be used when making graphql calls from both server and client components

Reproduction steps

  1. Configure a NextJS app with next-auth and a 3rd party provider for OIDC tokens. The NextAuth sample repo is a good starting point
  2. Configure a custom token provider for next auth
  3. Init an amplify project, add an API and select OIDC as the auth method (Todo example is fine)
  4. Make a graphql call from a server rendered component as per the docs

Code Snippet

// Put your logs below this line
// pass this to Amplify.configure - see manual configuration
export const tokenProvider = {
  async getTokens({ forceRefresh } = {}) {
    const session = await auth() // implementation: https://github.com/nextauthjs/next-auth-example/blob/main/auth.ts
    // error handling removed for brevity
    return {
        accessToken: decodeJWT(session?.user.access_token),
        idToken: decodeJWT(session?.user.id_token),
      }
  },
}

// in the server component
const cookiesClient = generateServerClientUsingCookies({
    config,
    cookies,
});
const {data, errors} = await cookiesClient.graphql({
    query: queries.listTodos,
});

Log output

aws-exports.js

No response

Manual configuration

Amplify.configure(config, {
  ssr: true,
  Auth: {
    tokenProvider
  }
})

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

@HuiSF
Copy link
Member

HuiSF commented Dec 12, 2023

Hi @HieronymusLex thanks for reaching out.

The singleton method Amplify.configure() does not take any effect on the server side usage. The custom token provider should be configured with the createServerRunner function exported from the @aws-amplify/adapter-nextjs, we haven't not yet, however, roll out the support. We will work on this.

In the meantime, you may consider to directly use the lower level SSR adapter function runWithAmplifyServerContext exported from the aws-amplify/adapter-core, where you can full freedom to passing custom token provider. Usage example see here.

I will mark this issue as a feature request for now.

@nadetastic nadetastic added feature-request New feature or request and removed pending-triage labels Dec 12, 2023
@HieronymusLex
Copy link
Author

@HuiSF Thanks for the update and workaround! I think it would be worth calling some of these features out in the docs and adding some an example for Next.JS regarding the custom token provider section.

Regarding using the runWithAmplifyServerContext from the adapter-core, do you have an example of how to make a graphql call using this? My code is as follows:

const results = await runWithAmplifyServerContextCore(
    config,
    {
      Auth: {
        tokenProvider
      }
    },
    async (...args) => {
      const cookiesClient = generateServerClientUsingCookies({
        config,
        cookies,
        authMode: "oidc",
      });
      const {data, errors} = await cookiesClient.graphql({
        query: queries.listTodos,
      });
      return data
    });

I'm assuming my error is getting the client from generateServerClientUsingCookiesbut I'm not sure how else to get the client?

@HieronymusLex
Copy link
Author

just following up here with the following code which is working:

const reqResBasedClient = generateServerClientUsingReqRes({
  config
});

const results = await runWithAmplifyServerContextCore(
  config,
  {
    Auth: {
      tokenProvider
    }
  },
  async (contextSpec) => {
    const {data, errors} = await reqResBasedClient.graphql(contextSpec, {
      query: queries.listTodos,
    });
    return data
  }
);

@harryy2510
Copy link

harryy2510 commented Jan 4, 2024

I've been struggling with the exact same issue and exact same codebase 😄

export const {
  handlers: { GET, POST },
  auth,
  signOut,
  signIn,
} = NextAuth({
  secret: config.auth.authSecret,
  providers: [provider],
  pages: {
    signIn: '/api/auth/sign-in',
  },
  callbacks: {
    async jwt({ token, account }) {
      if (account) {
        return produce(token, (draft) => {
          draft.accessToken = account.access_token;
          draft.idToken = account.id_token;
          draft.refreshToken = account.refresh_token;
        });
      }
      return token;
    },
    async session({ session, token }) {
      return produce(session, (draft) => {
        draft.accessToken = token.accessToken as string;
        draft.idToken = token.idToken as string;
        draft.refreshToken = token.refreshToken as string;
        draft.user.id = token.sub!;
      });
    },
  },
});
const nextAuthTokenProvider: TokenProvider = {
  async getTokens() {
    const session = await auth();
    if (!session) {
      return null;
    }
    const accessTokenString = session.accessToken;
    const idTokenString = session.idToken;
    return {
      accessToken: decodeJWT(accessTokenString),
      idToken: decodeJWT(idTokenString),
    };
  },
};

export async function configureAmplify() {
  Amplify.configure(amplifyConfig, {
    ssr: true,
    Auth: {
      tokenProvider: nextAuthTokenProvider,
    },
  });
}

@HieronymusLex Your solution looks great for the SSR. Do you also happen to have something for the client side working? I mean I'm also trying to generate a client for client side with the same session details from next-auth.
Would appreciate your help here!

@harryy2510
Copy link

ooo. I created a server action, and and called from client 😍 worked great for me. Thanks for the direction here. Your code snippet helped me get into the right direction, thanks again.

@stocaaro stocaaro transferred this issue from aws-amplify/amplify-js Jan 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request New feature or request GraphQL
Projects
None yet
Development

No branches or pull requests

5 participants