Appearance
01 이해하기
로그인·연결·로그아웃이 각각 무엇을 하는지, 연동 서비스가 받는 정보는 무엇인지, 계정에 무슨 일이 생기면 어떻게 보이는지.
| 상태 | 확정 |
| 최종 확인 | 2026-09-23 |
로그인
로그인은 사용자가 멤버십에서 자신을 인증하고, 연동 서비스에 자기 정보를 제공하도록 허락하는 절차다. OpenID Connect의 Authorization Code 방식을 따르며 PKCE를 필수로 요구한다.
연동 서비스 코드가 하는 일은 셋이다.
- 사용자를 인가 URL로 보낸다 — 인가 코드 요청
- 돌아온 인가 코드를 토큰으로 바꾼다 — 토큰 요청
- ID 토큰의
sub로 회원을 찾거나 만들고, 자기 세션을 발급한다 — 04 활용 › 연결
화면·비밀번호·동의는 멤버십이 처리한다.
토큰
| 토큰 | 용도 | 만료 |
|---|---|---|
| ID 토큰 | 사용자 식별. sub·email 클레임을 담은 JWT(RS256) | 10분 |
| 액세스 토큰 | 사용자 정보 조회(/oauth/userinfo) 호출용 | 10분 |
| 리프레시 토큰 | 액세스 토큰·ID 토큰 재발급. offline_access scope를 prompt=consent와 함께 요청한 경우만 발급 | 30일, 사용할 때마다 교체 |
ID 토큰에 필요한 정보가 모두 들어 있으므로 로그인 자체에는 액세스 토큰·리프레시 토큰이 필요하지 않다.
연동 서비스가 받는 정보
| 클레임 | 설명 | 예 | 항상 있는가 |
|---|---|---|---|
sub | 연동 서비스 전용 사용자 식별자. 접두어 + 32자 hex, 총 35자. 영구 불변 | rm_0ec504cc3974f5edd3e1830d8a67941c | 예 |
email | 이메일(소문자 정규화) | hong@example.com | 아니오 — 아래 참고 |
email_verified | 이메일 소유 확인 여부 | true | email이 있을 때 |
sub는 서비스마다 다르다. 같은 사람이라도 레밋이 받는 값과 핀모든이 받는 값은 다르며, 두 회사의 DB를 나란히 놓아도 같은 사람인지 알 수 없다(pairwise). 접두어로 어느 서비스용인지 보인다 — 머니박스 mb_, 레밋 rm_, 핀모든 fm_.
한 회사 안에서는 같다. 레밋이 웹·앱 클라이언트를 따로 등록해도 같은 사람은 같은 sub를 받는다. 식별자는 클라이언트가 아니라 서비스 단위로 발급된다.
email은 없을 수 있다
멤버십에 이관된 머니박스 기존 회원 중 이메일 없이 가입한 계정(이메일을 제공하지 않은 소셜 가입, 휴대폰 가입)이 있다. 이 계정은 email·email_verified 클레임이 빠진 채 로그인된다. 연동 서비스는 email이 없어도 sub만으로 회원을 만들 수 있어야 하고, 이메일이 꼭 필요하면 자기 온보딩에서 받는다. 멤버십은 이런 계정의 첫 로그인 때 이메일 등록을 요구할 예정이다 예정 — 그때까지는 부재를 전제한다.
이름·휴대폰·생년월일은 제공하지 않는다. 연동 서비스 온보딩에서 직접 받는다. 본인확인 도입 시 별도 scope로 추가된다 예정.
연결
연결은 멤버십 계정과 연동 서비스 회원이 짝지어진 상태다. 사용자가 어느 서비스에 처음 로그인하면 제3자 제공 동의를 거쳐 연결이 만들어지고, 이때 그 서비스용 sub가 발급된다.
| 상황 | 화면 |
|---|---|
| 멤버십 세션 없음 | 로그인 화면 |
| 이 서비스에 처음 연결 | 제3자 제공 동의 화면 — 제공받는 자(연동 서비스), 항목(이메일 + 전용 식별자), 목적, 보유기간 |
| 이미 연결된 서비스 | 화면 없이 바로 인가 코드 |
| 머니박스(운영 법인) | 동의 화면 없음 — 제3자 제공이 아니므로 |
연결과 동의는 클라이언트가 아니라 서비스 단위다. 같은 서비스에 새 클라이언트(앱 등)를 등록해도, 이미 그 서비스에 연결된 사용자에게는 동의 화면이 다시 뜨지 않는다. 동의는 연결당 한 번이며, 사용자가 멤버십 계정 관리에서 연결을 끊으면 예정 다음 로그인 때 동의 화면이 다시 뜬다.
회원가입은 둘로 나뉜다.
- 멤버십 가입 — 로그인 수단 만들기 + 멤버십 약관. 3사 공통, 한 번만. 멤버십 화면에서.
- 서비스 가입 — 연동 서비스 이용에 필요한 정보(이름·연락처·본인인증 등). 연동 서비스 화면에서, 토큰을 받은 뒤.
처음 온 사용자는 멤버십에서 계정을 만들고 돌아오면 연동 서비스 DB에 없는 sub다. 그때 연동 서비스가 회원 행을 만들고 온보딩을 시작한다. 기존 회원을 잇는 방법은 04 활용 › 연결에.
계정에 일어나는 일과 연동 서비스에 보이는 것
| 사용자 쪽 사건 | sub | 연동 서비스가 보는 것 | 연동 서비스가 할 일 |
|---|---|---|---|
| 이메일 변경 | 같음 | 다음 로그인의 email이 바뀜 | 로그인마다 email 갱신 |
| 비밀번호 변경, 로그인 수단 추가·해제 | 같음 | 아무것도 안 보임 | 없음 |
| 다른 서비스에 연결·해제 | 같음 | 아무것도 안 보임 | 없음 |
| 이 서비스 연결 해제(동의 철회) 예정 | 같음(보존) | 토큰 갱신 invalid_grant. 다음 로그인 때 동의 화면 재노출 후 같은 sub | 갱신 실패 시 재로그인 유도 |
| 계정 정지 | 같음 | 로그인 시도: 안내 후 access_denied. 토큰 갱신 invalid_grant | 재로그인 유도 |
| 계정 탈퇴 | 소멸 — 그 sub는 다시 발급되지 않음 | 토큰 갱신 invalid_grant. 같은 사람이 재가입하면 새 sub = 연동 서비스에는 새 회원 | 갱신 실패 시 연결 해제 처리. 옛 회원 정리는 연동 서비스 정책 |
멤버십은 계정을 사후에 병합하지 않는다. 한 사람이 이메일 계정과 (소셜 예정) 계정을 따로 만들지 못하도록 가입 시점에 이메일 기준으로 하나로 모은다. 따라서 "같은 사람인데 sub가 둘"인 상황은 탈퇴 후 재가입 외에는 생기지 않는다.
로그아웃
| 방식 | 동작 |
|---|---|
| 연동 서비스 로그아웃 | 연동 서비스 세션만 삭제. 멤버십 세션은 유지되어 다른 서비스 SSO는 그대로 |
| 멤버십 로그아웃 | 브라우저를 /oauth/logout으로 보낸다. 멤버십 세션이 끊겨 모든 서비스에서 다시 로그인해야 한다 |
다른 서비스로 로그아웃을 전파(백채널 로그아웃)하지는 않는다. 각 사 세션은 각 사가 끊는다.
보안
| 항목 | 내용 |
|---|---|
| PKCE | 필수(S256). 인가 코드가 가로채여도 code_verifier 없이는 토큰을 받을 수 없다 |
state | 필수. 콜백에서 보낸 값과 비교해 CSRF를 막는다. 1회용. 서버가 누락을 막지 않으므로 클라이언트 책임 |
nonce | 필수. ID 토큰의 nonce와 비교해 재사용을 막는다. 서버가 누락을 막지 않으므로 클라이언트 책임 |
iss (콜백) | 콜백 쿼리에 발급자가 함께 온다(RFC 9207). 라이브러리가 디스커버리의 issuer와 대조한다 |
| 클라이언트 시크릿 | 서버에만 둔다. 앱·브라우저 코드에 넣지 않는다 |
| 토큰 저장 | 멤버십 토큰을 프론트엔드에 저장하지 않는다. 세션은 연동 서비스 것이다 |
redirect_uri | 사전 등록된 값과 정확히 일치해야 한다 |
| 브라우저 직접 호출 | 토큰·userinfo 엔드포인트는 서버 간 호출 전용이다. CORS를 열지 않는다 |
이용 정책
- 멤버십이 제공한
sub·이메일은 연동 서비스 회원 식별·연락 목적으로만 쓴다. 다른 회사와 대조하거나 제3자에 넘기지 않는다. - 연동 서비스의 개인정보 처리방침에 머니박스로부터의 제공 사실을 반영한다 — 02 사전 설정 › 개인정보 처리방침에 적을 것.
- 회원 명단을 멤버십에 보내 계정을 미리 만드는 것은 불가하다. 사용자 본인이 가입·동의해야 한다(타 법인 간 개인정보 제공).
- 사용자가 멤버십 연결을 끊거나 탈퇴하면 연동 서비스는 그 회원을 자기 정책대로 처리한다. 멤버십은 여기에 관여하지 않는다.
- 연동 서비스의 자체 로그인 유지 여부는 연동 서비스가 결정한다. 멤버십은 다른 로그인이 있는지 알지 못한다.
고정값
프로토콜 세부는 서버가 고정한다. 연동 서비스가 고르는 것은 redirect_uri와 scope 조합뿐이다.
| 항목 | 고정값 |
|---|---|
| 인가 방식 | Authorization Code + PKCE(S256) — 필수 |
response_type | code만 |
| 클라이언트 인증 | client_secret_post |
| scope | openid · email · offline_access |
| 식별자 | pairwise sub |
| 서명 | RS256, 공개키는 JWKS로 게시. kid로 선택 |
| 시각 허용 오차 | exp는 +60초, iat는 −60초까지 허용하도록 라이브러리를 설정한다 |