인증
nextAuth 관련
Next.js는 Serverless를 지원하도록 처음부터 설계되었습니다. NextAuth.js를 사용하여 인증을 수행하는 것이 정말 간단합니다. next-auth의 공식문서를 기반으로 토큰, 세션방식 인증 소스코드를 기술합니다.
1. NextAuth 왜쓸까요?
1-1. 유연합니다.
- 모든 OAuth 서비스와 함께 작동하도록 설계되었으며 OAuth 1.0, 1.0A, 2.0 및 OpenID Connect를 지원합니다.
- 다양한 인기 있는 로그인 서비스 에 대한 기본 지원
- 이메일/비밀번호 없는 인증 지원
- 모든 백엔드 (Active Directory, LDAP 등) 를 사용하여 상태 비저장 인증을 지원합니다.
- JSON 웹 토큰과 데이터베이스 세션을 모두 지원합니다.
- Serverless용으로 설계되었지만 어디에서나 실행 가능(AWS Lambda, Docker, Heroku 등)
1-2. 데이터베이스와 함께 사용하거나 데이터베이스 없이도 사용할 수 있습니다.
- URL을 직접 사용할 경우 URL이 변경되거나 CDN(content delivery network)이 서비스에 바로 반영되어 에러가 생길 수 있습니다.
- 데이터를 제어할 수 있는 오픈 소스 솔루션
- BYOD(Bring Your Own Database)를 지원하며 모든 데이터베이스와 함께 사용할 수 있습니다.
- MySQL, MariaDB, Postgres, SQL Server, MongoDB 및 SQLite 에 대한 기본 지원
- 인기 있는 호스팅 제공업체의 데이터베이스와 잘 작동합니다.
- 데이터베이스 없이도 사용 가능 (예: OAuth + JWT)
- (feat) 이메일 로그인을 위해서는 일회용 확인 토큰을 저장하기 위한 데이터베이스를 구성해야 합니다.
1-3. 보안이 우수합니다.
- 비밀번호 없는 로그인 메커니즘 사용을 장려합니다
- 기본적으로 보안이 유지되도록 설계되었으며 사용자 데이터를 보호하기 위한 모범 사례를 장려합니다.
- POST 경로에서 크로스 사이트 요청 위조 토큰 사용(로그인, 로그아웃)
- 기본 쿠키 정책은 각 쿠키에 적합한 가장 제한적인 정책을 목표로 합니다.
- JSON 웹 토큰이 활성화되면 기본적으로 A256GCM으로 암호화됩니다(JWE)
- 개발자의 편의를 위해 대칭 서명 및 암호화 키를 자동 생성합니다.
- 단기 세션을 지원하기 위한 탭/창 동기화 및 keepalive 메시지 기능이 있습니다.
- Open Web Application Security Project 에서 발행한 최신 지침을 구현하려고 시도합니다.
- 고급 옵션을 사용하면 JSON 웹 토큰을 인코딩 및 디코딩하기 위해 어떤 계정에 로그인이 허용되는지 제어하는 루틴을 직접 정의하고, 사용자 지정 쿠키 보안 정책과 세션 속성을 설정하여 누가 로그인할 수 있는지와 세션을 얼마나 자주 재검증해야 하는지 제어할 수 있습니다.
2. nextAuth 토큰 기반
2.1 라이브러리 설치
타입스크립트 쓰시더라도 next-auth package 안에 타입 정의 되어 있어서 안심하셔도 됩니다.
명령어에
npm install next-auth
또는
yarn add next-auth
또는
pnpm add next-auth
입력합니다.
2.2 API route 추가
next 13.2 이상 기준으로 app/ 경로에다가 해당 파일은 추가합니다. 역할은 dynamic route handler 역할입니다. 그리고 기본적인 전역 config 설정입니다.
dynamic route handler 정의가 헷갈리시면 검색해 보셔요.
app/api/auth/[...nextauth].js 추가하세요.
import NextAuth from "next-auth"
import GithubProvider from "next-auth/providers/github"
export const authOptions = {
// Configure one or more authentication providers
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
// ...add more providers here
],
}
export default NextAuth(authOptions)
위 파일을 추가하면 /api/auth/* 를 거치는 모든 요청은 해당 파일에의해 자동으로 handle 됩니다.
2.3 authOptions 관련
환경변수들
- NEXTAUTH_URL
OAuth 제공자와의 인증 과정에서 올바른 콜백 URL을 설정합니다. 이메일 인증을 위한 링크를 생성할 때 올바른 URL을 포함합니다. NextAuth.js의 API 경로(예: /api/auth)를 올바르게 처리합니다.
NEXTAUTH_URL=https://example.com
- NEXTAUTH_SECRET
NextAuth.js JWT를 암호화하고 이메일 인증 토큰을 해시하는 데 사용됩니다. 이는 NextAuth와 미들웨어에서 secret 옵션의 기본 값입니다.
- NEXTAUTH_URL_INTERNAL
만약 제공된다면, 서버 측 호출은 NEXTAUTH_URL 대신 이것을 사용합니다. 서버가 사이트의 정규 URL에 접근할 수 없는 환경에서 유용합니다. 기본값은 NEXTAUTH_URL입니다.
NEXTAUTH_URL_INTERNAL=http://10.240.8.16
Options
사용자가 자신이 선호하는 기존 로그인으로 로그인할 수 있도록 하는 OAuth 정의입니다. (e.g. Google, Facebook, Twitter, GitHub, Email, etc)
- providers
- default value : []
- Required : Yes
- secret
- Default value : string
- Required : Yes, in production
NEXTAUTH_SECRET 세팅했으면 정의할 필요 없어요~
- session
- Default value : obejct
- Required : No
초기값은 다음과 같습니다.
session: {
// Choose how you want to save the user session.
// The default is `"jwt"`, an encrypted JWT (JWE) stored in the session cookie.
// If you use an `adapter` however, we default it to `"database"` instead.
// You can still force a JWT session by explicitly defining `"jwt"`.
// When using `"database"`, the session cookie will only contain a `sessionToken` value,
// which is used to look up the session in the database.
strategy: "database",
// Seconds - How long until an idle session expires and is no longer valid.
maxAge: 30 * 24 * 60 * 60, // 30 days
// Seconds - Throttle how frequently to write to database to extend a session.
// Use it to limit write operations. Set to 0 to always update the database.
// Note: This option is ignored if using JSON Web Tokens
updateAge: 24 * 60 * 60, // 24 hours
// The session token is usually either a random UUID or string, however if you
// need a more customized session token string, you can define your own generate function.
generateSessionToken: () => {
return randomUUID?.() ?? randomBytes(32).toString("hex")
}
}
- jwt
JSON Web Tokens(JWT)는 session: { strategy: "jwt" } 옵션을 활성화하면 세션 토큰으로 사용할 수 있습니다.
- Default value : obejct
- Required : No
초기값은 다음과 같습니다.
jwt: {
// The maximum age of the NextAuth.js issued JWT in seconds.
// Defaults to `session.maxAge`.
maxAge: 60 * 60 * 24 * 30,
// You can define your own encode/decode functions for signing and encryption
async encode() {},
async decode() {},
}
- pages
사용자 정의 로그인, 로그아웃 및 오류 페이지를 만들려는 경우 사용할 URL을 지정하세요.
- Default value : {}
- Required : No
pages: {
signIn: '/auth/signin',
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)
}
- callbacks
주어진 작업이나 이벤트가 완료된 후에 실행됩니다. 비동기적으로 작동하여 프로그램의 실행 흐름을 방해하지 않습니다.
- Default value : {}
- Required : No
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
return true
},
async redirect({ url, baseUrl }) {
return baseUrl
},
async session({ session, token, user }) {
return session
},
async jwt({ token, user, account, profile, isNewUser }) {
return token
}
}
- events
- Default value : object
- Required : No
이러한 이벤트 핸들러들은 사용자 인증과 관련된 다양한 작업들을 처리합니다.
events: {
async signIn(message) { /* on successful sign in */ },
async signOut(message) { /* on signout */ },
async createUser(message) { /* user created */ },
async updateUser(message) { /* user updated - e.g. their email was verified */ },
async linkAccount(message) { /* account (e.g. Twitter) linked to a user */ },
async session(message) { /* session is active */ },
}
이외에도 몇가지 속성이 더 있지만 자주 쓰이는 것 위주로 기술하고 넘어갑니다. 공부는 꼬리에 꼬리를 물어서 깊게 파다보면 한도끝도 없으니 어느 적정선에서 끊어야 합니다.
2.4 세션 유지
useSession 을 사용하기 위해서는 <SessionPrivider /> 를 application 루트에다가 노출시켜야 합니다.
import { SessionProvider } from 'next-auth/react'
export default function App({
Component,
pageProps: { session, ...pageProps },
}) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
)
}
2.5 프론트 엔드 - React Hook 추가
useSession React Hook은 어떠한 누군가가 로그인 했는지 안했는지 체크하기 위한 가장 쉬운 방법입니다.
import { useSession, signIn, signOut } from 'next-auth/react'
export default function Component() {
const { data: session } = useSession()
if (session) {
return (
<>
Signed in as {session.user.email} <br />
<button onClick={() => signOut()}>Sign out</button>
</>
)
}
return (
<>
Not signed in <br />
<button onClick={() => signIn()}>Sign in</button>
</>
)
}
2.6 백엔드 - API Route
API Route 를 보호하기 위해서 getServerSession() 함수를 사용할 수 있습니다.
import { getServerSession } from 'next-auth/next'
import { authOptions } from './auth/[...nextauth]'
export default async (req, res) => {
const session = await getServerSession(req, res, authOptions)
if (session) {
res.send({
content:
'This is protected content. You can access this content because you are signed in.',
})
} else {
res.send({
error:
'You must be signed in to view the protected content on this page.',
})
}
}
2.7 확장성
로그인 과정에서 서버측에서 생성된 값을 클라이언트 측으로 전달하는 방법을 설명합니다.
...
callbacks: {
async jwt({ token, account }) {
// Persist the OAuth access_token to the token right after signin
if (account) {
token.accessToken = account.access_token
}
return token
},
async session({ session, token, user }) {
// Send properties to the client, like an access_token from a provider.
session.accessToken = token.accessToken
return session
}
}
...
Now whenever you call getSession or useSession, the data object which is returned will include the accessToken value.
import { useSession, signIn, signOut } from 'next-auth/react'
export default function Component() {
const { data } = useSession()
const { accessToken } = data
return <div>Access Token: {accessToken}</div>
}
2.8 Configuring callback URL (OAuth only)
OAuth 제공자 설정: 제공자(예: Google, Facebook 등)를 사용하는 경우, 제공자의 설정에서 **콜백 URL(callback URL)**을 구성해야 합니다. 콜백 URL은 사용자가 인증을 완료한 후 제공자가 리디렉션할 URL입니다. 이 과정은 OAuth 제공자의 설정을 통해 콜백 URL을 정확히 설정하고, 해당 URL을 통해 인증 프로세스를 완료할 수 있도록 하는 것입니다.
3 Client API 정리
아래의 library 들은 session과 상호작용을 쉽게하기 위해 제공되는 API 입니다.