11 KiB
id, kind, slug, title, topic, project, status, version, verifiedOn, studio, public
| id | kind | slug | title | topic | project | status | version | verifiedOn | studio | public |
|---|---|---|---|---|---|---|---|---|---|---|
| 66c18e42-116c-459f-86bd-b7e4bf394866 | REFERENCE | oauth-token-application-session-boundary | OAuth Token과 Application Session을 구분하는 기준 | OAuth/OIDC 인증 경계 | KeyCloak Patterns | 게시 중 | 33 | 2026-08-30 | https://hyeonworks.com/studio/documents/66c18e42-116c-459f-86bd-b7e4bf394866/edit | 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이 아니다