134 lines
11 KiB
Markdown
134 lines
11 KiB
Markdown
---
|
|
id: 66c18e42-116c-459f-86bd-b7e4bf394866
|
|
kind: REFERENCE
|
|
slug: oauth-token-application-session-boundary
|
|
title: OAuth Token과 Application Session을 구분하는 기준
|
|
topic: OAuth/OIDC 인증 경계
|
|
project: KeyCloak Patterns
|
|
status: 게시 중
|
|
version: 33
|
|
verifiedOn: 2026-08-30
|
|
studio: "https://hyeonworks.com/studio/documents/66c18e42-116c-459f-86bd-b7e4bf394866/edit"
|
|
public: "https://hyeonworks.com/references/oauth-token-application-session-boundary"
|
|
---
|
|
|
|
# OAuth Token과 Application Session을 구분하는 기준
|
|
|
|
인증 과정에서 만들어지는 상태를 모두 하나의 `로그인 상태`로 보면 안 된다.
|
|
IdP의 SSO Session, Access Token, Refresh Token, 애플리케이션의 Session Cookie, 인증 Proxy의 Session Cookie는
|
|
각각 생성하는 주체와 사용하는 주체가 다르고 유효 시간도 서로 다르다.
|
|
|
|
예를 들어 Access Token이 만료되었다고 해서 애플리케이션 Session이나 IdP의 SSO Session까지 같이 만료된 건 아니다.
|
|
반대로 애플리케이션 Session을 삭제했다고 해서 IdP의 SSO Session이나 이미 발급된 Token까지 사라지는 것도 아니다.
|
|
|
|
그래서 어떤 Session이나 Token이 남아 있는지, 무엇이 만료되었는지, Logout할 때 어떤 상태를 삭제하거나 무효화해야 하는지를 각각 구분해서 확인한다.
|
|
|
|
## 관계
|
|
|
|
- **SPA에서 토큰을 직접 관리하면서 드러난 Browser Credential 경계**
|
|
JavaScript memory의 OAuth token과 Keycloak SSO session을 구분한 Case다.
|
|
- **Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출**
|
|
같은 요청 안에서 session cookie와 access token이 함께 움직인다.
|
|
- **Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정**
|
|
BFF에서는 session cookie, JavaScript가 읽는 CSRF token, server-side OAuth token을 각각 다른 용도로 사용한다.
|
|
- **Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유**
|
|
Forward-Auth에서는 upstream이 JWT를 직접 검증하지 않고 proxy session을 기반으로 edge가 만든 identity header를 사용한다.
|
|
|
|
## 목적
|
|
|
|
SPA, Mediator, BFF에서는 Resource Server가 Access Token을 검증한 뒤 JWT의 Claim에서 사용자 정보를 얻을 수 있다. 반면 Forward-Auth 구조에서는 애플리케이션이 인증 Proxy가 전달한 사용자 정보 Header를 사용한다.
|
|
최종적으로 같은 사용자 이름이 나오더라도, 한쪽은 Access Token을 검증해서 얻은 값이고 다른 한쪽은 신뢰할 수 있는 Proxy가 전달한 값이다.
|
|
|
|
Logout과 만료 처리에서도 같은 구분이 필요하다. IdP의 SSO Session, Access Token과 Refresh Token, Application Session은 서로 다른 주체가 관리하고 수명도 다르다. 따라서 Logout할 때 무엇을 삭제하거나 무효화할지, 특정 Credential이 만료되었을 때 어떤 상태를 계속 사용할 수 있는지를 각각 구분해서 설계한다.
|
|
|
|
## 규칙
|
|
|
|
### 1. 인증 상태를 종류별로 구분해서 기록한다.
|
|
|
|
IdP SSO Session, OAuth Access Token, OAuth Refresh Token, Application Session Cookie, Proxy Session Cookie는 각각 생성하는 주체와 사용하는 목적이 다른 별개의 상태다.
|
|
|
|
로그와 진단 정보에서도 어떤 상태를 확인한 것인지 구체적으로 기록한다.
|
|
예를 들어 단순히 `로그인 상태가 만료되었다`고 남기는 대신 `Application Session이 만료되었다`, `Access Token이 만료되었다`, `Proxy Session이 존재하지 않는다`처럼 실제 Session이나 Token의 종류를 명시한다.
|
|
|
|
이렇게 이름을 구분해야 장애를 분석하거나 Logout과 만료 동작을 확인할 때 어떤 상태가 남아 있고 어떤 상태를 삭제하거나 갱신해야 하는지 정확하게 판단할 수 있다.
|
|
|
|
### 2. 만든 주체와 주된 소비자로 구분한다
|
|
|
|
Access Token, Application Session Cookie, Proxy Session Cookie는 각각 발급하는 주체와 사용하는 주체가 다르다.
|
|
|
|
Access Token은 IdP가 발급하고 Resource Server가 요청을 처리할 때 검증한다.
|
|
Application Session Cookie는 애플리케이션이 발급하고, 이후 브라우저가 보낸 Cookie를 이용해 애플리케이션이 자신의 로그인 Session을 찾는 데 사용한다.
|
|
Proxy Session Cookie는 인증 Proxy가 발급하고, 이후 Proxy가 인증 상태를 확인할 때 사용한다.
|
|
|
|
이처럼 어떤 주체가 Credential을 발급했고, 요청을 처리할 때 어떤 주체가 이를 검증하는지가 다르다면 서로 다른 Credential과 인증 상태로 구분해서 다뤄야 한다.
|
|
|
|
### 3. 같은 사용자라도 Credential은 서로 다른 상태를 나타낸다
|
|
|
|
Application Session Cookie는 서버에 저장된 Session을 찾기 위한 `session ID`를 브라우저에 전달하는 데 사용한다.
|
|
실제 Access Token과 Refresh Token은 Cookie 안에 들어 있는 것이 아니라 Authorized Client와 같은 별도의 서버 저장소에 보관된다. 따라서 Session Cookie와 OAuth Token 저장소는 서로 구분해서 봐야 한다.
|
|
|
|
Proxy Session Cookie는 반드시 같은 방식으로 동작하는 것은 아니다.
|
|
별도의 서버 Session Store를 두지 않고, 인증 상태를 확인하는 데 필요한 정보를 Cookie 자체에 담은 뒤 Proxy가 Cookie의 유효성을 검증하는 방식으로 구성할 수도 있다.
|
|
이 경우 Cookie는 서버에 저장된 Session을 조회하기 위한 `session ID`와는 역할이 다르다.
|
|
|
|
### 4. 브라우저에 없는 것을 범위까지 적는다
|
|
|
|
브라우저 JavaScript에 OAuth Token을 전달하지 않는 구조에서도 브라우저에 인증과 관련된 상태는 남아 있을 수 있다.
|
|
예를 들어 BFF 구조에서는 애플리케이션의 `HttpOnly` Session Cookie가 유지될 수 있고, IdP에서는 자신의 도메인에 SSO Session Cookie를 유지할 수 있다.
|
|
|
|
따라서 단순히 `브라우저에 인증 정보가 없다`거나 `브라우저에 Credential이 없다`고 표현하면 안 된다.
|
|
`브라우저 JavaScript에 Access Token과 Refresh Token을 노출하지 않는다`처럼 무엇이 없고 어느 범위에서 접근할 수 없는지를 적는다.
|
|
|
|
### 5. 영구 저장과 메모리 보관을 구분한다.
|
|
|
|
OAuth Token을 JavaScript Memory에만 보관하면 Local Storage나 Session Storage와 같은 Web Storage에 Token을 지속적으로 저장하지 않을 수 있다.
|
|
다만 실행 중인 브라우저 JavaScript에서도 Token에 접근할 수 없다는 뜻은 아니다.
|
|
|
|
애플리케이션이 Token Endpoint의 응답을 JavaScript로 받아 처리한다면, 실행 중에는 Token 값이 JavaScript가 다루는 메모리에 존재한다. 같은 Origin에서 악성 Script가 실행될 수 있는 상황에서는 Token 응답이나 애플리케이션이 Token을 처리하는 경로가 공격 대상이 될 수 있기 때문에, Web Storage에 Token이 저장되지 않을 뿐이고 XSS를 통해 Token에 접근할 여지는 남는다.
|
|
|
|
### 6. 로그아웃 범위를 상태별로 적는다
|
|
|
|
애플리케이션에서 Logout하는 것과 IdP의 SSO Session을 종료하는 것은 서로 다른 동작이다.
|
|
애플리케이션 Session이나 Cookie를 삭제하더라도 IdP의 SSO Session은 그대로 남아 있을 수 있으며, 반대로 IdP Session을 종료하더라도 이미 발급된 Access Token의 처리 방식은 별도로 확인해야 한다.
|
|
|
|
특히 Resource Server가 Self-contained JWT Access Token을 매 요청마다 IdP에 확인하지 않고 자체적으로 검증하는 구조에서는 이미 발급된 Token이 Logout과 동시에 자동으로 무효화되지는 않는다.
|
|
Resource Server는 JWT의 서명과 만료 시간 등 필요한 Claim을 검증하고 Token이 아직 유효하면 요청을 받아들일 수 있다.
|
|
|
|
따라서 Denylist처럼 이미 발급된 Token의 상태를 추가로 확인하는 방법을 사용하지 않는다면, 애플리케이션 Logout만으로 기존 Access Token을 즉시 사용할 수 없게 만들 수는 없다.
|
|
이런 구조에서는 Access Token의 TTL을 짧게 설정해 Logout 이후에도 기존 Token을 사용할 수 있는 시간을 제한하고,
|
|
Refresh Token과 Session은 각각의 저장 위치와 관리 주체에 맞게 별도로 종료하거나 제거한다.
|
|
|
|
### 7. Logout 대상 credential을 구체적으로 적는다
|
|
|
|
SPA에서 JavaScript Memory에 보관하던 OAuth Token을 제거하더라도 Keycloak의 SSO Session까지 종료되는 것은 아니다. Keycloak의 SSO Session이 아직 유효하다면 이후 새로운 Authorization Request를 보냈을 때 사용자가 다시 아이디와 비밀번호를 입력하지 않고 인증 절차가 진행될 수 있다.
|
|
|
|
따라서 Logout을 단순히 브라우저의 Token이나 Cookie를 삭제하는 동작으로만 정의하면 안 된다.
|
|
어떤 수준까지 로그아웃할 것인지에 따라 애플리케이션 상태와 IdP의 SSO Session을 각각 어떻게 종료할지 정해야 한다.
|
|
|
|
Mediator나 BFF처럼 서버에서 Application Session과 Authorized Client를 함께 관리하는 구조에서는 두 상태의 정리 방법도 각각 명시한다.
|
|
Application Session을 무효화하는 것과 Authorized Client에 저장된 Access Token 및 Refresh Token을 제거하는 것은 서로 다른 처리기 때문에, Logout 시 어떤 상태를 삭제하고 어떤 상태를 유지할지를 별도로 확인한다.
|
|
|
|
## 적용 조건
|
|
|
|
- 인증 상태를 표나 문서로 정리할 때
|
|
- 로그아웃과 만료 동작을 설계할 때
|
|
- 브라우저가 어떤 credential을 저장하거나 전송하는지 설명할 때
|
|
- 여러 구조를 비교할 때
|
|
|
|
## 예외
|
|
|
|
- 하나의 요청 흐름 안에서 어떤 Session이나 Token을 의미하는지가 이미 명확한 경우에는 짧은 이름을 사용할 수 있다.
|
|
다만 문서에서 처음 등장할 때는 전체 이름을 먼저 적어 어떤 상태를 의미하는지 명확하게 정의한다.
|
|
이후 같은 문맥에서는 의미가 달라지지 않는 범위에서 `Session`, `Access Token`, `Refresh Token`처럼 줄여서 표현할 수 있다.
|
|
- IdP를 쓰지 않고 애플리케이션이 자체 로그인만 하는 구조에는 SSO session과 access token, refresh token이 없다.
|
|
|
|
## 예시
|
|
|
|
- IdP SSO session : IdP 도메인의 cookie이고 애플리케이션 memory와 별개다
|
|
- access token : IdP가 만들고 Resource Server가 서명과 issuer, audience를 검증한다
|
|
- refresh token : 새 access token을 받는 장기 credential이다
|
|
- 애플리케이션 session cookie : server-side 로그인 상태를 찾는 credential이다.
|
|
- proxy session cookie : proxy의 auth endpoint에 제시하는 최소 상태다
|
|
- CSRF token : cookie가 자동으로 붙는 상태 변경 요청의 의도를 확인한다
|
|
- identity header : edge가 확인한 사용자 정보이고 JWT token이 아니다
|