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.