이번에 플젝을 next 를 쓰는 플젝을 진행했는데 다른건 그냥 만들면 됐는데 처음 가장 발목잡았던 next auth...
설정다하면 편하기는 편한데 설정이 복잡하다...
우선 next 디렉토리 구소를 api 형식이다. pages 라면 구글링해보면 다른 예제가 많아서 구글링을 추천한다.
NextAuth.js
Authentication for Next.js
next-auth.js.org
여기서 npm install 해주고 폴더구조를 우선
api/auth/[...nextauth]/route.js 를 정한다. ts 안쓰는 이유는 내가 안쓰는거까지도 타입다 지정해야하고 너무 불편하다
구글 애플/ 네이버 카카오 리턴이 다른데 콜백에서 접근할때 없는거니까 접근안된다고 에러까지 뜬다..
이런 에러 헨들링 가능하면 ts 를 추천한다.
import NextAuth, { AuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import AppleProvider from "next-auth/providers/apple";
import KakaoProvider from "next-auth/providers/kakao";
import NaverProvider from "next-auth/providers/naver";
import CredentialsProvider from "next-auth/providers/credentials";
import { BASE_URL, JWT_SECRET } from "@/util/path";
import jwt from 'jsonwebtoken';
import { SignJWT } from "jose";
import { createPrivateKey, KeyObject } from "crypto";
const authOptions = {
session: {
strategy: "jwt",
maxAge: 60 * 60 * 24 * 7, // 31일
updateAge: 60 * 60 * 24, // 1일
},
cookies: {
pkceCodeVerifier: {
name: "next-auth.pkce.code_verifier",
options: {
httpOnly: true,
sameSite: "none",
path: "/",
secure: true,
},
},
},
pages: {
signIn: "/next/login", // 로그인 페이지
signOut: "/next/signout", // 로그아웃 페이지
error: "/next/api/auth/error", // 오류 페이지
verifyRequest: "/next/api/auth/verify-request", // 이메일 확인 페이지
},
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
username: { label: "Username", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
// 로그인 인증 처리 추가해야함
const response_j = await response.json();
let user = { id: null, name: null, email: null };
if (response_j?.result) {
user = { id: credentials.username, name: null, email: response_j._email };
}
if (user.id) {
return user;
} else {
return null;
}
},
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_KEY,
}),
AppleProvider({
clientId: process.env.APPLE_ID,
clientSecret: await getAppleToken(),
}),
KakaoProvider({
clientId: process.env.KAKAO_API_KEY,
clientSecret: process.env.KAKAO_SCRIPT_KEY,
}),
NaverProvider({
clientId: process.env.NAVER_API_KEY,
clientSecret: process.env.NAVER_CLIENT_SECRET,
}),
],
secret: process.env.NEXTAUTH_SECRET,
callbacks: {
async signIn({ user, account, profile, email, credentials, context }) {
console.log("======signIn======");
if (account.provider === "apple" || account.provider === "naver" || account.provider === "kakao" || account.provider === "google") {
// 1. 가입 유무 체크 user.id
const response_j = await response.json();
if (response_j.result) {
// 3. 가입 되어있으면 로그인 완료
// console.log("regiCheck > response_j:: ", response_j)
} else {
// 2. 가입 안되면 간편가입으로 (필요 데이터 넘기기)
// 전화번호, 아이디, 이메일, 이름, 닉네임, 소셜로그인,
let nickname = null
let phonenumber = null
if (account.provider === "kakao") {
nickname = profile.properties.nickname
phonenumber = profile.kakao_account.phone_number
if (phonenumber) {
phonenumber = phonenumber.replace('+82', '0').replace(/[\s-]+/g, "").replace(/[^0-9]*/s, '');
}
} else if (account.provider === "naver") {
nickname = profile.response.nickname
phonenumber = profile.response.mobile
if (phonenumber) {
phonenumber = phonenumber.replace('+82', '0').replace(/[\s-]+/g, "").replace(/[^0-9]*/s, '');
}
}
// 여기서 로그인 안되어있으니까 다른 페이지로 이동
}
}
if (account.provider === "credentials") {
}
return true;
},
async jwt({ token, trigger, session, user}) {
console.log("======jwt======");
let uToken = null;
if (token?.uId !== null && token?.uId !== undefined) {
uToken = jwt.sign({
username: token.username,
}, JWT_SECRET, { expiresIn: '5m' });
token.uToken = uToken;
}
if (token?.sub) {
console.log("======jwt setUserData======");
const response_j = await response.json();
// console.log("jwt > > response_j: ",response_j)
if (response_j?.uData) {
// 여기서 로그인 성공시 추가 테이터 지정
token.username = response_j?.uData?.username;
}
}
return token;
},
async session({ session, token, trigger, newSession }) {
// console.log("======session======");
// 토큰으로 지정한애들이 token. 안에 다 들어있어서 세션에 추가해야지 우리가 접근할수있다
if (token.username !== undefined) {
session.user.username = token.username;
}
return session;
},
},
};
// 이건 애플로그인 할때 필요한건데 그냥 넣자
async function getAppleToken() {
const key = await process.env.APPLE_SECRET;
const privateKey = createPrivateKey({
key: key,
format: "pem",
type: "pkcs8",
});
const appleToken = await new SignJWT({})
.setAudience("https://appleid.apple.com")
.setIssuer(process.env.APPLE_TEAM_ID)
.setIssuedAt(Math.floor(Date.now() / 1000))
.setExpirationTime(Math.floor(Date.now() / 1000) + 3600 * 2)
.setSubject(process.env.APPLE_ID)
.setProtectedHeader({
alg: "ES256",
kid: process.env.APPLE_KEY_ID,
})
.sign(privateKey);
return appleToken;
}
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
이렇게 코드를 크게 짤수있다. 쿠키에 pkceCodeVerifier 이거는 애플로그인할때 필요하다. 중요 코드는 다빼서 알아서 추가해서 쓰자
pages 에 /next 를 다 붙이는 이유는
next config 에
basePath: '/next',
assetPrefix: '/next/',
이걸 추가해서 넣었다
그리고 나처럼 로그인 다되고 user 를 뜯어고치고 싶으면 app/type/next-auth.d.ts 를 추가하자
import NextAuth, { DefaultSession } from "next-auth"
declare module "next-auth" {
/**
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
*/
interface Session {
// 추가할 세션 항목
user: {
username: string | null,
}
// & DefaultSession["user"] // 기존 세션 항목 유지
}
interface User {
}
}
이렇게 추가하면 된다
그리고 api 의 루트 layout 구조도 변경해야하는데
import React from "react";
import "./globals.css";
import { NextAuthProvider } from "./providers";
export default function RootLayout({ children,}: { children: React.ReactNode; }) {
return (
<html >
<body>
<div className=" bg-white">
<NextAuthProvider>
{children}
</NextAuthProvider>
</div>
</body>
</html>
);
}
이렇게 수정하고 NextAuthProvider 는
"use client";
import { SessionProvider } from "next-auth/react";
import React from "react";
type Props = {
children?: React.ReactNode;
};
export const NextAuthProvider = ({ children }: Props) => {
return <SessionProvider basePath="/next/api/auth">{children}</SessionProvider>;
};
이렇게 설정하자
env 설정은 다른거는 다 구글링하면서 알아서 하겠지만
NEXTAUTH_SECRET
JWT_SECRET
NEXTAUTH_URL="만약에 나처럼 url 건들었음 https://도메인명/next/api/auth/ 이런식으로 지정해야한다 "
APPLE_SECRET="-----BEGIN PRIVATE KEY-----\n여기에 키 내용 처음 열었을때 줄바꿈마다\n 을 붙여야함\n-----END PRIVATE KEY-----\n"
이거는 꼭 확인해서 적어주자
'코딩 공부 > REACT' 카테고리의 다른 글
[REACT] - 숫자만 받기 (보완점 많음) (0) | 2022.03.06 |
---|---|
[React] - 6차 - props 반복 학습 (0) | 2022.02.18 |
[React] - 5차 - 조건 렌더링 (0) | 2022.02.18 |
[React] - 4차 -겹치는 코드 뭉쳐서 관리 (0) | 2022.02.18 |
[React] - 3차 - useEffect 사용 예 (0) | 2022.02.18 |