aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.43k stars 2.13k forks source link

Trying to call rest api from getserversideprops gives me 401 unauthorized error #13125

Closed didemkkaslan closed 7 months ago

didemkkaslan commented 7 months ago

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

REST API

Amplify Version

v6

Amplify Categories

api

Backend

CDK

Environment information

``` # Put output below this line System: OS: macOS 14.2.1 CPU: (8) arm64 Apple M2 Memory: 154.86 MB / 8.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 20.10.0 - ~/.nvm/versions/node/v20.10.0/bin/node Yarn: 1.22.19 - /opt/homebrew/bin/yarn npm: 10.2.3 - ~/.nvm/versions/node/v20.10.0/bin/npm pnpm: 8.13.1 - ~/.nvm/versions/node/v20.10.0/bin/pnpm bun: 1.0.2 - ~/.bun/bin/bun Browsers: Chrome: 122.0.6261.129 Safari: 17.2.1 npmPackages: @amplitude/analytics-browser: ^2.3.3 => 2.4.1 @ampproject/toolbox-optimizer: undefined () @ant-design/cssinjs: ^1.17.2 => 1.18.4 @ant-design/icons: ^5.2.6 => 5.3.0 @ant-design/plots: ^1.2.5 => 1.2.6 @aws-amplify/adapter-nextjs: ^1.0.9 => 1.0.16 @aws-amplify/adapter-nextjs/api: undefined () @aws-amplify/adapter-nextjs/data: undefined () @babel/core: undefined () @babel/runtime: 7.15.4 @edge-runtime/cookies: 4.0.2 @edge-runtime/ponyfill: 2.4.1 @edge-runtime/primitives: 4.0.2 @graphql-codegen/cli: 5.0.0 => 5.0.0 @graphql-codegen/client-preset: 4.1.0 => 4.1.0 @graphql-codegen/introspection: 4.0.0 => 4.0.0 @hapi/accept: undefined () @mantine/hooks: ^7.1.5 => 7.5.2 @microsoft/teams-js: ^2.16.0 => 2.20.0 @mswjs/interceptors: undefined () @napi-rs/triples: undefined () @next/bundle-analyzer: ^13.5.6 => 13.5.6 @next/font: undefined () @next/react-dev-overlay: undefined () @opentelemetry/api: undefined () @react-pdf/renderer: ^3.1.13 => 3.3.8 @segment/ajv-human-errors: undefined () @tailwindcss/typography: ^0.5.10 => 0.5.10 @tanstack/query-codemods: 4.24.3 @tanstack/react-query: ^5.0.5 => 5.20.5 @tanstack/react-query-devtools: ^5.8.9 => 5.20.5 @testing-library/jest-dom: ^6.1.4 => 6.4.2 @testing-library/react: ^14.0.0 => 14.2.1 @testing-library/user-event: ^14.5.1 => 14.5.2 @types/jest: ^29.5.6 => 29.5.12 @types/lodash: ^4.14.200 => 4.14.202 @types/mixpanel-browser: ^2.47.4 => 2.49.0 @types/node: ^20.8.8 => 20.11.17 (16.18.87) @types/react: ^18.2.31 => 18.2.55 @types/react-dom: ^18.2.14 => 18.2.19 @types/react-google-recaptcha: ^2.1.7 => 2.1.9 @types/react-highlight-words: ^0.16.6 => 0.16.7 @types/uuid: ^9.0.7 => 9.0.8 @typescript-eslint/eslint-plugin: ^6.9.0 => 6.21.0 @typescript-eslint/parser: ^6.9.0 => 6.21.0 @vercel/nft: undefined () @vercel/og: undefined () acorn: undefined () amphtml-validator: undefined () anser: undefined () antd: 5.10.2 => 5.10.2 apexcharts: ^3.44.0 => 3.45.2 arg: undefined () assert: undefined () async-retry: undefined () async-sema: undefined () autoprefixer: ^10.4.16 => 10.4.17 aws-amplify: 6.0.15 => 6.0.15 aws-amplify/adapter-core: undefined () aws-amplify/analytics: undefined () aws-amplify/analytics/kinesis: undefined () aws-amplify/analytics/kinesis-firehose: undefined () aws-amplify/analytics/personalize: undefined () aws-amplify/analytics/pinpoint: undefined () aws-amplify/api: undefined () aws-amplify/api/server: undefined () aws-amplify/auth: undefined () aws-amplify/auth/cognito: undefined () aws-amplify/auth/cognito/server: undefined () aws-amplify/auth/enable-oauth-listener: undefined () aws-amplify/auth/server: undefined () aws-amplify/datastore: undefined () aws-amplify/in-app-messaging: undefined () aws-amplify/in-app-messaging/pinpoint: undefined () aws-amplify/push-notifications: undefined () aws-amplify/push-notifications/pinpoint: undefined () aws-amplify/storage: undefined () aws-amplify/storage/s3: undefined () aws-amplify/storage/s3/server: undefined () aws-amplify/storage/server: undefined () aws-amplify/utils: undefined () aws-rum-web: ^1.15.0 => 1.17.0 axios: ^1.5.1 => 1.6.7 babel-packages: undefined () browserify-zlib: undefined () browserslist: undefined () buffer: undefined () bytes: undefined () ci-info: undefined () cli-select: undefined () client-only: 0.0.1 clsx: ^2.0.0 => 2.1.0 comment-json: undefined () compression: undefined () conf: undefined () constants-browserify: undefined () content-disposition: undefined () content-type: undefined () cookie: undefined () cookies-next: ^4.1.1 => 4.1.1 cross-spawn: undefined () crypto-browserify: undefined () css.escape: undefined () data-uri-to-buffer: undefined () dayjs: ^1.11.10 => 1.11.10 debug: undefined () devalue: undefined () docx: ^8.5.0 => 8.5.0 domain-browser: undefined () edge-runtime: undefined () eslint: ^8.52.0 => 8.56.0 eslint-config-airbnb: ^19.0.4 => 19.0.4 eslint-config-airbnb-typescript: ^17.1.0 => 17.1.0 eslint-config-next: ^13.5.6 => 13.5.6 eslint-config-prettier: ^9.0.0 => 9.1.0 eslint-plugin-i18next: ^6.0.3 => 6.0.3 eslint-plugin-import: ^2.29.0 => 2.29.1 eslint-plugin-jest: ^27.4.3 => 27.8.0 eslint-plugin-jest-dom: ^5.1.0 => 5.1.0 eslint-plugin-jsx-a11y: ^6.7.1 => 6.8.0 eslint-plugin-react: ^7.33.2 => 7.33.2 eslint-plugin-testing-library: ^6.1.2 => 6.2.0 events: undefined () find-cache-dir: undefined () find-up: undefined () framer-motion: ^10.16.4 => 10.18.0 fresh: undefined () get-orientation: undefined () glob: undefined () graphql: ^16.8.1 => 16.8.1 (15.8.0) graphql-request: ^6.1.0 => 6.1.0 gzip-size: undefined () http-proxy: undefined () http-proxy-agent: undefined () https-browserify: undefined () https-proxy-agent: undefined () husky: ^8.0.3 => 8.0.3 i18next: ^23.7.15 => 23.8.2 i18next-chained-backend: ^4.5.0 => 4.6.2 i18next-http-backend: ^2.2.2 => 2.4.3 i18next-localstorage-backend: ^4.2.0 => 4.2.0 icss-utils: undefined () ignore-loader: undefined () image-size: undefined () is-animated: undefined () is-docker: undefined () is-wsl: undefined () jest: ^29.7.0 => 29.7.0 jest-environment-jsdom: ^29.7.0 => 29.7.0 jest-worker: undefined () json5: undefined () jsonwebtoken: undefined () jwt-decode: ^3.1.2 => 3.1.2 loader-runner: undefined () loader-utils: undefined () lodash: ^4.17.21 => 4.17.21 lodash.curry: undefined () lru-cache: undefined () micromatch: undefined () mini-css-extract-plugin: undefined () mixpanel-browser: ^2.47.0 => 2.49.0 nanoid: undefined () native-url: undefined () neo-async: undefined () next: ^13.5.6 => 13.5.6 next-i18next: ^15.1.2 => 15.2.0 next-i18next-create-client: undefined () next-seo: ^6.1.0 => 6.4.0 node-fetch: undefined () node-html-parser: undefined () nookies: ^2.5.2 => 2.5.2 ora: undefined () os-browserify: undefined () p-limit: undefined () path-browserify: undefined () platform: undefined () postcss: ^8.4.31 => 8.4.35 (8.4.31) 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 () prettier: ^3.0.3 => 3.2.5 prettier-plugin-tailwindcss: ^0.5.6 => 0.5.11 process: undefined () punycode: undefined () querystring-es3: undefined () raw-body: undefined () react: 18.2.0 => 18.2.0 react-apexcharts: ^1.4.1 => 1.4.1 react-builtin: undefined () react-dom: 18.2.0 => 18.2.0 react-dom-builtin: undefined () react-dom-experimental-builtin: undefined () react-experimental-builtin: undefined () react-google-recaptcha: ^3.1.0 => 3.1.0 react-highlight-words: ^0.20.0 => 0.20.0 react-i18next: ^14.0.0 => 14.0.5 react-icons: ^4.11.0 => 4.12.0 react-infinite-scroll-component: ^6.1.0 => 6.1.0 react-is: 18.2.0 react-refresh: 0.12.0 react-server-dom-turbopack-builtin: undefined () react-server-dom-turbopack-experimental-builtin: undefined () react-server-dom-webpack-builtin: undefined () react-server-dom-webpack-experimental-builtin: undefined () regenerator-runtime: 0.13.4 sass-loader: undefined () scheduler-builtin: undefined () scheduler-experimental-builtin: undefined () schema-utils: undefined () semver: undefined () send: undefined () server-only: 0.0.1 setimmediate: undefined () sharp: ^0.32.6 => 0.32.6 shell-quote: undefined () source-map: undefined () stacktrace-parser: undefined () stream-browserify: undefined () stream-http: undefined () string-hash: undefined () string_decoder: undefined () strip-ansi: undefined () superstruct: undefined () tailwind-merge: ^1.14.0 => 1.14.0 tailwindcss: ^3.3.3 => 3.4.1 tar: undefined () terser: undefined () text-table: undefined () timers-browserify: undefined () tty-browserify: undefined () typescript: ^5.2.2 => 5.3.3 ua-parser-js: undefined () undici: undefined () unistore: undefined () usehooks-ts: ^2.9.1 => 2.14.0 util: undefined () uuid: ^9.0.1 => 9.0.1 (8.3.2) vm-browserify: undefined () watchpack: undefined () web-vitals: undefined () webpack: undefined () webpack-sources: undefined () ws: undefined () zod: ^3.22.4 => 3.22.4 () npmGlobalPackages: @aws-amplify/cli: 12.10.0 corepack: 0.22.0 eas-cli: 7.0.0 npm: 10.2.3 pnpm: 8.13.1 ```

Describe the bug

I'm getting unauthorized error trying to access rest api endpoints using getServerSideProps in a NextJS app(pages router).

 ⨯ unhandledRejection: UnauthorizedException: Unauthorized
    at buildRestApiError (file:///Users/didemkucukkaraaslan/spiky-projects/platform-v2/node_modules/@aws-amplify/api-rest/dist/esm/utils/serviceError.mjs:89:26)
    at parseRestApiServiceError (file:///Users/didemkucukkaraaslan/spiky-projects/platform-v2/node_modules/@aws-amplify/api-rest/dist/esm/utils/serviceError.mjs:26:16)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async job (file:///Users/didemkucukkaraaslan/spiky-projects/platform-v2/node_modules/@aws-amplify/api-rest/dist/esm/utils/createCancellableOperation.mjs:27:23) {
  underlyingError: UnauthorizedException: Unauthorized
      at parseJsonError (file:///Users/didemkucukkaraaslan/spiky-projects/platform-v2/node_modules/@aws-amplify/core/dist/esm/clients/serde/json.mjs:28:19)
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async parseRestApiServiceError (file:///Users/didemkucukkaraaslan/spiky-projects/platform-v2/node_modules/@aws-amplify/api-rest/dist/esm/utils/serviceError.mjs:19:28)
      at async job (file:///Users/didemkucukkaraaslan/spiky-projects/platform-v2/node_modules/@aws-amplify/api-rest/dist/esm/utils/createCancellableOperation.mjs:27:23) {
    '$metadata': {
      httpStatusCode: 401,
      requestId: '40937120-1421-462c-94fb-e39306c89ed2',
      extendedRequestId: undefined,
      cfId: 'h5NI1kUBA7UM-p9_aBafVFrbxs3vLerxrge9aD0i6gkT-v0hmKqUEQ=='
    }
  },
  recoverySuggestion: undefined,
  constructor: [class RestApiError extends ApiError],
  _response: {
    statusCode: 401,
    headers: {
      connection: 'keep-alive',
      'content-length': '26',
      'content-type': 'application/json',
      date: 'Fri, 15 Mar 2024 06:45:51 GMT',
      via: '1.1 6e4ed2b1996ce238462d61d3bfff667a.cloudfront.net (CloudFront)',
      'x-amz-apigw-id': 'UqI5AGd6CYcECog=',
      'x-amz-cf-id': 'h5NI1kUBA7UM-p9_aBafVFrbxs3vLerxrge9aD0i6gkT-v0hmKqUEQ==',
      'x-amz-cf-pop': 'FRA60-P6',
      'x-amzn-errortype': 'UnauthorizedException',
      'x-amzn-requestid': '40937120-1421-462c-94fb-e39306c89ed2',
      'x-cache': 'Error from cloudfront'
    },
    body: '{"message":"Unauthorized"}'
  },
  '$metadata': {
    httpStatusCode: 401,
    requestId: '40937120-1421-462c-94fb-e39306c89ed2',
    extendedRequestId: undefined,
    cfId: 'h5NI1kUBA7UM-p9_aBafVFrbxs3vLerxrge9aD0i6gkT-v0hmKqUEQ=='
  }
}

Expected behavior

It shouldnt be unauthorized

Reproduction steps

  export const getServerSideProps: GetServerSideProps = async (
  ctx: GetServerSidePropsContext,
) => {
  const locale = getLocale(ctx);
  const { scopes } = await getScopes(ctx);

  // I can access amplify category apis with no problem
  const userAttributes = await runWithAmplifyServerContext({
    nextServerContext: { request: ctx.req, response: ctx.res },
    operation: (contextSpec) => fetchUserAttributes(contextSpec),
  });

  // This one is failing
  const user = await runWithAmplifyServerContext({
    nextServerContext: { request: ctx.req, response: ctx.res },
    operation: async (contextSpec) =>
      get({
        apiName: APINames.PlatformCoreRestApi,
        path: '/people/me',
        options: {
          headers: {
            Authorization: `Bearer ${contextSpec.token.value.toString()}`,
          },
        },
      }),
  });

  return {
    props: {
      ...(await serverSideTranslations(locale, ['settings', 'onboarding'])),
      scopes,
      currentUserAttributes: userAttributes,
    },
  };
};

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

chrisbonifacio commented 7 months ago

Hi @didemkkaslan thanks for raising this issue!

In aws-amplify v6, you can import a server-side method for the REST API operations.

For instance, you can refactor your code a bit to something like this:

import { get } from "aws-amplify/api/server";

 const user = await runWithAmplifyServerContext({
    nextServerContext: { request: ctx.req, response: ctx.res },
    operation: async (contextSpec) =>
      get(contextSpec, {
        apiName: APINames.PlatformCoreRestApi,
        path: '/people/me',
        options: {
          headers: {
            Authorization: `Bearer ${contextSpec.token.value.toString()}`,
          },
        },
      }),
  });

Can you try this and see if it resolves the issue?

HuiSF commented 7 months ago

In addition to the above example, the token property of the contextSpec object that's passed by runWithAmplifyServerContext function, is not an auth token.

didemkkaslan commented 7 months ago

Hello @chrisbonifacio thanks for the help but I think get only accepts 1 paramater its not really accepting the contextSpec

Screenshot 2024-03-19 at 19 15 26

I've tried below approach also with no luck it returns 401 Unauthorized

  const user = await runWithAmplifyServerContext({
    nextServerContext: { request: ctx.req, response: ctx.res },
    operation: async (contextSpec) =>
      get({
        apiName: APINames.PlatformCoreRestApi,
        path: '/people/me',
        options: {
          headers: {
            Authorization: `Bearer ${contextSpec.token.value.toString()}`,
          },
        },
      }),
  });

What worked for me is this one( tho i really dont think its the easiest way):

 const user = await runWithAmplifyServerContext({
    nextServerContext: { request: ctx.req, response: ctx.res },
    operation: async (contextSpec) => {
      const session = await fetchAuthSession(contextSpec);
      const token = session.tokens?.idToken?.toString();
      const response = await get({
        apiName: APINames.PlatformCoreRestApi,
        path: '/people/me',
        options: {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        },
      });
      return (await response.response).body.json() as unknown as PeopleData;
    },
  });
chrisbonifacio commented 7 months ago

@didemkkaslan awesome! Yes, fetchAuthSession would be the recommended way to get the user's cognito tokens.

Thanks @HuiSF for the clarification!

HuiSF commented 7 months ago

Hi @didemkkaslan This documentation states that all APIs that are supported to be used on the server side, are exported from the /server subpath.

In your use case you should use the get API export from aws-amplify/api/server.

Also, please avoid directly using @aws-amplify namespaced packages. aws-amplify is the entry point you should interact with.