SSO, logout và session
Tiếp đi: SSO và logout trong Keycloak chạy thế nào?
Khái niệm SSO đã có ở 4.4.7. Bài này đi vào cơ chế session của Keycloak và cách logout đúng.
1. Bài toán
- Bob login
shop-web, mởshop-adminkhông phải login lại. - Bob bấm Logout ở admin-portal: cả web cũng phải logout.
- Mobile app muốn khách mở app sau 2 tuần vẫn đăng nhập.
- Bob nghỉ việc: khóa ngay.
2. Vấn đề
- Logout chỉ xóa token ở SPA: cookie SSO trên Keycloak vẫn còn, bấm Login là vào lại ngay.
- Access token đã phát vẫn hợp lệ ở service cho tới khi hết hạn.
- Nhầm lẫn giữa access token lifetime và SSO session.
3. Giải pháp: ba tầng thời gian
| Thứ | Nằm ở đâu | Sống bao lâu |
|---|---|---|
| SSO session | Server Keycloak + cookie KEYCLOAK_IDENTITY/KEYCLOAK_SESSION trên domain Keycloak | SSO Session Idle / Max |
| Refresh token | Client; gắn với SSO session | Hết khi session idle/max hết hoặc logout |
| Access token | Client, gửi kèm request | Access Token Lifespan (vài phút), không thu hồi được khi verify offline |
| Offline token | Client (scope offline_access) | Offline Session Idle (mặc định 30 ngày), sống cả khi SSO session hết |
4. Code từng bước
Bước 1: Cấu hình (Realm settings → Sessions / Tokens)
SSO Session Idle: 30m SSO Session Max: 10h
Offline Session Idle: 30d Offline Session Max Limited: On, 90d
Revoke Refresh Token: On (rotation: mỗi refresh token chỉ dùng 1 lần)
Bước 2: Refresh và offline token
KC=http://localhost:8080/realms/shop/protocol/openid-connect
# refresh
curl -s -X POST $KC/token -d grant_type=refresh_token \
-d client_id=shop-web -d refresh_token="$REFRESH_TOKEN"
# mobile xin offline token: thêm scope offline_access khi login
# .../auth?...&scope=openid%20offline_access
# user cần role offline_access (mặc định có trong default-roles-shop)
Bước 3: RP-initiated logout (SPA chuyển trình duyệt tới Keycloak):
GET $KC/logout?id_token_hint=$ID_TOKEN
&post_logout_redirect_uri=https://shop.example.com/
&client_id=shop-web
URI phải nằm trong "Valid post logout redirect URIs". Keycloak 18+ yêu cầu id_token_hint hoặc client_id thay cho tham số redirect_uri cũ.
Bước 4: Back-channel logout cho shop-admin (có backend giữ session riêng):
Clients → shop-admin → Settings → Logout settings
Backchannel logout URL: https://admin.shop.example.com/logout/connect/back-channel/keycloak
Backchannel logout session required: On
Spring Security 6.2+ có sẵn .oidcLogout(l -> l.backChannel(Customizer.withDefaults())) để nhận logout token và hủy HTTP session.
Khóa bob ngay (admin):
kcadm.sh update users/$BOB_ID -r shop -s enabled=false
kcadm.sh create users/$BOB_ID/logout -r shop # hủy mọi session, kể cả offline
5. Cách hoạt động
SSO
shop-web ──/auth──> Keycloak: chưa có cookie → form login → tạo SSO session + cookie
shop-admin ──/auth──> Keycloak: có cookie hợp lệ → không hỏi password → code ngay
(mỗi client nhận token riêng, aud/role riêng; chung SSO session)
Logout từ shop-admin
browser ──/logout──> Keycloak: xóa SSO session + cookie
├─ back-channel POST logout_token ──> shop-admin backend (hủy HTTP session)
└─ (front-channel, nếu bật) iframe tới shop-web
shop-web: refresh token lần sau → 400 invalid_grant → về trang login
Access token đang lưu hành: VẪN hợp lệ tới exp ở order-service
6. So sánh cách logout
| Cách | Ai gọi | Tin cậy |
|---|---|---|
| Chỉ xóa token ở client | App | Không phải logout thật |
| RP-initiated | Browser → Keycloak | Hủy SSO session |
| Front-channel | Browser (iframe) → các app | Dễ hỏng do chặn cookie bên thứ ba |
| Back-channel | Keycloak → backend app (server-to-server) | Tin cậy nhất, cần app có backend |
7. Khi nào nên dùng / không nên dùng
- Nên: access token ngắn + refresh rotation; back-channel cho app có server session; offline token cho mobile "nhớ đăng nhập".
- Không nên: offline token cho SPA trong trình duyệt (lộ qua XSS là sống 30 ngày); kéo dài access token lên hàng giờ để "đỡ refresh".
Bob đã logout, nhưng ai đó copy được access token của bob vẫn gọi order-service thành công. Vậy logout có tác dụng gì?
Bạn quan sát đúng. Logout hủy session và refresh token, không làm JWT đã ký "biến mất". order-service verify chữ ký offline nên không biết session đã hết.
| Cách giảm rủi ro | Đánh đổi |
|---|---|
| Access token 5 phút | Cửa sổ rủi ro tối đa 5 phút |
Token introspection (/token/introspect) cho API nhạy cảm | Mỗi request gọi Keycloak, thêm độ trễ |
Nhận back-channel logout ở gateway, giữ deny-list sid trong Redis tới exp | Thêm hạ tầng |
| Sender-constrained token (DPoP, mTLS) | Token copy không dùng được nếu thiếu key; client phức tạp hơn |
Phần lớn hệ thống chọn: token ngắn + introspection cho vài endpoint quan trọng.
Restart Keycloak thì user có bị đá ra hết không? Session lưu ở đâu?
Phụ thuộc phiên bản. Từ Keycloak 25, tính năng persistent user sessions bật mặc định: session được lưu xuống database, cache Infinispan chỉ giữ phần đang dùng. Restart hoặc rolling upgrade, user vẫn còn đăng nhập. Bản cũ hơn giữ online session chỉ trong Infinispan: restart tất cả node là mất (offline session thì vẫn ở DB).
Clustering và cache xem bài 12.6.
Phỏng vấn hỏi SSO session, refresh token và logout trong Keycloak thì trả lời sao?
Q1. SSO trong Keycloak hoạt động thế nào?
🇻🇳 Tiếng Việt:
Lần đầu login, Keycloak tạo SSO session và đặt cookie trên domain của nó. App khác trong cùng realm redirect sang thì Keycloak thấy cookie, phát code ngay không hỏi password. Mỗi app vẫn nhận token riêng.
🇬🇧 English:
On first login Keycloak creates an SSO session and sets a cookie on its own domain. When another client in the same realm redirects there, Keycloak sees the cookie and issues a code without asking for the password. Each application still gets its own tokens.
Q2. Access token, refresh token, offline token khác nhau?
🇻🇳 Tiếng Việt:
Access token ngắn, gửi tới API. Refresh token gắn với SSO session, dùng xin access token mới, chết khi session hết. Offline token xin bằng scope
offline_access, sống độc lập với SSO session, hợp cho mobile hoặc job, phải lưu an toàn.
🇬🇧 English:
The access token is short-lived and sent to APIs. The refresh token is bound to the SSO session and gets new access tokens until the session ends. An offline token, requested with
offline_access, survives the SSO session, suits mobile apps or jobs, and must be stored securely.
Q3. Logout xong access token cũ có còn dùng được không?
🇻🇳 Tiếng Việt:
Còn, tới khi hết hạn, vì service verify JWT offline. Logout hủy session và refresh token. Em giảm rủi ro bằng token ngắn, back-channel logout, và introspection cho API nhạy cảm.
🇬🇧 English:
Yes, until it expires, because services verify the JWT offline. Logout kills the session and refresh tokens. I reduce the risk with short-lived tokens, back-channel logout and introspection for sensitive APIs.
Cách nhớ ngắn gọn
- SSO = cookie trên domain Keycloak, trong một realm.
- Session > refresh > access về thời gian sống; offline đứng riêng.
- Logout thật = gọi
/logoutcủa Keycloak; back-channel cho backend. - JWT đã phát không thu hồi được: giữ nó ngắn.
Tự kiểm tra
Chỉ xóa token trong localStorage có phải logout không?
Không. SSO session và cookie trên Keycloak còn, login lại không cần password.
Tham số nào cần cho RP-initiated logout ở Keycloak 26?
id_token_hint (hoặc client_id) và post_logout_redirect_uri đã đăng ký.
Back-channel logout khác front-channel thế nào?
Keycloak gọi server-to-server tới backend app với logout token, không phụ thuộc cookie trình duyệt.
Revoke Refresh Token bật để làm gì?
Rotation: refresh token chỉ dùng một lần; dùng lại token cũ bị từ chối, phát hiện token bị đánh cắp.