amplify-js: Next.js and AWS Amplify - `custom_header` defined in Amplify.configure() doesn't work with SSR Calls (REST, not GraphQL)

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

Authentication, REST API

Amplify Categories

auth, api

Environment information

  System:
    OS: macOS 12.5
    CPU: (10) arm64 Apple M1 Max
    Memory: 7.33 GB / 64.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.16.0 - ~/.volta/tools/image/node/16.16.0/bin/node
    Yarn: 1.22.19 - ~/.volta/bin/yarn
    npm: 8.11.0 - ~/.volta/tools/image/node/16.16.0/bin/npm
  Browsers:
    Chrome: 104.0.5112.101
    Safari: 15.6
  npmPackages:
    @ampproject/toolbox-optimizer:  undefined ()
    @aws-amplify/ui-react: ^3.4.1 => 3.4.1
    @aws-amplify/ui-react-internal:  undefined ()
    @aws-amplify/ui-react-legacy:  undefined ()
    @babel/core:  undefined ()
    @babel/runtime:  7.15.4
    @edge-runtime/primitives:  1.1.0-beta.26
    @hapi/accept:  undefined ()
    @headlessui/react: ^1.6.6 => 1.6.6
    @heroicons/react: ^1.0.6 => 1.0.6
    @napi-rs/triples:  undefined ()
    @next/react-dev-overlay:  undefined ()
    @segment/ajv-human-errors:  undefined ()
    @tailwindcss/forms: ^0.5.2 => 0.5.2
    @vercel/nft:  undefined ()
    acorn:  undefined ()
    amphtml-validator:  undefined ()
    arg:  undefined ()
    assert:  undefined ()
    async-retry:  undefined ()
    async-sema:  undefined ()
    autoprefixer: ^10.4.8 => 10.4.8
    aws-amplify: ^4.3.33 => 4.3.33
    babel-packages:  undefined ()
    babel-plugin-inline-react-svg: ^2.0.1 => 2.0.1
    browserify-zlib:  undefined ()
    browserslist:  undefined ()
    buffer:  undefined ()
    bytes:  undefined ()
    chalk:  undefined ()
    ci-info:  undefined ()
    cli-select:  undefined ()
    comment-json:  undefined ()
    compression:  undefined ()
    conf:  undefined ()
    constants-browserify:  undefined ()
    content-disposition:  undefined ()
    content-type:  undefined ()
    cookie:  undefined ()
    cross-spawn:  undefined ()
    crypto-browserify:  undefined ()
    cssnano-simple:  undefined ()
    debug:  undefined ()
    devalue:  undefined ()
    domain-browser:  undefined ()
    edge-runtime:  undefined ()
    eslint: 8.22.0 => 8.22.0
    eslint-config-next: 12.2.5 => 12.2.5
    events:  undefined ()
    find-cache-dir:  undefined ()
    find-up:  undefined ()
    fresh:  undefined ()
    get-orientation:  undefined ()
    glob:  undefined ()
    gzip-size:  undefined ()
    http-proxy:  undefined ()
    https-browserify:  undefined ()
    icss-utils:  undefined ()
    ignore-loader:  undefined ()
    image-size:  undefined ()
    is-animated:  undefined ()
    is-docker:  undefined ()
    is-wsl:  undefined ()
    jest-worker:  undefined ()
    json5:  undefined ()
    jsonwebtoken:  undefined ()
    loader-utils:  undefined ()
    lodash.curry:  undefined ()
    lru-cache:  undefined ()
    micromatch:  undefined ()
    mini-css-extract-plugin:  undefined ()
    nanoid:  undefined ()
    native-url:  undefined ()
    neo-async:  undefined ()
    next: 12.2.5 => 12.2.5
    node-fetch:  undefined ()
    node-html-parser:  undefined ()
    ora:  undefined ()
    os-browserify:  undefined ()
    p-limit:  undefined ()
    path-browserify:  undefined ()
    postcss: ^8.4.16 => 8.4.16 (8.4.14)
    postcss-flexbugs-fixes:  undefined ()
    postcss-modules-extract-imports:  undefined ()
    postcss-modules-local-by-default:  undefined ()
    postcss-modules-scope:  undefined ()
    postcss-modules-values:  undefined ()
    postcss-preset-env:  undefined ()
    postcss-safe-parser:  undefined ()
    postcss-scss:  undefined ()
    postcss-value-parser:  undefined ()
    process:  undefined ()
    punycode:  undefined ()
    querystring-es3:  undefined ()
    raw-body:  undefined ()
    react: 18.2.0 => 18.2.0 (18.0.0)
    react-dom: 18.2.0 => 18.2.0
    react-is:  17.0.2
    react-refresh:  0.12.0
    react-server-dom-webpack:  undefined ()
    regenerator-runtime:  0.13.4
    sass-loader:  undefined ()
    schema-utils:  undefined ()
    semver:  undefined ()
    send:  undefined ()
    setimmediate:  undefined ()
    source-map:  undefined ()
    stream-browserify:  undefined ()
    stream-http:  undefined ()
    string-hash:  undefined ()
    string_decoder:  undefined ()
    strip-ansi:  undefined ()
    tailwindcss: ^3.1.8 => 3.1.8
    tar:  undefined ()
    terser:  undefined ()
    text-table:  undefined ()
    timers-browserify:  undefined ()
    tty-browserify:  undefined ()
    ua-parser-js:  undefined ()
    unistore:  undefined ()
    util:  undefined ()
    vm-browserify:  undefined ()
    watchpack:  undefined ()
    web-vitals:  undefined ()
    webpack:  undefined ()
    webpack-sources:  undefined ()
    ws:  undefined ()
  npmGlobalPackages:
    corepack: 0.10.0
    npm: 8.11.0

Describe the bug

I’m trying to use getServerSideProps + SSR.API.get (REST, not GraphQL) and I’m getting “No current user”. If I move the code to useEffect, it works fine.

export async function getServerSideProps({req}) {
  const { Auth, API } = withSSRContext({req});
 
  try {
    const apiName = "API";
    const path = `/user/self`;

    // throws No current user
    user = await API.get(apiName, path);

    return { props: { user } }    
  } catch (e) {
    console.error(e);
    throw e
  }
}

The API Authorization is configured (it works with useEffect), and I also have SSR enabled ssr: true.

  // configuration
  API: {
    endpoints: [
      {
       /* ... */
        custom_header: async () => {
          return {
            Authorization: `Bearer ${(await Auth.currentSession())
              .getIdToken()
              .getJwtToken()}`,
          };
        },
      },
    ],
  },
  ssr: true,

I tried to re-set the Authorization, although SSR.Auth.currentSession() works fine. I could get a token, but I’m still getting “No current user”.

export const getServerSideProps = async ({ req }) => {
  const SSR = withSSRContext({ req });

    const options = {
      headers: {
        Authorization: `Bearer ${(await SSR.Auth.currentSession())
          .getIdToken()
          .getJwtToken()}`,
      },
    };

    const user = await SSR.API.get("API", "users/self", options); // throws No current user

Could it be my version of next: 12.2.5?

I found a similar issue on StackOverflow but no answers in there.

Expected behavior

Be able to make authorized requests from getServerSideProps.

Reproduction steps

  1. Use REST API instead of GraphQL
  2. Try to make an authorized call from getServerSideProps

Code Snippet

// Put your code below this line.

Log output

// Put your logs below this line


aws-exports.js

No response

Manual configuration

No response

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

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 34 (16 by maintainers)

Most upvoted comments

@phstc I was finally able to get passed the "No current user" error.

It seems like the custom header in your amplify configure might be causing the issue. I did quite a bit of digging and logging and noticed that the error was actually being thrown from AuthClass and the more specific error was "Failed to get user from user pool"

I was able to get rid of the error by removing the entire custom_header section from Amplify.configure.

Personally, I am now getting 401/403 errors but I believe that is a different issue from how I set up this API.

Please let me know if this solves the issue for you as well

EDIT: Another note, it works successfully when I manually configure the project with the resources that I made through the CLI, so my 401/403 errors were definitely from some bad resource setup on my end.

My final configuration was as follows:

Amplify.configure({
  Auth: {
    region: "XX-XXXX-X",
    userPoolId: "XX-XXXX-X_abcd1234",
    userPoolWebClientId: "a1b2c3d4e5f6g7h8i9j0k1l2m3",
    identityPoolId: "XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab",
  },
  API: {
    endpoints: [
      {
        name: "apiName",
        endpoint: "https://12a3bcdefg.execute-api.us-east-1.amazonaws.com/dev/",
        region: "XX-XXXX-X"
      },
    ],
  },
  ssr: true,
});

This in turn allowed me to use API.get as expected in getServerSideProps:

export async function getServerSideProps({ req }) {
  const apiName = "apiName";
  const path = "items";

  const SSR = withSSRContext({ req });
  const response = await SSR.API.get(apiName, path);

  return {
    props: {
      response,
    },
  };
}

Removing the custom_header worked for mee ❤️

@nadetastic thank you! Just confirming, there are two issues:

  1. SSR does not work with custom_header
  2. External links don’t not work with SSR (there’s still another issue)

Hi, i’m facing same problem as @phstc and only solution found until now it’s:

in amplify.config.ts use a try catch so i can keep setting for all requests (SSR and client)

API: {
    endpoints: [
      {
        name: 'BE-API',
        endpoint: process.env.NEXT_PUBLIC_APIGATEWAY_URL,
        region: process.env.NEXT_PUBLIC_REGION,
        custom_header: async () => {
          try {
            const session = await Auth.currentSession();
            const token = session.getIdToken();
            const jwt = token.getJwtToken();

            return {
              Authorization: `Bearer ${jwt}`,
            };
          } catch (error) {
            return {};
          }
        },
      },
    ],
  },
  ssr: true,

for every fetch in getServerSideProps it must build headers as in config

...
const { Auth, API } = withSSRContext(context);
const session = await Auth.currentSession();
const token = session.getIdToken();
const jwt = token.getJwtToken();
const headers = { Authorization: `Bearer ${jwt}` };
result = await API.get('BE-API', '/endpoint', { headers });
return {props: result}

for client request (usually mutation as post, put) API works as expected

  const submit = useCallback(
    async (data) => {
      try {
          await API.post('BE-API', '/endopoint', { body: user });
      } catch (err: any) {
        // show error
      }
    },
    [],
  );

The error returned in SSR is “no current user” (as i recall). Cognito seems to be configured correctly.

Sorry for the confusion, I updated my comment.

@phstc I was finally able to get passed the "No current user" error.

It seems like the custom header in your amplify configure might be causing the issue. I did quite a bit of digging and logging and noticed that the error was actually being thrown from AuthClass and the more specific error was "Failed to get user from user pool"

I was able to get rid of the error by removing the entire custom_header section from Amplify.configure.

Personally, I am now getting 401/403 errors but I believe that is a different issue from how I set up this API.

Please let me know if this solves the issue for you as well

EDIT: Another note, it works successfully when I manually configure the project with the resources that I made through the CLI, so my 401/403 errors were definitely from some bad resource setup on my end.

My final configuration was as follows:

Amplify.configure({
  Auth: {
    region: "XX-XXXX-X",
    userPoolId: "XX-XXXX-X_abcd1234",
    userPoolWebClientId: "a1b2c3d4e5f6g7h8i9j0k1l2m3",
    identityPoolId: "XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab",
  },
  API: {
    endpoints: [
      {
        name: "apiName",
        endpoint: "https://12a3bcdefg.execute-api.us-east-1.amazonaws.com/dev/",
        region: "XX-XXXX-X"
      },
    ],
  },
  ssr: true,
});

This in turn allowed me to use API.get as expected in getServerSideProps:

export async function getServerSideProps({ req }) {
  const apiName = "apiName";
  const path = "items";

  const SSR = withSSRContext({ req });
  const response = await SSR.API.get(apiName, path);

  return {
    props: {
      response,
    },
  };
}

@phstc after some discussions with the team on this, I’m marking this as a bug. Thank you for all your time and feedback on this - truly appreciated. Let me know if you have any other questions

@phstc I was able to get this working as seen on my fork of your repo - https://github.com/nadetastic/cognito-amplify-cdk-10290/commit/0efcc007dfc35fccc408cff9173eb93caca12b37

In short, updated from using the custom header within amplifyConfig.API to setting it within the headers right before calling SSR.API.get(). This was pointed out before but you mentioned that you were still having issues after removing the custom header, but I’d like to note that I created new resources using the CDK stack you provided, so it doesn’t seem to be an issue with the CDK definition (backend looks unchanged in my repo, but I did modify it as you stated in the README just didn’t push the changes).

Are you able to try with the changes I have added? - https://github.com/nadetastic/cognito-amplify-cdk-10290

@phstc thank you very much for sharing the sample app! I will follow up soon

@tannerabread @nadetastic I was able to create a repo cognito-amplify-cdk-10290 with the code to reproduce the problem.

I added deploy instructions in the README to configure the sample code. Please, let me know if that works for you.

Once you run it locally npm run dev and then open http://localhost:3000, there will be two links:

image

One example is working using fetch discussed previously, and the other one is the broken one using SSR.API.

Hi @phstc following up here,

I also haven’t been able to reproduce your issue and I get the expected result when configuring similar to what you have. However one thing l’d like to confirm - on your Authorizer for API Gateway, are you specifying the Token Source?

APIGWY Authorizer

@weisisheng for your question, you shouldn’t have to modify cookie config from aws-exports

Hi 👋 Closing this as resolved. If you are still experiencing this issue and in need of assistance, please feel free to comment and provide us with additional information so we can re-open this issue and be better able to assist you.

Thank you!

Hi 👋 @phstc!

I have reproduced this on the server with a manual configuration of some old cognito and API resources, but the problem does not happen if new resources are created from the Amplify CLI.

It also does not work for me by calling it in useEffect but I was able to obtain the resources from getServerSideProps with fetch.

I have a few follow up questions so that we can figure out the root cause:

  1. Which type of Auth do you have set up for this project? API Key or Cognito?
  2. If it is API key, what is the expiration on the API key? Is it expired?