•
Okta 공시문서: AI Agent의 토큰 교환
신원과 로그인
사용자 로그인은 Okta Org Authorizaiton Server 대상 Authorizaiton code + PKCE로 처리한다.
로그인에 성공하면 Okta가 발급한 ID Token을 서버가 Encrypt JWT로 암호화해 httpOnly 쿠키(okta session 1시간 만료)에 저장한다. 이 ID Token이 이후 모든 에이전트 토큰 교환의 subject_token으로 사용된다.
로그아웃은 앱 세션만 지우는게 아니라 RP-initiated logout으로 Okta/v1/logout을 호출해 Okta 세션 자체를 종료한다. 재로그인 시, 자격증명 다시 입력해야함.
에이전트 입증 - Cross App Access(XAA)
채팅/콘텐츠 DB 접근은 에이전트가 사용자의 ID 토큰을 위임받아 2단계 토큰 교환을 거쳐야 리소스용 액세스 토큰을 얻는 구조다. 에이전트는 자신의 개인키로 서명한 client_assertion(private_key_jwt)으로 매 단계 자신을 증명한다.
단계 | 주체 → 대상 | 동작 | 발급물 |
1 | 사용자 → Okta Org AS | Authorization Code + PKCE 로그인 | ID Token |
2 | Agent → Okta Org AS | grant_type=token-exchange
ID Token 제출 + Private_key_jwt 서명 | ID-JAG |
3 | Agent → Custom AS | grant_type=jwt-bearer
ID-JAG 제출 + private_key_jwt 서명 | Access token |
4 | 리소스(앱) 자체 검증 | JWKS로 서명 issuer/audience/scope 독립 검증 | 통과 시에만 실행 |
기능별 최소 권한
scope | 허용 기능 | 강제 위치 |
chat.invoke | AI 채팅 | api/chat/route.ts |
content.read | 콘텐츠 DB/AI 인용 분석(조회,쓰기) | contents, citations의 page/actions |
킬 스위치
관리자가 Okta에서 에이전트를 Deactivate하거나 특정 Resource Connection을 삭제하면, 그 즉시(캐시 없음) 해당 기능만 차단된다. 로그인 세션은 유지된다.
조작 | 영향 |
에어전트 Deactivate | 채팅/콘텐츠 DB 모두 즉시 400 |
OPENAI_API_KEY SECRET 연결 삭제 | 채팅/블로그 임베딩 생성 즉시 차단 |
SUPABASE_SERVICE_ROLE_KEY SECRTET 연결 삭제 | 콘텐츠 DB 관련 기능 즉시 차단 |
사람별 권한(그룹 기반 RBAC)
Okta의 그룹 맴버십을 ID Token의 group 클레임으로 실어, 그룹에 따라 notion 도구 접근 범위가 달라지도록 구현했다. 역할 판정은 앱이 하고, Okta는 “이 사용자가 이 그룹에 속해 있다.”까지만 증명한다.
Okta 그룹 | 역할 | Notion 도구 접근 |
notion-super-admin | admin | 전체 20개 (쓰기 포함) |
notion-readonly | read | 읽기 전용 10개 (명시적 허용 목록) |
(해당 그룹 없음) | none | 0개 noiton 도구 노출 안됨 |
서드파티 연동 - notion MCP (STS)
Notion mcp server는 XAA방식이 아닌 Okta의 Secure Token Storage(STS)방식을 사용한다.
최초 1회 사용자 동의(OAuth consent)가 필요하고, 이후에는 Okta 금고에 보관된 Notion 액세스 토큰을 매번 다시 위임받아 사용한다.
notion의 mcp server는 DCR(Dynamic Client Registration)만 지원해 POST mcp.notion.com/register 로 client_id/secret을 직접 발급받아 Okta의 “Resource server(MCP Server)”리소스 연결에 등록했다. resource 식별자는 URL이 아니라 Okta가 부여한 ORN(Resource Indicator)이다.
고정 비밀값 - secret 리소스 타입(OPA 금고)
Open AI API 키와 Supabase service role key는 더 이상 .env에 평문으로 존재하지 않는다.
매요청마다 사용자 ID Token으로 Okta에 “vaulted-secret” 토큰 교환을 요청해, OPA 금고에 저장된 값을 그 순간 받아와 사용한다.
Refresh Token - 세션 자동 연장/CLI 자동화
ID Token만 사용하게 되면 1시간마다 만료돼, 웹은 강제 재로그인이 필요했고 CLI 자동화는 매번 사람이 새 토큰을 Export해야 했다./
프로젝트 구조
파일 | 역할 |
lib/okta/session.ts | 암호화 세션 쿠키 (ID 토큰 보관) |
lib/okta/oidc.ts | 사용자 OIDC 로그인 (PKCE) |
lib/okta/client-assertion.ts | private_key_jwt 서명 (공유 모듈) |
lib/okta/agent.ts | XAA 2단계 토큰 교환 |
lib/okta/sts.ts | STS(OAuth 위임) · vaulted-secret 교환 |
lib/okta/secrets.ts | Secret 금고 조회 (OpenAI/Supabase, 캐시·폴백 없음) |
lib/okta/roles.ts | 그룹 → 역할 판정, Notion 도구 필터 |
lib/okta/verify.ts,
authorize.ts | 액세스 토큰 검증, scope 인가 헬퍼 |
lib/supabase/admin.ts | Supabase admin 클라이언트 (idToken 기반) |
app/api/auth/okta/* | 로그인 · 콜백 · 로그아웃 · export-token |
app/api/chat, api/trust | 채팅 라우트, Trust 패널 API |


