NextAuth.js signIn() method not working using serverless-http in AWS Lambda - next.js

I am trying to deploy NextJs and NextAuth.js to AWS using CDK (Cloud Development Kit). I have cloned the NextAuth.js example project (https://github.com/nextauthjs/next-auth-example) and
installed "serverless-http" for handling the binding from Lambda to NextJs. I attempted to follow this guide https://remaster.com/blog/nextjs-lambda-serverless-framework but using AWS CDK instead of the serverless.yml file as I am integrating it with existing infrastructure.
next.config.js:
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
images: {
unoptimized: true
},
output: 'standalone'
}
module.exports = nextConfig
[...nextauth].ts (From the example but using a simple credentials provider that always resolves):
import NextAuth, { NextAuthOptions } from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials";
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
username: { label: "Username", type: "text", placeholder: "jsmith" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
return { id: "1", name: "J Smith", email: "jsmith#example.com" }
}
})
],
theme: {
colorScheme: "light",
},
callbacks: {
async jwt({ token }) {
token.userRole = "admin"
return token
},
},
}
export default NextAuth(authOptions)
server.ts:
import { NextConfig } from "next";
import NextServer from "next/dist/server/next-server";
import serverless from "serverless-http";
// #ts-ignore
import { config } from "./.next/required-server-files.json";
const nextServer = new NextServer({
hostname: "localhost",
port: 3000,
dir: __dirname,
dev: false,
conf: {
...(config as NextConfig),
},
});
export const handler = serverless(nextServer.getRequestHandler());
It is being built using the following script:
#!/bin/bash
BUILD_FOLDER=.dist
yarn build
rm -rf $BUILD_FOLDER
mv .next/standalone/ $BUILD_FOLDER/
cp -r .next/static $BUILD_FOLDER/.next
rm $BUILD_FOLDER/server.js
cp -r next.config.js $BUILD_FOLDER/
cp -r node_modules/serverless-http $BUILD_FOLDER/node_modules/serverless-http
tsc server.ts --outDir .dist --esModuleInterop true
cp -r public $BUILD_FOLDER/
This is deployed using AWS written in CDK C#. Primarily using a HttpApi and a single Lambda. Each configured as shown below:
Lambda:
var function = new Function(this, "nextjs-function", new FunctionProps
{
Code = Code.FromAsset(...".dist"),
Handler = "server.handler",
Runtime = Runtime.NODEJS_16_X,
...
Environment = new Dictionary<string, string>
{
{ "NEXTAUTH_URL", "https://myDomainName.com" },
{ "NEXTAUTH_SECRET", portalSecret },
}
});
HttpApi:
var httpApi = new HttpApi(this, "http-api", new HttpApiProps
{
DisableExecuteApiEndpoint = true,
DefaultIntegration = new HttpLambdaIntegration("nextjs-route", function),
DefaultDomainMapping = new DomainMappingOptions
{
DomainName = "myDomainName.com"
}
});
Opening the deployed webpage and clicking the "Sign In" button at the top, I get taken to /api/auth/signin?callbackUrl=%2F with a form. Without touching the credentials I click "Sign in with credentials". This results in the page reloading and nothing happening. Expected behaviour should be a session and a redirect back to the home page (/) as is happening when running it locally using either yarn dev or yarn build && yarn start.
I get no errors client/server-side thus leaving me in the dark.
I suspect that it has to do with domain configuration but I am unable to find the problem. I tested with another NextJs/NextAuth project using a AWS Cognito provider. This also had problems as when I clicked the sign in button I got an "Unexpected token" error due to the underlying signIn(...) function (from the NextAuth library) trying to parse the fetched page as JSON, which turned out to be the sign-in-page. Thus my suspicion of something domain-related.

Related

500 Internal Server Error fetch()'ing data with Next.js app deployed on Netlify (works locally)

I'm building a toy app with Next.js 13 (with the new app/ directory). My app works fine locally, but generates a 500 Internal Server Error when deployed to Netlify.
Here's the route that generates the error
app/team/[id]/page.jsx
import Link from "next/link";
async function getEmployee() {
const res = await fetch(
'https://dynamic-routes--dundermifflininc.netlify.app/.netlify/functions/team?id=1',
{ cache: 'no-store' }
);
return await res.json();
}
export default async function Employee() {
const employee = await getEmployee();
return (
<div>
<h1>{ employee.name }</h1>
<h3>{ employee.title }</h3>
</div>
);
}
I am confident that the error is happening with fetch(), because if I replace fetch() with hard-coded data, the app works properly. Additionally, the logs for my netlify function indicate that the endpoint isn't even being hit by the fetch() call.
The Netlify function is defined like this
netlify/functions/team.js
exports.handler = async function (event, context) {
const employees = [
{ id: 1, name: "Michael Scott", title: "Regional manager" },
{ id: 2, name: "Jim Halpert", title: "Salesman" },
{ id: 3, name: "Pam Beesly", title: "Receptionist" },
{ id: 4, name: "Dwight Schrute", title: "Assistant to the regional manager" },
];
var data;
if (event.queryStringParameters.hasOwnProperty("id")) {
const id = parseInt(event.queryStringParameters.id);
data = employees[id - 1];
} else {
data = employees;
}
return {
statusCode: 200,
body: JSON.stringify(data),
headers: {
'access-control-allow-origin': '*',
'content-type': 'application/json'
},
};
};
You can test the endpoint for yourself here.
I couldn't reproduce this on a demo site, but checking the logs for your example it's giving the error: "Error: Page changed from static to dynamic at runtime /team/1, see more here https://nextjs.org/docs/messages/app-static-to-dynamic-error
I'm not sure why it's trying to statically render it when you have no-store set, but you might want to try following the instruction in the linked doc, and exporting export const dynamic = 'force-dynamic' in the page.
A good way to track this down is to run netlify serve locally, using the latest version of the Netlify CLI. This is a new option that runs the site in a production-like environment

Next js - Next Auth - Keep having error=OAuthCreateAccount (google provider)

I have set up next-auth with the GoogleProvider.
Everything works fine locally, however in production, I am having aOAuthCreateAccount error: api/auth/signin?error=OAuthCreateAccount
stating "Try signing in with a different account."
I have provided the ID & Secret of the Provider, I have dropped my DB, tried to log with multiples accounts... I do not understand. Is there something that my production environment is not accessing?
Here's my nextauth.js:
`
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import { MongoDBAdapter } from "#next-auth/mongodb-adapter";
import clientPromise from "../../../lib/mongodb";
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
// ...add more providers here
],
secret: process.env.NEXTAUTH_SECRET,
// Can custom page & path
pages: {
signOut: "/auth/signout",
error: "/auth/error", // Error code passed in query string as ?error=
verifyRequest: "/auth/verify-request", // (used for check email message)
// newUser: "/auth/new-user", // New users will be directed here on first sign in (leave the property out if not of interest)
newUser: "/recruiter/2", // New users will be directed here on first sign in (leave the property out if not of interest)
},
adapter: MongoDBAdapter(clientPromise),
});
`
And my mongodb.js:
`
import { MongoClient } from "mongodb";
const uri = process.env.MONGODB_URI;
const options = {
useUnifiedTopology: true,
useNewUrlParser: true,
};
let client;
let clientPromise;
if (!process.env.MONGODB_URI) {
throw new Error("Please add your Mongo URI to .env.local");
}
if (process.env.NODE_ENV === "development") {
// In development mode, use a global variable so that the value
// is preserved across module reloads caused by HMR (Hot Module Replacement).
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options);
global._mongoClientPromise = client.connect();
}
clientPromise = global._mongoClientPromise;
} else {
// In production mode, it's best to not use a global variable.
client = new MongoClient(uri, options);
clientPromise = client.connect();
}
// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise;
`
Thank you!
Read the documentations.
Look on Stackoverflow and github thread, tried all the offered solutions, in vain.
I have managed to fix it reading this thorough article: https://medium.com/geekculture/why-and-how-to-get-started-with-next-auth-61740558b45b
I was missing the database variable in my deployment system (vercel) :)

Next Auth doesn't work with custom basePath

My NextAuth are returning 404 when searching for api/auth/session at credential provider custom login, seems like Next Auth are pointing to the wrong url.
My next.config.js have a basePath that points to a subfolder basePath: '/twenty-test' and my NEXTAUTH_URL is already set to my subdomain,
but when I go to my credential provider login custom page (that was working at localhost because it was not at a subdomain), i see an 404 error at console like https://explample.com/api/auth/session 404.
This is my custom provider config:
providers: [
CredentialProvider({
name: 'Credentials',
type: 'credentials',
async authorize(credentials) {
//
if(credentials.email == "john#gmail.com" && credentials.password == "test"){
return {
id: 2,
name: 'John Doe',
email: 'john#gmail.com',
permition: {
group: 2,
level: 0
}
}
}
return null;
}
})
],
This is my next.config.js
const nextConfig = {
reactStrictMode: true,
basePath: '/twenty-test',
images: {
domains: ['example.com'],
},
}
module.exports = nextConfig
This is my NEXTAUTH_URL env variable
NEXTAUTH_URL="https://example.com/twenty-test/api/auth"
This is my getCsrfToken config
export async function getServerSideProps(context) {
return {
props: {
csrfToken: await getCsrfToken(context)
}
}
}
My project are not on vercel. I'm using a custom server config to deploy with cPanel
The problem was on building the app in localhost and deploying on server.
The app was building expecting NEXTAUTH_URL as localhost, and simply changing the .env variable on server didnt worked.
The solution was building the app on server.
Another workaround was replacing the localhost NEXTAUTH_URL ocurrences after building with the value of NEXTAUTH_URL on server.

Amplify CDK with Next.js SSR

I am trying to deploy a next.js (ssr) application in AWS' Amplify using the CDK but Amplify fails to identify the app as next.js ssr. When I do it manually though, using AWS UI, app is identified as SSR and works as expected.
This is generated by aws-cdk/aws-amplify v118 as:
import * as cdk from '#aws-cdk/core';
import * as amplify from '#aws-cdk/aws-amplify';
import codebuild = require('#aws-cdk/aws-codebuild');
export class AmplifyStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props: cdk.StackProps) {
super(scope, id, props);
const sourceCodeProvider = new amplify.GitHubSourceCodeProvider({
owner: '.....',
repository: '....',
oauthToken: cdk.SecretValue.secretsManager('github-token'),
});
const buildSpec = codebuild.BuildSpec.fromObjectToYaml(
{
version: 1,
applications: [
{
frontend: {
phases: {
preBuild: {
commands: [
"npm install"
]
},
build: {
commands: [
"npm run build"
]
}
},
artifacts: {
baseDirectory: ".next",
files: [
"**/*"
]
},
cache: {
paths: [
"node_modules/**/*"
]
}
}
}
]
}
);
const amplifyApp = new amplify.App(this, "cdk-nf-web-app", {
sourceCodeProvider: sourceCodeProvider,
buildSpec: buildSpec
});
amplifyApp.addBranch('develop', {
basicAuth: amplify.BasicAuth.fromGeneratedPassword('dev')
});
amplifyApp.addCustomRule({
source: "</^[^.]+$|\\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf|map|json)$)([^.]+$)/>",
target: "/index.html",
status: amplify.RedirectStatus.REWRITE
});
}
}
Which is identical to what AWS has generated when I do it manually from UI. The difference here is the lack of Framework identification as shown in picture. Any ideas?
To answer my own question, I was missing the role as without it aws won't create the necessary resources. (role: https://docs.aws.amazon.com/cdk/api/latest/docs/aws-iam-readme.html)
Edit to elaborate on how i fixed it:
Added a new role that can be used by amplify
const role = new iam.Role(this, 'amplify-role-webapp-'+props.environment, {
assumedBy: new iam.ServicePrincipal('amplify.amazonaws.com'),
description: 'Custom role permitting resources creation from Amplify',
});
and assigned a policy (AdministratorAccess) that role
let iManagedPolicy = iam.ManagedPolicy.fromAwsManagedPolicyName(
'AdministratorAccess',
);
role.addManagedPolicy(iManagedPolicy)
Then upon creating the app, i assigned the role to the app:
const amplifyApp = new amplify.App(this, "cdk-nf-web-app", {
sourceCodeProvider: sourceCodeProvider,
buildSpec: buildSpec,
role: role <--- this line here
});
The amplify app requires authorisation to create the relevant resources:
// This is for demonstrations purposes only; Do not give full access for production usage!
amplifyApp.grantPrincipal.addToPrincipalPolicy(new iam.PolicyStatement({
resources: ["*"],
actions: ['*'],
}))
Source Code Showcase

Next.js returns 500: internal server error in Production

Created a next.js full stack application. After production build when I run next start it returns 500 : internal server. I'm using environment varibles for hitting api.
env.development file
BASE_URL=http://localhost:3000
It was working fine in development
service.ts
import axios from 'axios';
const axiosDefaultConfig = {
baseURL: process.env.BASE_URL, // is this line reason for error?
headers: {
'Access-Control-Allow-Origin': '*'
}
};
const axio = axios.create(axiosDefaultConfig);
export class Steam {
static getGames = async () => {
return await axio.get('/api/getAppList');
};
}
Do you have a next.config.js file?
To add runtime configuration to your app open next.config.js and add the publicRuntimeConfig and serverRuntimeConfig configs:
module.exports = {
serverRuntimeConfig: {
// Will only be available on the server side
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET, // Pass through env variables
},
publicRuntimeConfig: {
// Will be available on both server and client
staticFolder: '/static',
},
}
To get access to the runtime configs in your app use next/config, like so:
import getConfig from 'next/config'
// Only holds serverRuntimeConfig and publicRuntimeConfig
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig()
// Will only be available on the server-side
console.log(serverRuntimeConfig.mySecret)
// Will be available on both server-side and client-side
console.log(publicRuntimeConfig.staticFolder)
function MyImage() {
return (
<div>
<img src={`${publicRuntimeConfig.staticFolder}/logo.png`} alt="logo" />
</div>
)
}
export default MyImage
I hope this helps.
I dont think you have setup env.
You need to configure it for it to work. Try it without it and it should work fine!

Resources