LMS에 로그인했다는 정보와 Chat의 세션을 나눈다
LMS에 로그인한 사용자가 AI Chat을 열 때 다시 로그인할 필요는 없어야 했다. 하지만 AI Chat은 LMS와 서버·DB가 다른 서비스다. LMS의 로그인 토큰을 모든 Chat 요청에 계속 쓰면, 사용자를 처음 확인하는 일과 제품 안에서 세션을 유지하는 일이 하나의 자격정보에 묶인다.
로그인 구현에서는 두 책임을 나눴다. LearningX는 로그인한 사용자에게 짧게 유효한 JWT를 발급하고, AI Chat은 그 토큰을 검증해 자체 서버 세션을 만든다. 이후 요청은 세션 쿠키로 인증한다. Chat이 새 JWT를 발급해 브라우저에 보관하는 구조는 아니다.
| 자격정보 | 맡는 일 | 수명을 정하는 곳 |
|---|---|---|
| LearningX의 임시 JWT | LMS에서 확인한 사용자를 Chat에 전달 | LearningX의 토큰 TTL 설정 |
| Chat의 세션 쿠키 | Chat의 서버 세션을 찾아 이후 요청 인증 | Chat의 쿠키·세션 저장소 설정 |
Chat 서버는 직접 구현하던 세션·CSRF 처리를 express-session과 csrf-csrf로 바꾸고, MongoDB에 세션을 저장하도록 했다. 이후 LearningX의 토큰 발급·리다이렉트 경로가 추가돼 양쪽의 URL 진입 흐름을 연결했다.
URL의 토큰을 제품에서 계속 쓸 자격정보로 남기지 않는다
LearningX의 발급 경로는 로그인한 사용자를 확인하고 Canvas 사용자와 매칭한다. 사용자 정보를 JWT에 담고, 공유 비밀값으로 서명한 뒤 Chat 주소의 tempToken 쿼리에 붙여 이동한다. 발급 시 유효기간은 exp = now + ttl로 정한다. 당시 기본 TTL은 60초이며 환경 설정으로 바꿀 수 있었다.
- LearningX에서 임시 토큰 발급
이미 로그인한 사용자 정보를 짧게 유효한 JWT에 담아 Chat으로 전달한다.
- Chat에서 서명과 사용자 입력 검증
공유 비밀값으로 JWT를 검증하고 필수 사용자 식별값을 확인한다. 검증 오류는 인증 실패로 전달한다.
- Chat의 서버 세션 생성
제품 사용자 정보를 생성·갱신하고 서버 세션에 저장한다. 브라우저는 세션 쿠키로 이후 요청을 보낸다.
- 교환 성공 뒤 URL 정리
클라이언트가 사용자 상태를 설정하고 history.replaceState로 임시 토큰이 들어 있던 쿼리를 제거한다.
임시 토큰이 없으면 클라이언트는 현재 사용자 조회를 시도한다. 매번 URL 토큰을 받아야만 화면을 열 수 있는 구조가 아니라, 이미 만들어진 제품 세션을 사용하는 경로도 둔 것이다.
URL 정리는 교환이 성공한 뒤에 수행한다. 토큰의 수명도 URL에서 지운 시점이 아니라 JWT의 만료 시각으로 결정된다. 60초라는 기본값은 전달된 토큰을 쓸 수 있는 시간을 줄이지만, 토큰을 사용한 사실을 기록해 재사용을 막는 장치는 아니다.
세션을 분리한 뒤에도 재로그인의 순서는 보완해야 했다
최초 교환을 구현했다고 재로그인의 세션·CSRF 처리가 모두 끝난 것은 아니었다. 재로그인 때 invalid csrf token이 발생하는 문제를 수정했다. 세션을 재생성하고 사용자 정보를 저장한 뒤, 새 세션에 맞춰 CSRF 토큰을 덮어쓰도록 순서를 정했다. 이 보완은 최초 토큰 교환을 구현한 뒤 추가했다.
이후 과목의 LTI 진입도 연결했다. LTI 요청은 OAuth 서명을 검증하고 사용자·과목 입력을 처리한다. 그 뒤 서버 세션을 재생성하고 저장한 후 CSRF 토큰을 발급해 화면으로 이동한다. URL의 임시 JWT 교환과 인증 입력은 다르지만, 제품 안에서는 같은 서버 세션 형태를 사용한다.
따라서 두 진입 경로를 지원한다는 말은 LTI도 임시 JWT를 반드시 거친다는 뜻이 아니다. 각 경로가 외부의 인증 입력을 검증하고, Chat의 세션으로 이어 주는 책임을 맡는다.
독립적인 세션에는 별도의 저장과 운영 책임이 생긴다
이 선택으로 LMS의 토큰 수명과 Chat의 이용 세션을 따로 정할 수 있었다. 그 대신 Chat은 MongoDB의 세션 저장, 공유 비밀값, 쿠키 속성과 CSRF 구성을 운영해야 한다. 쿠키의 maxAge, 저장소 TTL, 당시 세션의 expiresAt도 각각 설정됐으므로, 세 가지 수명이 자동으로 일치한다고 볼 수는 없다.
같은 토큰을 유효기간 안에 다시 보내지 못하게 하는 소비 기록은 이 교환 경로에 없다. LMS에서 로그아웃했다는 사실이 이미 만들어진 Chat 세션을 즉시 종료한다는 보장도 이 구조에서 나오지 않는다. 제품 세션을 분리했다는 설계 판단과 전체 SSO 수명 정책은 구분해야 한다.
당시 인증 테스트 파일은 객체의 존재 여부를 확인하는 기본 형태였다. 토큰 교환·만료·재사용·잘못된 사용자 입력을 실제 요청으로 검증한 보안 테스트의 근거로 삼을 수 없다. 구현 코드에서 발급과 검증, 세션 저장의 연결은 확인할 수 있지만, 로그인 실패율이나 처리 시간의 개선은 측정된 결과가 없다.
AI Commons의 SSO 로그아웃 순서는 이미 로그인한 사용자의 세션을 끝내는 다른 경계다. 로그인 입력을 제품 세션으로 바꾼 뒤에는, 그 세션을 언제 어떻게 종료할지도 별도의 흐름으로 정해야 한다.