AWS Amplify Authentication Issue: "Auth UserPool not configured" in a Next.js Project
Asked Answered
H

8

8

I'm working on a NextJS project where I'm using AWS Amplify for authentication with Amazon Cognito. However, I'm encountering an issue where I receive an error message stating "Auth UserPool not configured" when I try to log in/sign up. I've checked my configurations and they seem correct, I am also able to manually create users from the AWS GUI. Below is the relevant code and configuration details.

Index.js

import React, { useState, useEffect } from 'react'

import { Amplify } from 'aws-amplify'
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
import { Auth } from 'aws-amplify'

Amplify.configure({
  Auth: {
    region: process.env.REGION,
    userPoolId: process.env.USER_POOL_ID,
    userPoolWebClientId: process.env.USER_POOL_APP_CLIENT_ID,
  },
})

function Home() {
  const [jwtToken, setJwtToken] = useState('')

  useEffect(() => {
    fetchJwtToken()
  }, [])

  const fetchJwtToken = async () => {
    try {
      const session = await Auth.currentSession()
      const token = session.getIdToken().getJwtToken()
      setJwtToken(token)
    } catch (error) {
      console.log('Error fetching JWT token:', error)
    }
  }

  return (
    <Authenticator
      initialState='signIn'
      components={{
        SignUp: {
          FormFields() {
            return (
              <>
                <Authenticator.SignUp.FormFields />

                {/* Custom fields for given_name and family_name */}
                <div>
                  <label>First name</label>
                </div>
                <input
                  type='text'
                  name='given_name'
                  placeholder='Please enter your first name'
                />
                <div>
                  <label>Last name</label>
                </div>
                <input
                  type='text'
                  name='family_name'
                  placeholder='Please enter your last name'
                />
                <div>
                  <label>Email</label>
                </div>
                <input
                  type='text'
                  name='email'
                  placeholder='Please enter a valid email'
                />
              </>
            )
          },
        },
      }}
      services={{
        async validateCustomSignUp(formData) {
          if (!formData.given_name) {
            return {
              given_name: 'First Name is required',
            }
          }
          if (!formData.family_name) {
            return {
              family_name: 'Last Name is required',
            }
          }
          if (!formData.email) {
            return {
              email: 'Email is required',
            }
          }
        },
      }}
    >
      {({ signOut, user }) => (
        <div>
          Welcome {user.username}
          <button onClick={signOut}>Sign out</button>
          <h4>Your JWT token:</h4>
          {jwtToken}
        </div>
      )}
    </Authenticator>
  )
}

export default Home

This is the template.yaml file I ran using sam deploy --guided to create the infrastructure

AWSTemplateFormatVersion: 2010-09-09
Description: >-
  template for creating the Cognito User Pool
Transform:
  - AWS::Serverless-2016-10-31

Parameters:
  Env:
    Type: String
    Default: dev

  S3BucketName:
    Type: String
    Default: pibot-nextjs-website
  CognitoUserPoolName:
    Type: String
    Default: pibot-users-v2
  CognitoWebClientName:
    Type: String
    Default: cognito-webclient

Resources:
  CloudFrontOriginAccessIdentity:
    Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: 'Origin Access Identity'

  CloudfrontDistribution:
    Type: 'AWS::CloudFront::Distribution'
    Properties:
      DistributionConfig:
        Comment: 'Cloudfront distribution for the static website'
        DefaultRootObject: 'index.html'
        Enabled: true
        HttpVersion: http2
        Origins:
          - Id: s3-website
            DomainName: !GetAtt S3Bucket.DomainName
            S3OriginConfig:
              OriginAccessIdentity:
                Fn::Sub: 'origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}'
        DefaultCacheBehavior:
          Compress: 'true'
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
          ForwardedValues:
            QueryString: false
          TargetOriginId: s3-website
          ViewerProtocolPolicy: redirect-to-https

  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${Env}-${S3BucketName}'

  S3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action: 's3:GetObject'
            Resource:
              - !Sub 'arn:aws:s3:::${S3Bucket}/*'
            Principal:
              AWS: !Sub 'arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}'

  CognitoUserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: !Sub '${Env}-${CognitoUserPoolName}'
      AliasAttributes:
        - email
      UsernameConfiguration:
        CaseSensitive: false
      AutoVerifiedAttributes:
        - email
      Policies:
        PasswordPolicy:
          MinimumLength: 8
          RequireLowercase: true
          RequireNumbers: true
          RequireUppercase: true
          RequireSymbols: true
      Schema:
        - AttributeDataType: String
          Mutable: true
          Name: given_name
          Required: true
          StringAttributeConstraints:
            MinLength: '1'
        - AttributeDataType: String
          Mutable: true
          Name: family_name
          Required: true
          StringAttributeConstraints:
            MinLength: '1'
        - AttributeDataType: String
          Mutable: true
          Name: email
          Required: true
          StringAttributeConstraints:
            MinLength: '1'

  WebCognitoUserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      ClientName: !Sub '${Env}-${CognitoWebClientName}'
      UserPoolId: !Ref CognitoUserPool
      ExplicitAuthFlows:
        - ALLOW_USER_SRP_AUTH
        - ALLOW_REFRESH_TOKEN_AUTH
      PreventUserExistenceErrors: ENABLED

Hilarius answered 15/12, 2023 at 17:40 Comment(3)
how are you defining your environmental variables in your Next app? make sure you're doing it in the correct way: nextjs.org/docs/pages/building-your-application/configuring/…Beckibeckie
its a bit late but which amplify version are you using ?Hoffman
After many hours of struggle, this solved the problem. Add 'use client'; to first line of src/app/page.tsxGiacometti
A
7

It appears that the issue stems from the configuration setup. In AWS Amplify version 6, user pool configuration has been updated. Please adjust your configuration as follows:

Amplify.configure({
  Auth: {
    Cognito: {
      userPoolClientId: 'abcdefghij1234567890',
      userPoolId: 'us-east-1_abcd1234',
      loginWith: { // Optional
        oauth: {
          domain: 'abcdefghij1234567890-29051e27.auth.us-east-1.amazoncognito.com',
          scopes: ['openid email phone profile aws.cognito.signin.user.admin '],
          redirectSignIn: ['http://localhost:3000/','https://example.com/'],
          redirectSignOut: ['http://localhost:3000/','https://example.com/'],
          responseType: 'code',
        }
        username: 'true',
        email: 'false', // Optional
        phone: 'false', // Optional
      }
    }
  }
});

For additional details, you can refer to the official documentation at: AWS Amplify Documentation

I hope this helps resolve your issue. If you have further questions, feel free to ask.

Averroes answered 18/12, 2023 at 21:32 Comment(1)
The typescript types indicate true and false not 'true' and 'false'.Pupil
S
4

In my case, the config object was correct.

My issue was ultimately a "single character" error of importing from the old lib.

The fix was to modify...

import { Amplify } from '@aws-amplify';
// Amplify.configure( ... );

... to instead be...

import { Amplify } from 'aws-amplify';
// Amplify.configure( ... );

☝️ notice the removed @

source

Sculley answered 19/4 at 15:15 Comment(0)
W
1

I had the same error until this afternoon. I figured out what the error is (or was is my case). I think I can see why you'd get the same error.

Like you, I was able to create new users and see them in Amplify console. However I wasn't able to log in due to that error.

You've chosen to configure amplify with the lines that you've shown. You may want to comment out that block until after you try what I suggest.

Amplify will have configured a default configuration as you've set up your authentication and answered the questions to amplify add auth or amplify update auth. This config is output to ./aws-export.js and ./amplifyconfiguration.json. They both have the same info, only the first is only the json without export and the second file exports the data.

The problem is that you're not using all the settings from this in your current project. To do this you need to call Amplify.configure() with the json like shown here: https://docs.amplify.aws/javascript/build-a-backend/auth/set-up-auth/

import { Amplify } from 'aws-amplify';
import config from './amplifyconfiguration.json';
Amplify.configure(config);

The best way to figure to actually do this is to create a separate file: ConfigureAmplify.tsx with the following contents:

import { Amplify } from "aws-amplify";
import config from "../amplifyconfiguration.json";

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

export default function ConfigureAmplifyClientSide() {
  return null;
}

This essentially wraps the include into a do-nothing component and allows you to use the configuration anywhere in your project. Add it to your ./src/app/page.tsx:

'use client';

import type { WithAuthenticatorProps } from '@aws-amplify/ui-react';
import { withAuthenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import ConfigureAmplifyClientSide from '../../components/ConfigureAmplify';

export function App({ signOut, user } : WithAuthenticatorProps) {
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <ConfigureAmplifyClientSide />
      <h1>Hello {user?.username}</h1>
      <button onClick={signOut}>Sign out</button>
    </main>
  );
}

export default withAuthenticator(App);

I found this somewhere in the documentation. When in the right place that error should disappear. You should be able to add your current Amplify.configure back where you have it. You may even find it beneficial to add it to the bottom of the ConfigureAmplify.tsx file.

You're able to call Amplify.configure() many times to override/add/update functionality as you wish.

In my case I was calling Amplify.configure() in the wrong file. I moved it and got it working "inline" however I prefer it in a separate file.

Good luck!

Worrell answered 6/3 at 21:54 Comment(0)
P
1

The core of the problem is that Amplify.configure(amplifyConfig) is not async - so we can await it somewhere early in <App />

When you call the configure in the entry of your react code the react router could be already trying to use something from amplify.

i have to find a pattern so that Amplify is configured before any loading happens. For example this AuthGuard fails everytime:


const AuthGuard = () => {
  const user = await getCurrentUser();

  useEffect(() => {
    if (!user) {
      window.location.href = "/auth/login";
    }
  }, [user]);

  return <Outlet/>;
};

because the routes get loaded before Amplify.configure(amplifyConfig)

chunk-GSLA2FH7.js?v=bb46ed7c:45 Uncaught (in promise) AuthUserPoolException: Auth UserPool not configured.
    at http://localhost:5173/node_modules/.vite/deps/chunk-GSLA2FH7.js?v=bb46ed7c:45:11
    at assertTokenProviderConfig (http://localhost:5173/node_modules/.vite/deps/chunk-GSLA2FH7.js?v=bb46ed7c:153:3)
    at getCurrentUser (http://localhost:5173/node_modules/.vite/deps/chunk-GSLA2FH7.js?v=bb46ed7c:6153:3)
    at getCurrentUser2 (http://localhost:5173/node_modules/.vite/deps/chunk-GSLA2FH7.js?v=bb46ed7c:6173:10)
    at AuthGuard (http://localhost:5173/src/app/components/auth/guards/AuthGuard.tsx?t=1724479425050:23:16)
    at getProfileRoutes (http://localhost:5173/src/app/routes/profileRoutes.ts?t=1724479425050:27:16)
    at http://localhost:5173/src/app/routes/appRoutes.ts?t=1724479425050:72:6

Practise answered 24/8 at 6:24 Comment(0)
B
0

I think You need to use NEXT_PUBLIC_ for your environmental vars to make them accessible to your browser environment? https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser

so your vars should be:

Amplify.configure({
  Auth: {
   Cognito: {
    region: process.env.NEXT_PUBLIC_REGION,
    userPoolId: process.env.NEXT_PUBLIC_USER_POOL_ID,
    userPoolWebClientId: process.env.NEXT_PUBLIC_USER_POOL_APP_CLIENT_ID,
   }
  }
});

don't forget to change the var names in your .env file too :)

Also in amplify docs userpool is configured inside Cognito property of Auth object

Beckibeckie answered 15/12, 2023 at 20:36 Comment(0)
B
0

I had the same problem and was related to the configuration on "aws-exports", the Documentation is a little confusing, but I made it work with this config:

export const amplifyConfig = {
  Auth: {
    Cognito: {
      userPoolId: your_userPoolId,
      userPoolClientId: your_userPoolClientId,
      region: your_region,
      loginWith: {
        oauth: {
          domain: your_domain,
          scopes: [
            'phone',
            'email',
            'profile',
            'openid',
            'aws.cognito.signin.user.admin'
          ],
          redirectSignIn: [window.location.origin],
          redirectSignOut: [window.location.origin],
          responseType: "code",
        },
      },
    },
  },
};

And also you should have something like this in you index

import { Amplify, ResourcesConfig } from "aws-amplify";
import { amplifyConfig } from './aws-exports';
Amplify.configure(amplifyConfig as ResourcesConfig );

I hope it works for you.

refs: https://github.com/aws-amplify/amplify-js/issues/12627#issuecomment-1848214340

Buchmanism answered 22/2 at 19:28 Comment(0)
H
0

For me, I had to import the following:

import { Amplify } from 'aws-amplify';
import amplifyconfig from '../src/amplifyconfiguration.json';

then call:

Amplify.configure(amplifyconfig);

I'm running react-native on expo.

Reference:

https://docs.amplify.aws/gen1/react-native/build-a-backend/auth/set-up-auth/

Hendeca answered 8/5 at 2:56 Comment(0)
P
0

Arriving here searching for the error in the title, the issue and its resolution for me wound up being very different from the ones so far described. Rather than Next.js I was using react-native with Expo and the metro bundler. That's a very different setup, but because I found this stackoverflow question for my identical error, this might be relevant to other searchers.

I had totally messed up my configuration for typescript and for the metro bundler.

Do NOT do this. I had no idea what I was doing; this was wrong and bad:

// metro.config.js:
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");
const defaultSourceExts =
  require("metro-config/src/defaults/defaults").sourceExts;
const sourceExts = [
  "jsx",
  "js",
  "cjs",
  "ts",
  "tsx",
  "json",
  "svg",
  "d.ts",
  "mjs",
].concat(defaultSourceExts);

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

module.exports = { ...config, resolver: { sourceExts } };


// tsconfig.json:
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true,
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "resolveJsonModule": true,
    "esModuleInterop": true
  },
  "include": [
    ...
  ],
  "exclude": ["node_modules/**/*", "babel.config.js", "metro.config.js"]
}

After reviewing the expo docs for typescript and metro, and educating myself somewhat better about TypeScript compilation, cjs, and esm, I changed those to this much more default-like config:

// metro.config.js:
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

module.exports = config;

// tsconfig.json:
{
  "extends": "expo/tsconfig.base",
  "compilerOptions": {
    "strict": true
  },
  "include": [
    ...
  ]
}

I determined I needed to look at this configuration by adding console.log statements to various amplify source code beneath node_modules/@aws-amplify. By doing so I determined that some amplify code from beneath dist/cjs was calling other code beneath dist/esm and vice-versa, which was causing the Amplify config to appear falsy in the module system by which it hadn't been loaded, and that system was the one throwing the error.

That was my clue that my typescript and javascript-module systems were horribly misconfigured. Returning to the defaults from expo did indeed resolve this specific error for my setup.

Pupil answered 7/7 at 1:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.