Auth.js
배경
Vercel은 next.js 인증 처리 패키지 next-auth
를 다른 메타 프레임워크에서 사용할 수 있도록 auth.js라는 이름으로 제공하고 있습니다.
다음과 같은 인증 방식을 지원합니다.
- OAuth: github, google, facebook 등 OAuth 인증 서비스를 사용하여 로그인
- Email: magic link를 사용자 이메일로 전송하여 로그인
- Credential: 아이디/암호 방식의 로그인
빌드
src
폴더에 있는 ts 파일의 컴파일 결과를 패키지 루트에 생성합니다.
pnpm build
명령 후에도 이를 바로 눈치챌 수 없었는데.
.vscode/settings.json
파일에서 산출물을 files.exclude
처리 했기 때문입니다.
관련 설정을 수정하면 산출물을 확인할 수 있습니다.
next-auth/react
auth.js라는 범용적인 이름을 가졌지만 패키지 이름은 next-auth
입니다.
이중 가장 많이 사용하는 형태는 아무래도 next-auth/react
이겠죠.
OAuth tutorial
Github를 사용하는 OAuth tutorial을 실펴 봅니다.
next-auth/react
는 FE단 라이브러리 입니다. 로그인, 로그아웃, 세션 관리를 수행합니다.next-auth/next
는 next.js 기반의 서버단 라이브러리 입니다.session
,signin
,callback
등의 라우트를 처리합니다.
next.js의 BE 코드를 작성하는 pages/api
폴더를 통해 auth.js가 라우트를 처리하도록 코드를 작성합니다.
이떄, Github OAuth 앱의 client id, secret 등을 설정합니다.
FE에서는 SessionProvider
로 애플리케이션에 세션 정보 켄텍스트를 제공하고 useSession
으로 사용합니다.
signin
, singout
과 같은 라이브러리를 사용하여 사용자 엑션에 따라 로그인, 로그아웃을 구현합니다.
SessionProvider
는 세션 정보를 하위 컴포넌트에 전달합니다.
/api/auth/session
API 요청을 mount와 동시에 요청합니다.
다음과 같은 경우 세션 정보를 업데이트 합니다.
- 새로운 탭을 열 경우
- 다른 탭에서 로그아웃
- 사용자가 페이지를 foreground로 활성화(visibilitychange 이벤트)
주요 절차 입니다.
SessionProvider
는/api/auth/session
요청으로 사용자 정보를 가져옵니다.next-auth.csrf-token
을 쿠키로 설정하여 요청에 대한 CSRF 위협을 회피합니다.signin
함수를 호출하면/api/auth/signin
요청으로 로그인 페이지를 보여 줍니다.- 로그인 페이지에서 사용자가 액션을 하면, form submit으로 POST
/api/auth/signin/github
요청을 합니다. - github 인증 URL을 생성하여 브라우저 redirect를 유도합니다.
https://github.com/login/oauth/authorize?client_id={CLIENT ID}&scope=read%3Auser%20user%3Aemail&response_type=code&redirect_uri={CALLBACK URL}&state={STATE}
- github에서는 인증 처리 후, callback 파라미터로 넘겨진 값을 기준으로 다시 redirect 합니다.
next-auth.session
을 쿠키로 설정하여 사용자 정보를 저장합니다.
takeaway: literial code assist
Well known literal type의 code assist를 지원하면서, string
형식도 지원하는 definition util.
/**
* Util type that matches some strings literally, but allows any other string as well.
* @source https://github.com/microsoft/TypeScript/issues/29729#issuecomment-832522611
*/
export type LiteralUnion<T extends U, U = string> =
| T
| (U & Record<never, never>);
takeaway: URLSearchParams - Web APIs | MDN
표준 URLSearchParams
API를 사용하여 URL을 처리합니다.
- Duplicated search parameter는
getAll
로 가져올 수 있습니다. - Base url을 파싱하지 않으니
new Url('...').search
의 값을 사용합니다. - 플러스(
+
)를 공백(+
를 포함할 가능성이 있을 경우btoa
로 변환하고, 복호할 때는atob
를 사용합니다.
takeaway: Session storage take away
http 요청 시, 항상 쿠키를 포함하기 때문에 데이터가 커지면 대역폭을 점유합니다.
sessionStorage
는 현재 떠 있는 탭 내에서만 유지합니다.
iframe
은 같은 값을 공유합니다.
새로고침을 해도 유지하지만, 탭을 닫으면 사라집니다.
localStorage
, sessionStorage
변경 시, storage
를 생성합니다.
auth.js이 기능을 이용해서 BroadcastChannel
poliyfill을 사용합니다.
BroadcastChannel
API를 사용하여 tab 간에 다음과 같은 이벤트를 전달합니다.
getSession
:/api/auth/session
API 요청을 사용하여 session 정보를 업데이트 하는 이벤트- 여러 탭 중에서 세션 정보 갱신이 이루어지면, 이벤트를 발생하고, 각 탭은 이를 받아서 각자
/api/auth/session
API 요청을 하여 세션 정보를 업데이트 합니다.
- 여러 탭 중에서 세션 정보 갱신이 이루어지면, 이벤트를 발생하고, 각 탭은 이를 받아서 각자
signout
: sign out 완료 이벤트- 여러 탭 중에서 sing out을 하면, 이를 각 탭에서 처리할 수 있습니다.
takeaway: CSRF token 관리
next-auth.csrf-token
쿠키 값을 사용합니다.
next-auth.csrf-token
쿠키에 값이 없을 경우 새로운 값을 생성합니다.
32 바이트 랜덤 변수의 hex 값 스트링을 토큰 값으로 사용합니다.
설정을 통해 제공하는 별도의 secret 값(NEXTAUTH_SECRET
환경 변수로 설정)과 함께 sha256
으로 인코딩합니다.
|
를 구분자를 사용하여 ${csrfToken}|${csrfTokenHash}
형식으로 쿠키값을 설정합니다.
next-auth.csrf-token
쿠키에 값이 존재하는 경우 검증을 합니다.
|
로 스플릿 한뒤, csrfToken
, csrfTokenHash
값을 확보합니다.
쿠키에 설정되어 있는 csrfToken
값을 사용하여 secret 값과함께 hash를 만들어 csrfTokenHash
값과 일치하는지 확인합니다.
POST 메소드는 token 검증이 된 경우에만 허용하는 방식입니다.
takeaway: 요청 URL 알아내기
full request url을 header의 x-forwarded-proto
, x-forwarded-host
값을 사용하여 알아냅니다.