She*_*ith 6 nginx typescript amazon-cognito next.js next-auth
我正在尝试发布一个 nextjs 应用程序,该应用程序将“next-auth”与 aws Cognito 结合使用。
当我在本地运行它时,使用next devORnext start它工作得很好。
当我在生产服务器(ubuntu,带有 nginx)上运行它时,它没有。
确切错误:访问 Cognito 内置登录页面后,重定向 URLhttps://...../api/auth/callback/cognito?code=......&state=.....显示 nginx 的默认 502 错误页面。
我检查过的内容:
是的,Callback URL(s)AWS Cognito 本身中的应用程序设置已设置为正确的 url ( https:// ....... /api/auth/callback/cognito)。
代码:
middleware.ts
export { default } from "next-auth/middleware";
export const config = { matcher: ["/dashboard/:path*"] };
Run Code Online (Sandbox Code Playgroud)
next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
compiler: {
styledComponents: true,
},
};
module.exports = nextConfig;
Run Code Online (Sandbox Code Playgroud)
pages/api/auth/[...nextauth].ts
import CognitoProvider from "next-auth/providers/cognito";
import NextAuth, { NextAuthOptions, Session } from "next-auth";
import {
AuthFlowType,
CognitoIdentityProviderClient,
InitiateAuthCommand,
} from "@aws-sdk/client-cognito-identity-provider";
import { JWT } from "next-auth/jwt";
const COGNITO_AWS_REGION = process.env.COGNITO_AWS_REGION;
const COGNITO_POOL_ID = process.env.COGNITO_POOL_ID;
const COGNITO_CLIENT_ID = process.env.COGNITO_CLIENT_ID;
const COGNITO_CLIENT_SECRET = process.env.COGNITO_CLIENT_SECRET;
const NEXTAUTH_SECRET = process.env.NEXTAUTH_SECRET;
const NEXTAUTH_URL = process.env.NEXTAUTH_URL;
if (!COGNITO_AWS_REGION) throw new Error("REGION is not set");
if (!COGNITO_CLIENT_ID) throw new Error("COGNITO_CLIENT_ID is not set");
if (!COGNITO_POOL_ID) throw new Error("COGNITO_USER_POOL_ID is not set");
if (!COGNITO_CLIENT_SECRET) throw new Error("COGNITO_CLIENT_SECRET is not set");
if (!NEXTAUTH_SECRET) throw new Error("NEXTAUTH_SECRET is not set");
if (!NEXTAUTH_URL) throw new Error("NEXTAUTH_URL is not set");
const refreshCognitoAccessToken = async (token: JWT) => {
const client = new CognitoIdentityProviderClient({
region: COGNITO_AWS_REGION,
});
const command = new InitiateAuthCommand({
AuthFlow: AuthFlowType.REFRESH_TOKEN_AUTH,
ClientId: COGNITO_CLIENT_ID,
AuthParameters: {
REFRESH_TOKEN: token.refreshToken as string,
},
});
const response = await client.send(command);
return response.AuthenticationResult;
};
export const authOptions: NextAuthOptions = {
secret: NEXTAUTH_SECRET,
// @ts-expect-error -- this property is not documented properly
site: NEXTAUTH_URL,
providers: [
CognitoProvider({
clientId: COGNITO_CLIENT_ID!,
issuer: `https://cognito-idp.${COGNITO_AWS_REGION}.amazonaws.com/${COGNITO_POOL_ID!}`,
clientSecret: process.env.COGNITO_CLIENT_SECRET!,
}),
],
callbacks: {
jwt: async ({ token, account, user }) => {
// Initial sign in
if (account && user) {
return {
// save token to session for authenticating to AWS
// https://next-auth.js.org/configuration/callbacks#jwt-callback
accessToken: account.access_token,
accessTokenExpires: account.expires_at
? account.expires_at * 1000
: 0,
refreshToken: account.refresh_token,
user,
};
}
// Return previous token if the access token has not expired yet
if (Date.now() < (token as unknown as Session).accessTokenExpires) {
return token;
}
// Access token has expired, try to update it
const refreshedTokens = await refreshCognitoAccessToken(token);
return {
...token,
accessToken: refreshedTokens?.AccessToken,
accessTokenExpires: refreshedTokens?.ExpiresIn
? Date.now() + refreshedTokens?.ExpiresIn * 1000
: 0,
refreshToken: refreshedTokens?.RefreshToken ?? token.refreshToken, // Fall back to old refresh token
};
},
session: async ({ session, token }) => {
if (!session?.user || !token?.accessToken) {
console.error("No accessToken found on token or session");
return session;
}
session.user = token.user as Session["user"];
session.accessToken = token.accessToken as string;
session.error = token.error as string | undefined;
return session;
},
redirect: async ({ url, baseUrl }) => {
// allows any url
if (url.startsWith("/")) return `${baseUrl}${url}`;
return url;
},
},
};
export default NextAuth(authOptions);
Run Code Online (Sandbox Code Playgroud)
好吧,我最终自己弄清楚了,事实证明我没有正确读取我的 nginx 日志,一旦我这样做了,这并不是什么太难的事情......这就是我所做的:
502 错误很可能与upstream sent too big header while reading response header from upstream该请求的 nginx 错误日志中的错误相对应。要解决此问题,请将其添加到您的配置中,/etc/nginx/nginx.conf在http {... } ...部分下
proxy_buffers 8 16k;
proxy_buffer_size 32k;
Run Code Online (Sandbox Code Playgroud)
(在这里找到这个解决方案:/sf/answers/2713082781/)
上面的内容应该可以解决 502 错误,但是您可能仍然会遇到 next-auth 错误,这里有一些我在尝试解决此问题时发现的其他可以尝试的事情......
在 AWS Cognito 中,尝试在 AWS Cognito 中创建并使用一个新的“应用程序客户端”,而不需要客户端密钥。
如果您现在运行它,您将收到诸如signin?error=OAuthCallback和 之类的错误client_secret_basic client authentication method requires a client_secret。因此,您还需要将 cognito 的配置更新 pages/api/auth/[...nextauth].ts为...
CognitoProvider({
clientId: COGNITO_CLIENT_ID,
issuer: `https://cognito-idp.${COGNITO_AWS_REGION}.amazonaws.com/${COGNITO_POOL_ID!}`,
clientSecret: "someString",
client: {
token_endpoint_auth_method: "none",
},
})
Run Code Online (Sandbox Code Playgroud)
我从这次讨论中发现:https ://github.com/nextauthjs/next-auth/issues/2524
注意:如果您从 Cognito 收到类似错误的信息,redirect_mismatch则意味着您尚未在 AWS Cognito 客户端应用程序设置中正确更新 URL,这是我在本地和实时调试之间切换时经常发现的情况。