OIDC 로그인 흐름¶
한 줄 요약: OIDC는 "구글로 로그인"처럼 누구인지 확인하는 일을 다른 서비스에 맡기는 표준이다. 즐겨찾기 같은 그 사람의 데이터는 여전히 우리 앱 DB에 둔다.
📍 만난 상황¶
- 2026-11-09 — 2번 물음 → 그날 일지
- 14:11 중계 서버가 필요하다는 설명을 들은 직후였다. dhub2에 가입된 사용자가 대시보드에도 같은 계정으로 로그인하게 할 수 있는지 물었다.
- 14:34 시니어와 다시 이야기해 즐겨찾기·사용자 표가 필요하다는 걸 정리했다. "SSO를 쓰면 즐겨찾기 기능을 넣을 수 있나?"라고 물었다.
- 결론: 가능하다. dhub2가 OIDC를 이미 제공한다. 사용자 데이터는 대시보드 DB에
sub기준으로 저장한다.
🧩 쉽게 말하면¶
호텔 프런트와 객실 키를 떠올리면 된다. 수영장(우리 앱)은 손님 신분증을 직접 검사하지 않는다. 프런트(dhub2)가 확인하고 "이 사람은 1207호 손님"이라는 확인증을 준다.
수영장은 그 확인증만 보고 들여보낸다. 그리고 "1207호 손님의 사물함"은 수영장이 따로 관리한다. 이게 SSO와 앱 DB의 관계다.
OIDC가 없으면 앱마다 회원 가입·비밀번호 관리를 따로 해야 한다. 이용자는 비밀번호를 여러 개 외워야 한다.
📖 제대로 알기¶
인증과 인가¶
- 인증(Authentication): "당신은 누구인가"를 확인하는 일. OIDC가 하는 일이다.
- 인가(Authorization): "당신은 이걸 해도 되는가"를 정하는 일. 관리자 여부, 데이터 권한 등.
용어¶
- SSO(Single Sign-On): 한 번 로그인으로 여러 서비스를 쓰는 것.
- OAuth 2.0: 남의 서비스에 "내 대신 이걸 해도 된다"는 권한을 넘기는 표준.
- OIDC(OpenID Connect): OAuth 2.0 위에 "누구인지"를 알려 주는 기능을 얹은 표준.
- IdP(Identity Provider: 신원 제공자): 로그인을 맡는 쪽. 여기서는 dhub2-manager.
- RP(Relying Party: 의존 앱) 또는 클라이언트: 로그인을 맡기는 쪽. 여기서는 대시보드.
주고받는 토큰¶
| 토큰 | 하는 일 |
|---|---|
| ID 토큰 | "이 사람은 누구"라는 증명서. JWT(서명된 JSON 문자열) |
| 액세스 토큰 | API를 부를 때 내미는 출입증 |
| 리프레시 토큰 | 출입증이 만료되면 새로 받는 데 씀 |
ID 토큰 안의 정보(claim)¶
sub(subject): 사람마다 다르고 바뀌지 않는 고유 ID. dhub2에서는 사용자 ID 그 자체다.iss(issuer): 누가 발급했나. hub-dev인지 운영인지 구분된다.aud(audience): 이 토큰을 받을 대상 앱.email,name,groups: 이메일, 이름, 소속 그룹.
설정 주소¶
/.well-known/openid-configuration은 IdP의 공개 안내판이다. 로그인 화면 주소, 토큰 주소, 지원 방식이 적혀 있다. 그날 hub-dev에서 이 주소가 정상 응답해 OIDC가 켜져 있음을 확인했다.
⚙️ 어떻게 동작하나¶
가장 많이 쓰는 authorization code 흐름(일회용 코드를 토큰으로 바꾸는 방식)이다.
sequenceDiagram
participant U as 브라우저
participant A as 대시보드 서버
participant I as dhub2 로그인
U->>A: 로그인 버튼 클릭
A->>U: dhub2로 이동하라 (state, PKCE 포함)
U->>I: 아이디와 비밀번호 입력
I->>U: 일회용 코드 들고 돌아가라
U->>A: 일회용 코드 전달
A->>I: 코드 + client secret 으로 토큰 요청
I->>A: ID 토큰, 액세스 토큰
A->>U: 대시보드 세션 쿠키만 줌
비밀번호는 dhub2 화면에만 입력된다. 토큰은 대시보드 서버에만 남고, 브라우저는 쿠키만 받는다.
- 서버가 state(위조 요청 방지용 일회용 값), nonce(토큰 재사용 방지 값), PKCE(코드를 가로채도 못 쓰게 하는 확인값)를 만든다.
- 브라우저가 dhub2 로그인 화면으로 간다. 대시보드는 비밀번호를 보지 않는다.
- 로그인되면 브라우저가 일회용 코드를 들고 대시보드로 돌아온다.
- 서버가 코드와 client secret을 dhub2에 보내 토큰을 받는다. 이 단계가 서버에서 일어나야 비밀값이 안전하다.
- 서버가 ID 토큰의 서명과
iss,aud를 검증한다. - 서버가 앱 DB의
app_user에 "있으면 갱신, 없으면 추가"한다. - 브라우저에는 대시보드 자체 세션 쿠키만 준다.
데이터를 조회할 때는 서버가 이용자 토큰을 "매니저용 토큰"(10분짜리)으로 교환(token-exchange)해 쓴다. 그러면 dhub2 권한이 그대로 적용된다.
🧪 예시¶
-
예시 1 · 일상에 빗대어: 쇼핑몰에 "카카오로 로그인"을 하면 카카오는 "이 사람 고유번호 1207"만 알려 준다. 장바구니 3개, 찜 5개는 쇼핑몰 DB에 "1207번 손님 것"으로 저장된다. 카카오는 장바구니를 모른다.
-
예시 2 · 실제로는: 로그인할 때마다 실행하는 SQL이다.
INSERT INTO app.app_user (issuer, sub, name, email, last_login)
VALUES ($1, $2, $3, $4, now())
ON CONFLICT (issuer, sub) DO UPDATE
SET name = EXCLUDED.name,
email = EXCLUDED.email,
last_login = now()
RETURNING id;
-- 즐겨찾기는 이 id로 저장
INSERT INTO app.favorite (user_id, dataset_id) VALUES ($5, $6)
ON CONFLICT DO NOTHING;
(issuer, sub)가 처음이면 새 줄을 넣는다.
② 이미 있으면 이름·이메일·마지막 로그인만 고친다. dhub2에서 이름을 바꾸면 다음 로그인 때 반영된다.
③ RETURNING id로 앱 내부 번호(예: 7)를 받아 세션에 적는다.
④ 즐겨찾기는 user_id = 7로 저장한다. 두 번 눌러도 한 줄이다.
사용자 표에는 비밀번호 열이 없다. 비밀번호 확인은 dhub2가 한다.
⚖️ 비교 · 흔한 오해¶
| 구분 | 누가 들고 있나 |
|---|---|
| 비밀번호, 계정 | dhub2(IdP) |
| 즐겨찾기, 최근 항목, 문의 | 대시보드 DB(앱) |
| 관리자인지(역할) | 대시보드가 규칙으로 정함 |
- 헷갈렸던 점: "SSO를 쓰면 사용자 데이터도 로그인 서버에 있어야 한다" — "사용자 = 로그인 서버의 것"이라고 느끼기 쉽다. 하지만 로그인 서버는 "누구인지"만 보증한다. 그 사람에 대한 앱 데이터는 앱이 갖는다.
- 오해: "이메일로 사람을 구분하면 된다" — 이메일은 바뀔 수 있다. 바뀌면 즐겨찾기가 사라지거나 남에게 붙는다. 바뀌지 않는
sub를 쓴다. - 오해: "
sub만 있으면 된다" — hub-dev와 운영은 사용자 목록이 다르다. 같은sub값이 다른 사람일 수 있다. 그래서issuer를 같이 둔다. - OAuth와 OIDC: OAuth는 "권한 위임", OIDC는 거기에 "신원 확인"을 더한 것이다. 로그인에는 OIDC를 쓴다.
🛠️ 실무에서는¶
- 체크리스트
- IdP에 앱을 클라이언트로 등록했나(client id·secret, 돌아올 주소
redirect_uri). - client secret과 토큰이 서버에만 있나.
- ID 토큰의 서명·
iss·aud·만료를 검증하나. UNIQUE (issuer, sub)가 걸려 있나.- 배포할 때 주의: 로그인 화면은 이용자 브라우저가 연다. 그래서 IdP의 공개 주소가 브라우저에서 열려야 한다. 서버 간 내부 주소만으로는 안 될 수 있다.
- 탈퇴한 사람: 앱은 자동으로 알 수 없다. 로그인을 못 하니 보안 문제는 없지만, 데이터가 남는다. 필요하면 "오래 로그인 안 한 사용자 정리" 작업을 둔다.
- 문제가 생기면 먼저 볼 것: ①
redirect_uri가 등록값과 글자 하나까지 같은가 ②iss주소가 일치하는가 ③ state 쿠키가 돌아올 때 남아 있는가.
❓ 자주 묻는 질문¶
- Q. 진짜 SSO를 붙이기 전에 개발은 어떻게 하나? A. 가짜 로그인 서버나 로컬 계정으로 "로그인된 사람"만 만들어 두면 된다. 마이페이지 코드는 "누구인지"만 받으므로 나중에 방식이 바뀌어도 그대로 쓴다.
- Q. 관리자인지도 dhub2가 알려 주나?
A.
groups정보는 온다. 하지만 그 그룹을 관리자로 볼지는 앱이 정한다(예: 그룹↔역할 대응 표). - Q. 로그인 상태는 어떻게 유지되나? A. 앱 세션 쿠키로 유지한다. 액세스 토큰이 만료되면 서버가 리프레시 토큰으로 새로 받는다.
🔭 더 깊이 보기¶
- JWT는 어떻게 위조를 막나? 서명 검증의 원리를 알면 "토큰을 믿는 이유"가 이해된다.
- 세션 쿠키 보안 옵션은?
HttpOnly,Secure,SameSite가 각각 무엇을 막는지다. - 토큰 교환(token exchange)은 언제 쓰나? 이용자 권한 그대로 다른 API를 부르는 방법이다.
- 관련 주제: 프론트엔드와 백엔드 서버 구조 — 토큰을 서버에 둬야 하는 이유
- 관련 주제: DB 정규화와 연결 표 —
favorite같은 연결 표 설계
📚 참고 자료¶
- Microsoft ID 플랫폼의 OpenID Connect (Microsoft Learn 한국어) — 인증 코드 흐름과 ID 토큰 설명 · 링크
- HTTP 쿠키 (MDN 한국어) — 로그인 뒤 세션 쿠키가 동작하는 방식 · 링크
- OpenID Connect (OpenID Foundation, 영문) — 표준의 공식 소개 · 링크
🗓️ 이력¶
- 2026-11-09: dhub2 계정 SSO가 되는지, SSO를 써도 즐겨찾기를 둘 수 있는지 물음 → 일지