amplify-js: Error: "No Credentials" on GraphQL Request Via Amplify in NextJs 14 (with App Router) SSR for Stripe Webhook

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

Authentication, GraphQL API

Amplify Version

v6

Amplify Categories

api

Backend

Amplify CLI

Environment information

# Put output below this line

 System:
    OS: macOS 13.6
    CPU: (10) arm64 Apple M1 Pro
    Memory: 839.03 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 16.14.2 - ~/.nvm/versions/node/v16.14.2/bin/node
    Yarn: 1.22.18 - ~/.nvm/versions/node/v16.14.2/bin/yarn
    npm: 9.6.7 - ~/.nvm/versions/node/v16.14.2/bin/npm
  Browsers:
    Chrome: 120.0.6099.234
    Safari: 17.0
  npmPackages:
    @ampproject/toolbox-optimizer:  undefined ()
    @apollo/client: ^3.8.8 => 3.8.8 
    @apollo/client/cache:  undefined ()
    @apollo/client/core:  undefined ()
    @apollo/client/dev:  undefined ()
    @apollo/client/errors:  undefined ()
    @apollo/client/link/batch:  undefined ()
    @apollo/client/link/batch-http:  undefined ()
    @apollo/client/link/context:  undefined ()
    @apollo/client/link/core:  undefined ()
    @apollo/client/link/error:  undefined ()
    @apollo/client/link/http:  undefined ()
    @apollo/client/link/persisted-queries:  undefined ()
    @apollo/client/link/remove-typename:  undefined ()
    @apollo/client/link/retry:  undefined ()
    @apollo/client/link/schema:  undefined ()
    @apollo/client/link/subscriptions:  undefined ()
    @apollo/client/link/utils:  undefined ()
    @apollo/client/link/ws:  undefined ()
    @apollo/client/react:  undefined ()
    @apollo/client/react/components:  undefined ()
    @apollo/client/react/context:  undefined ()
    @apollo/client/react/hoc:  undefined ()
    @apollo/client/react/hooks:  undefined ()
    @apollo/client/react/parser:  undefined ()
    @apollo/client/react/ssr:  undefined ()
    @apollo/client/testing:  undefined ()
    @apollo/client/testing/core:  undefined ()
    @apollo/client/utilities:  undefined ()
    @apollo/client/utilities/globals:  undefined ()
    @aws-amplify/adapter-nextjs: ^1.0.10 => 1.0.10 
    @aws-amplify/adapter-nextjs/api:  undefined ()
    @aws-amplify/adapter-nextjs/data:  undefined ()
    @babel/core:  undefined ()
    @babel/runtime:  7.22.5 
    @chakra-ui/icons: ^2.1.1 => 2.1.1 
    @chakra-ui/react: ^2.8.2 => 2.8.2 
    @edge-runtime/cookies:  4.0.2 
    @edge-runtime/ponyfill:  2.4.1 
    @edge-runtime/primitives:  4.0.2 
    @emotion/react: ^11.11.3 => 11.11.3 
    @emotion/styled: ^11.11.0 => 11.11.0 
    @hapi/accept:  undefined ()
    @mswjs/interceptors:  undefined ()
    @napi-rs/triples:  undefined ()
    @next/font:  undefined ()
    @next/react-dev-overlay:  undefined ()
    @opentelemetry/api:  undefined ()
    @segment/ajv-human-errors:  undefined ()
    @stripe/react-stripe-js: ^2.4.0 => 2.4.0 
    @stripe/stripe-js: ^2.3.0 => 2.3.0 
    @types/node: ^20 => 20.10.6 
    @types/react: ^18 => 18.2.46 
    @types/react-dom: ^18 => 18.2.18 
    @vercel/nft:  undefined ()
    @vercel/og:  0.5.15 
    acorn:  undefined ()
    amphtml-validator:  undefined ()
    anser:  undefined ()
    apollo-link: ^1.2.14 => 1.2.14 (1.2.5)
    apollo-link-http: ^1.5.17 => 1.5.17 (1.5.8)
    arg:  undefined ()
    assert:  undefined ()
    async-retry:  undefined ()
    async-sema:  undefined ()
    aws-amplify: ^6.0.10 => 6.0.10 
    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-appsync: ^4.1.9 => 4.1.9 
    babel-packages:  undefined ()
    browserify-zlib:  undefined ()
    browserslist:  undefined ()
    buffer:  undefined ()
    bytes:  undefined ()
    ci-info:  undefined ()
    cli-select:  undefined ()
    client-only:  0.0.1 
    comment-json:  undefined ()
    compression:  undefined ()
    conf:  undefined ()
    constants-browserify:  undefined ()
    content-disposition:  undefined ()
    content-type:  undefined ()
    cookie:  undefined ()
    cross-spawn:  undefined ()
    crypto-browserify:  undefined ()
    css.escape:  undefined ()
    data-uri-to-buffer:  undefined ()
    debug:  undefined ()
    devalue:  undefined ()
    domain-browser:  undefined ()
    edge-runtime:  undefined ()
    eslint: ^8 => 8.56.0 
    eslint-config-next: 14.0.4 => 14.0.4 
    events:  undefined ()
    find-cache-dir:  undefined ()
    find-up:  undefined ()
    framer-motion: ^10.17.9 => 10.17.9 
    fresh:  undefined ()
    get-orientation:  undefined ()
    glob:  undefined ()
    gzip-size:  undefined ()
    http-proxy:  undefined ()
    http-proxy-agent:  undefined ()
    https-browserify:  undefined ()
    https-proxy-agent:  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-runner:  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: 14.0.4 => 14.0.4 
    node-fetch:  undefined ()
    node-html-parser:  undefined ()
    ora:  undefined ()
    os-browserify:  undefined ()
    p-limit:  undefined ()
    path-browserify:  undefined ()
    platform:  undefined ()
    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 => 18.2.0 
    react-builtin:  undefined ()
    react-dom: ^18 => 18.2.0 
    react-dom-builtin:  undefined ()
    react-dom-experimental-builtin:  undefined ()
    react-experimental-builtin:  undefined ()
    react-icons: ^4.12.0 => 4.12.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 ()
    shell-quote:  undefined ()
    source-map:  undefined ()
    stacktrace-parser:  undefined ()
    stream-browserify:  undefined ()
    stream-http:  undefined ()
    string-hash:  undefined ()
    string_decoder:  undefined ()
    strip-ansi:  undefined ()
    stripe: ^14.12.0 => 14.12.0 
    superstruct:  undefined ()
    tar:  undefined ()
    terser:  undefined ()
    text-table:  undefined ()
    timers-browserify:  undefined ()
    tty-browserify:  undefined ()
    typescript: ^5 => 5.3.3 
    ua-parser-js:  undefined ()
    unistore:  undefined ()
    util:  undefined ()
    vm-browserify:  undefined ()
    watchpack:  undefined ()
    web-vitals:  undefined ()
    webpack:  undefined ()
    webpack-sources:  undefined ()
    ws:  undefined ()
    zod:  undefined ()
  npmGlobalPackages:
    @aws-amplify/cli: 12.10.0
    apollo: 2.34.0
    avo: 3.2.1
    corepack: 0.10.0
    graphql: 16.6.0
    n: 9.2.0
    npm: 9.6.7
    yarn: 1.22.18

Describe the bug

I’m trying to setup a route.ts to listen for Stripe webhooks then make a graphql request to log details of the transaction in dynamo db. I’ve tried many different approaches following fragments of the Amplify documentation, although I can’t find any docs that seem to follow this exact use case? Some of the docs refer to the old NextApiResponse as opposed to the NextResponse in the App Router approach.

It sounds like “cookies” from next/header is suppose to include the auth information, but how should this be passed in the graphql query? My cookies are always nil in this ssr route. I’m setting the “ssr” to true when I configure amplify in my layout.tsx.

Help greatly appreciated! Should it be possible to use Amplify with Next 14?

Expected behavior

The most basic functionality of a graphql query to work and be documented.

Reproduction steps

I can use the Stripe CLI tool to trigger the payment_intent.succeeded. My code executes but the graphql query fails with the error “No Credentials”.

Code Snippet

// Put your code below this line.
import { NextRequest, NextResponse } from "next/server";
import Stripe from 'stripe';
// import { generateClient } from 'aws-amplify/api';
import { createTransaction } from '../../../graphql/mutations';
import { TransactionInput } from '../../../graphql/API';

// Amplify ssr
import { runWithAmplifyServerContext } from '../../../utils/amplifyServerUtils';
import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/api';
import { cookies } from 'next/headers';
import amplifyConfig from '../../../deployment/amplify-config';


// TODO: put in utils 
export const cookieBasedClient = generateServerClientUsingCookies({
  config: amplifyConfig,
  cookies
});


const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  // @ts-ignore
  apiVersion: '2022-11-15',
});

export async function POST(req: NextRequest) {
  if (req.method !== 'POST') {
    return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), {
      status: 405,
      headers: {
        'Content-Type': 'application/json',
      },
    });
  }

  // Cookie debug
  const rawCookies = req.headers.get('cookie');
  console.log('Raw Cookie String:', rawCookies);

  const sig = req.headers.get('stripe-signature');
  let event;

  try {
    const body = await readRawBody(req);
    event = stripe.webhooks.constructEvent(body, sig!, process.env.STRIPE_WEBHOOK_SECRET!);
  } catch (err: any) {
    return new NextResponse(JSON.stringify({ error: `Webhook Error: ${err.message}` }), {
      status: 400,
      headers: {
        'Content-Type': 'application/json',
      },
    });
  }

  switch (event.type) {
    case 'charge.succeeded':
      console.log('Handling charge.succeeded');
      break;
    case 'payment_intent.created':
      console.log('Handling payment_intent.created');
      break;
    case 'payment_intent.canceled':
      console.log('Handling payment_intent.canceled');
      break;
    case 'payment_intent.succeeded':
      console.log('Handling payment_intent.succeeded');

      const paymentIntentSucceeded = event.data.object as Stripe.PaymentIntent;

      console.log('paymentIntentSucceeded = ', paymentIntentSucceeded);

      const transactionInput: TransactionInput = {
        transactionId: paymentIntentSucceeded.id,
        userId: '1234', // TODO: get this
        amount: paymentIntentSucceeded.amount, 
        currency: paymentIntentSucceeded.currency,
        timestamp: new Date().toISOString(),
        status: paymentIntentSucceeded.status, 
        paymentIntentId: paymentIntentSucceeded.id, 
        priceId: 'priceId', // TODO: set this 
      };

      console.log('cookies = ', cookies().getAll());
      
      try {
        const result = await runWithAmplifyServerContext({
            nextServerContext: { cookies },
            operation: () =>
            cookieBasedClient.graphql({
                    query: createTransaction,
                    variables: {
                      transaction: transactionInput
                    },
                }),
        });
        console.log("createTransaction result transactionId = ", result.data.createTransaction?.transactionId);
    } catch (error) {
        console.log("createTransaction failed with error: ", error);
    }
      break;
    default:
      console.log('Unhandled Stripe webhook event type:', event.type);
      break;
  }

  return new NextResponse(JSON.stringify({ received: true }), {
    status: 200,
    headers: {
      'Content-Type': 'application/json',
    },
  });
}

// Helper function to read raw request body
async function readRawBody(req: NextRequest): Promise<string> {
  if (!req.body) {
    throw new Error("Request body is null");
  }

  const reader = req.body.getReader();
  let receivedValue = '';
  let done = false;

  while (!done) {
    const { value, done: readerDone } = await reader.read();
    done = readerDone;
    if (value) {
      receivedValue += new TextDecoder().decode(value, { stream: true });
    }
  }

  return receivedValue;
}

Log output

// Put your logs below this line


aws-exports.js

No response

Manual configuration

const amplifyConfig: ResourcesConfig = {
  Auth: {
    Cognito: {
      userPoolClientId: config.USER_POOL_CLIENT_ID,
      userPoolId: config.USER_POOL_ID,
      loginWith: { // Optional
        oauth: {
          domain: 'https://my_domain.auth.us-east-1.amazoncognito.com',  
          scopes: ['email', 'openid', 'aws.cognito.signin.user.admin'],
          redirectSignIn: [config.REDIRECT_SIGN_IN],
          redirectSignOut: [config.REDIRECT_SIGN_OUT],
          responseType: 'code',
        },
        username: true, // note: username is their email 
        email: false, // Optional
        phone: false, // Optional
      }
    }
  },
  API: {
    GraphQL: {
      endpoint: config.GRAPHQL_ENDPOINT,
      defaultAuthMode: 'iam',  
      region: config.REGION, // Optional
    }
  },
};

export default amplifyConfig;

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 5 months ago
  • Reactions: 2
  • Comments: 25 (12 by maintainers)

Most upvoted comments

Yes, it works nice @chrisbonifacio . Thanks.

I’ve test that in server-side and it works OK.

const client = generateServerClientUsingCookies({
  config: config,
  cookies,
  // authMode: "iam", Default is configured to IAM
});

const mutationTest = await client.graphql({
  query: mutations.createTodo,
  variables: {
    input: {
      content: "Content",
    },
  },
  // authMode: "iam", Default is configured to IAM
});

const queryTest = await client.graphql({
    query: queries.listTodo,
    variables: {},
    // authMode: "iam", Default is configured to IAM
  });

Based on the testing with the stack that @jojemapa provided, this is a bug that using iam auth mode is not working as expected on the server side. We are currently working on a fix.

Hi, i use Amplkify CLI.

amplify add auth amplidy add api

in my schema i just put allow: private and provider: iam.

The last part amplify add the iam permission to appsync in the auth role.

mmm, i finally i get that error, i still searching for a fix.