사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
일반▾
인터페이스▾
관측성▾
성능▾
네이티브▾
개발▾

소셜 로그인

SSO는 비밀번호 대신 GitHub, Google, Facebook, Kakao, Naver 계정으로 로그인하게 해 줍니다. libs/shared를 쓰는 앱이라면 route와 로그인 로직은 이미 있습니다. 키와 버튼, 그리고 사용자가 도착할 화면만 준비하면 됩니다.
이 페이지에서 쓰는 말
provider
사용자가 누구인지 확인해 주는 서비스입니다. GitHub, Google, Facebook, Kakao, Naver, Apple이 있습니다.
callback
provider가 사용자를 되돌려 보내는 route입니다. 일회용 code를 함께 가져옵니다.
accountId
로그인할 때마다 사용자를 가려내는 값 하나입니다. email이나 GitHub username입니다.
prepare user
처음 온 사용자를 위해 만든 prepare 상태의 user입니다. 가입 화면이 이 user를 완성합니다.
할 일
  1. 키를 넣습니다. provider마다 받은 client ID와 secret을 서버 env 파일의 security.sso에 적습니다.
  2. redirect URI를 등록합니다. 각 provider의 개발자 콘솔에 <origin>/api/user/<provider>/callback을 적습니다.
  3. 버튼을 놓습니다. 로그인 화면에 User.Util.SSOButtons를 그리고 이동할 곳 세 군데를 넘깁니다.
  4. 도착 화면을 만듭니다. query에서 userId를 읽는 가입 화면과 error를 읽는 오류 화면입니다.
사용자가 버튼을 누르면
  1. st.do.ssoSigninUser가 이동할 곳 세 군데와 현재 origin을 cookie에 저장하고 /api/user/<provider>를 엽니다.
  2. 시작 route가 브라우저를 provider의 동의 화면으로 보냅니다.
  3. provider가 code를 붙여 브라우저를 /api/user/<provider>/callback으로 돌려보냅니다.
  4. callback이 code를 profile로 바꾸고 accountId를 골라냅니다.
  5. userService.handleSsoCallback이 로그인시키거나, 가입을 이어가게 하거나, 오류 화면으로 보냅니다.

provider 등록

provider마다 개발자 콘솔에서 client ID와, 대개는 secret도 발급해 줍니다. 환경마다 있는 서버 env 파일의 security.sso에 적습니다:
apps/koyo/env/env.server.local.ts
  • 적은 provider만 켜집니다. 여기에 없는 provider는 SSO.<Provider> guard가 두 route를 모두 ssoNotConfigured로 거절합니다.
  • 키는 git에 올라가지 않습니다. env.server.local.ts, env.server.main.ts 같은 환경별 파일은 gitignore 대상입니다.
  • security를 적으면 객체 전체가 바뀝니다. verifies와, 쓴다면 jwtSecret도 sso 옆에 함께 적습니다.
키 항목
clientIDstring필수
provider 콘솔에서 받은 앱의 client ID입니다. Kakao에서는 REST API 키입니다.
clientSecretstring
code를 토큰으로 바꿀 때 함께 보냅니다. 값이 있을 때만 보냅니다.
teamIDstringapple
Apple 개발자 팀 ID입니다. Apple용 client secret의 발급자로 들어갑니다.
keyIDstringapple
Sign in with Apple 키의 ID입니다. secret의 kid로 들어갑니다.
keyFilePathstringapple
그 키의 개인 키 파일 경로입니다. Akan이 이 파일로 client secret을 서명합니다.
콘솔에 등록할 redirect URI
provider는 등록해 둔 URI로만 사용자를 돌려보냅니다. Akan은 사용자가 버튼을 누른 화면의 origin으로 이 URI를 만듭니다:
https://<your-domain>/api/user/<provider>/callback
  • origin마다 등록합니다. origin을 브라우저에서 가져오므로 로컬, 스테이징, 운영에 각각 항목이 필요합니다.
  • 아래 scope를 허용해 둡니다. 콘솔에서 켜지 않은 동의 항목은 비어서 돌아옵니다. Kakao의 email이 대표적입니다.
security.sso 키Akan이 요청하는 scope
githubuser
googleemail profile
facebookemail
kakaoaccount_email,profile_nickname
naver(없음)

callback 작성

직접 작성할 일은 드뭅니다. libs/shared/lib/user/user.signal.ts에 provider마다 시작 route와 callback이 한 쌍씩 이미 있습니다. provider를 추가하거나 로그인 뒤의 동작을 바꿀 때 참고합니다.
endpoint경로
↳ 하는 일
google/api/user/google
브라우저를 Google 동의 화면으로 보냅니다.
googleCallback/api/user/google/callback
Google의 code를 profile로 바꾼 뒤 로그인하거나 가입을 이어갑니다.
실제로 들어 있는 Google 쌍입니다. 다른 provider도 모양이 같습니다:
libs/shared/lib/user/user.signal.ts
여기 쓰인 조각들입니다. user service의 method인 handleSsoCallback을 빼면 모두 @libs/shared/srvkit에서 가져옵니다:
SSO.GoogleSSO.GithubSSO.Kakao…
그 provider의 키가 없으면 ssoNotConfigured로 호출을 거절하는 guard입니다.
makeOAuthRedirectResponse
ssoOrigin cookie로 provider 동의 화면행 302 응답을 만듭니다.
getSsoCodegetSsoOrigin
code query와 ssoOrigin cookie를 읽고, 둘 중 하나라도 없으면 오류를 던집니다.
extractGoogleProfileextractGithubProfile…
provider마다 하나씩 있습니다. code를 토큰으로 바꾸고 profile을 가져옵니다.
handleSsoCallback
user service가 로그인, 가입 계속, 오류 중 하나를 정합니다. { cookie, redirect }를 돌려줍니다.
makeSsoRedirectResponse
redirect로 가는 마지막 302 응답입니다. 세션 cookie가 있으면 함께 심습니다.
  • callback은 작게 둡니다. profile을 accountId와 닉네임으로 바꾸기만 하고, 판단은 모두 handleSsoCallback이 합니다.
  • Apple은 아직 연결되어 있지 않습니다. SSO.Apple과 Apple 키 항목은 있지만, 들어 있는 apple과 appleCallback은 아무 일도 하지 않습니다. verifyAppleUser로 직접 만듭니다.

accountId 맞추기

provider마다 사용자를 부르는 이름이 다릅니다. callback은 service를 부르기 전에 profile을 accountId 하나로 맞추고, 그 뒤로는 이 값이 사용자를 가려냅니다.
provideraccountId닉네임 재료
githubusernamedisplayName
googleemails[0].valuedisplayName
facebookemails[0].valuegivenName familyName
kakaoemailname
naveremailname
callback을 직접 쓴다면, 차이를 한곳에 모아 둡니다:
apps/koyo/srvkit/accountIdOf.ts
  • 계정 하나에 provider 하나입니다. 이미 있는 accountId가 한 번도 쓰지 않은 provider로 들어오면 noVerifiesInUser와 함께 오류 화면으로 갑니다.
  • GitHub와 Google은 서로 다른 사용자가 됩니다. 같은 사람이라도 username과 email은 일치하지 않기 때문입니다.
  • 닉네임은 초안입니다. 처음 온 사용자는 profile 이름을, 비어 있으면 accountId의 @ 앞부분을 받습니다. 12자로 자르고 겹치지 않게 만듭니다.

callback 이후 이동

callback은 언제나 세 화면 중 하나에서 끝나고, 세 곳 모두 로그인 버튼에 미리 적어 둡니다:
apps/koyo/page/signin.tsx
결과조건이동
로그인accountId가 active, restricted, dormant 사용자 중 하나의 것입니다.signinRedirect
가입 계속아직 없는 사용자라서, 겹치지 않는 닉네임을 붙인 prepare user를 만듭니다.signupRedirect?userId=<id>
오류로그인이나 가입 준비가 실패합니다. noVerifiesInUser가 한 예입니다.errorRedirect?error=<error key>
SSOButtons props
signinRedirectstring필수
기존 사용자가 로그인된 채로 도착하는 곳입니다.
signupRedirectstring필수
처음 온 사용자가 가입을 마치러 가는 곳입니다. 뒤에 ?userId=<id>가 붙습니다.
errorRedirectstring기본값 "/404"
로그인에 실패하면 도착하는 곳입니다. 뒤에 ?error=<오류 키>가 붙습니다.
mainSsosSsoType["value"][]기본값 []
문구가 들어간 넓은 버튼으로 보여줄 provider입니다.
subSsosSsoType["value"][]기본값 []
아래에 동그란 아이콘 버튼 한 줄로 보여줄 provider입니다.
replaceboolean기본값 false
새 기록을 쌓지 않고 현재 방문 기록을 바꿔치기합니다.
  • 버튼을 직접 만들 때는 st.do.ssoSigninUser(ssoType, { signinRedirect, signupRedirect, errorRedirect })를 부릅니다. SSOButtons가 쓰는 것과 같은 action입니다.
  • 경로는 앱 안에서 보이는 그대로 적습니다. 앱에 basePath가 있으면 action이 앞에 붙여 줍니다.

꿀팁

  • provider별 차이는 callback에 둡니다. 로그인 규칙은 service에 둡니다.
  • service method는 모든 provider가 하나를 씁니다. accountId를 맞춘 뒤에는 어느 callback이든 같은 handleSsoCallback을 부릅니다.
  • 세 화면을 먼저 만듭니다. SSO를 켜기 전에 로그인 후 화면, 가입 화면, 오류 화면을 준비해 둡니다.

MIT 라이선스 하에 배포되었습니다.

내 AI에 이 문서 연결하기

MCPhttps://akanjs.com/mcp
Copyright © 2026 Akan.js 모든 권리 보유.시스템 관리자bassman