Auth.js

Auth.js에 대해서 알아 봅니다.

2023-03-03

Auth.js

배경

Vercel은 next.js 인증 처리 패키지 next-auth를 다른 메타 프레임워크에서 사용할 수 있도록 auth.js라는 이름으로 제공하고 있습니다.

Authentication | Next.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

참고: OAuth authentication | Auth.js

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 이벤트)

참고: Spring Security_CSRF Token의 개념과 사용 방법

주요 절차 입니다.

  • 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의 값을 사용합니다.
  • 플러스(+)를 공백( )으로 변환합니다. Query parameter value가 +를 포함할 가능성이 있을 경우 btoa로 변환하고, 복호할 때는 atob를 사용합니다.

takeaway: Session storage take away

localStorage와 sessionStorage

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 값을 사용하여 알아냅니다.

Loading script...