0

good evening! I am building a NextJS 15 + AWS Amplify application! I know that AWS doesn't yet provide support for NextJS 15, but since this is a new application and AWS will soon offer support for NextJS 15, I decided to start studying it!

In this application, I am working with a business logic focused on server actions, and so far, I have completed the entire custom integration with Cognito for user creation, confirmation, password recovery, and login. However, regarding the last item, when I execute the signIn function from the aws-amplify package, I get a response indicating that the login was successful as you can see bellow.

page.tsx

'use client'

import Link from "next/link";
import Image from "next/image";
import {useActionState} from "react";
import {signInFormType} from "./schema";
import {Brand} from "@/components/ui/brand";
import {Label} from "@/components/ui/label";
import {Submit} from "@/components/ui/submit";
import {Button} from "@/components/ui/button";
import {Heading} from "@/components/ui/heading";
import {Divider} from "@/components/ui/divider";
import {signInWithRedirect} from "aws-amplify/auth";
import {Input, InputError} from "@/components/ui/input";
import {signInWithCredentialsAction} from "./controller";
import {Alert, AlertDescription} from "@/components/ui/alert";

export default function Page() {
  const [state, formAction] = useActionState(signInWithCredentialsAction, {} as signInFormType);

  return (
    <>
      <Brand className="text-5xl"/>

      <Heading title="Sign In" description="Enter your credentials in the fields below to access your dashboard."/>

      {state?.message && (<Alert variant={state?.success ? 'default' : 'destructive'}><AlertDescription>{state?.message}</AlertDescription></Alert>)}

      <form action={formAction} className="space-y-4">
        <div>
          <Label>Email Address</Label>
          <Input type="text" name="email" defaultValue={state?.data?.email || undefined} placeholder="[email protected]"/>
          <InputError message={state?.errors?.fieldErrors?.email} />
        </div>

        <div>
          <div className="flex-between">
            <Label>Password</Label>
            <Link href="/forgot-password" className="link text-sm pr-1">Forgot your password?</Link>
          </div>

          <Input type="password" name="password" placeholder="your password"/>
          <InputError message={state?.errors?.fieldErrors?.password} />
        </div>

        <div className="pt-4">
          <Submit behavior="full">Sign In</Submit>
        </div>
      </form>

      <div className="text-center text-muted-foreground text-sm">
        Don't have an account? <Link href="/register" className="link !font-bold">Sign Up</Link>
      </div>

      <Divider text="ou"/>

      <Button variant="outline" behavior="full" size="sm" onClick={async () =>  await signInWithRedirect({provider: 'Google'})}>
        <Image src="/google.svg" width={24} height={24} alt="Google" className="size-4"/>
        <span className="normal-case font-bold">sign in with Google</span>
      </Button>

      <div className="flex flex-col items-center justify-center">
        <Link href="/" className="link clicked">Home Page</Link>
      </div>
    </>
  )
}

On the server action side, I have the code below.

controller.tsx

'use server'

import {signIn, signInWithRedirect} from "aws-amplify/auth";
import {redirect} from "next/navigation";
import {signInSchema, signInSchemaErrorType, signInSchemaType} from "./schema";

export async function signInWithCredentialsAction(prevState: unknown, payload: FormData) {
  const validated = signInSchema.safeParse(Object.fromEntries(payload.entries()));

  if (!validated.success) {
    return {
      errors: validated.error.formErrors as signInSchemaErrorType,
      data: Object.fromEntries(payload.entries()) as signInSchemaType
    }
  }

  const {nextStep} = await signIn({
    username: validated.data.email,
    password: validated.data.password
  });

  if (isSignedIn) {
    return redirect(`/dashboard`);
  }

  return {
    success: false,
    message: 'Login could not be completed.',
    data: Object.fromEntries(payload.entries()) as signInSchemaType
  };
}

and this is the response that I have when I call the signIn method:

isSignedIn: true
nextStep: { signInStep: 'DONE' }

I also wrote some helper functions to assist with retrieving the authenticated user and the user's session.

/lib/amplify.ts

import {cookies} from "next/headers";
import {ResourcesConfig} from "aws-amplify";
import outputs from "@/amplify_outputs.json";
import {type Schema} from "@/amplify/data/resource";
import {createServerRunner} from "@aws-amplify/adapter-nextjs";
import {getCurrentUser, fetchAuthSession} from "aws-amplify/auth/server";
import {generateServerClientUsingCookies} from "@aws-amplify/adapter-nextjs/data";

export const {runWithAmplifyServerContext} = createServerRunner({config: outputs as ResourcesConfig});

export const cookieBasedClient = generateServerClientUsingCookies<Schema>({config: outputs as ResourcesConfig, cookies, authMode: "iam"});

export const isAuthenticated = async (): Promise<boolean> => {
  return await runWithAmplifyServerContext({
    nextServerContext: {cookies},
    async operation(contextSpec) {
      try {
        const user = await getCurrentUser(contextSpec);

        return !!user;
      } catch {
        return false;
      }
    },
  });
};

but for some reason that I don't understand, when I call the method isAuthenticated() I aways get an false as a return.

and as I'm studying NextJs and AWS Amplify for a couple of weeks I'm a little bit lost! If someone could help me on this case, just to make some workaround while AWS don't release the Next15 support I will be very thankful.

1 Answer 1

1

Had the same problem: Amplify was not correct on the client side, so the subsequent server calls had an initial token: Had to adjust the <ConfigureAmplifyClientSide />like so:

'use client';

import '@aws-amplify/ui-react/styles.css';
import { Amplify, type ResourcesConfig } from 'aws-amplify';
import outputs from '../../amplify_outputs.json';

export const authConfig: ResourcesConfig['Auth'] = {
  Cognito: {
    userPoolId: String(process.env.NEXT_PUBLIC_USER_POOL_ID),
    userPoolClientId: String(
      process.env.NEXT_PUBLIC_USER_POOL_CLIENT_ID
    ),
  },
};

Amplify.configure(
  {
    ...outputs,
    Auth: authConfig,
  },
  {
    ssr: true,
  }
);

export default function ConfigureAmplifyClientSide() {
  return null;
}

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.