Các flow đăng nhập: Auth Code + PKCE, Client Credentials

4 câu hỏi~25 phútTừ khóa: Standard flow, PKCE S256, service account, device flow, direct access grants, redirect URI, web origins

Tiếp đi: cấu hình flow đăng nhập cho từng client của shop-app trong Keycloak.

Lý thuyết các grant đã có ở 4.4.7, code Spring gọi Client Credentials ở 4.4.8. Bài này nói phần Keycloak: bật công tắc nào, điền ô nào, sai ở đâu.

1. Bài toán

ClientAi dùngFlow cần
shop-web, shop-mobileKhách hàngAuthorization Code + PKCE
shop-adminNhân viên, có backendAuthorization Code (confidential)
order-serviceService gọi payment-serviceClient Credentials
Máy POS ở cửa hàngThiết bị không có bàn phím tốtDevice Authorization

2. Vấn đề

3. Giải pháp: tên công tắc trong Keycloak

Admin console (Capability config)Grant OAuth2
Standard flowAuthorization Code
Service accounts rolesClient Credentials
OAuth 2.0 Device Authorization GrantDevice flow
Direct access grantsResource Owner Password (tắt)
Implicit flowImplicit (tắt)

Mỗi client chỉ bật đúng flow nó cần.

4. Code từng bước

Bước 1: shop-web: Client authentication Off, Standard flow On, Direct access grants Off. Advanced → PKCE method S256.

Valid redirect URIs:        https://shop.example.com/callback
                            http://localhost:5173/callback   (chỉ realm dev)
Valid post logout redirect: https://shop.example.com/
Web origins:                https://shop.example.com

Bước 2: shop-mobile: tương tự, redirect URI dạng app link https://shop.example.com/app/callback hoặc custom scheme com.example.shop:/callback.

Bước 3: order-service: Client authentication On, Standard flow Off, Service accounts On. Tab Credentials lấy secret (đưa vào secret manager). Tab Service account roles: gán payment-service: payment:charge.

Bước 4: Test bằng curl

KC=http://localhost:8080/realms/shop/protocol/openid-connect

# Client Credentials
curl -s -X POST $KC/token \
  -d grant_type=client_credentials \
  -d client_id=order-service \
  -d client_secret="$ORDER_SERVICE_SECRET" | jq -r .access_token

# Authorization Code + PKCE (giả lập SPA)
VERIFIER=$(openssl rand -base64 48 | tr -d '=+/\n' | cut -c1-64)
CHALLENGE=$(printf %s "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=')
echo "$KC/auth?client_id=shop-web&response_type=code&scope=openid&redirect_uri=http://localhost:5173/callback&code_challenge=$CHALLENGE&code_challenge_method=S256&state=xyz"
# mở URL, login, lấy ?code=... trên thanh địa chỉ
curl -s -X POST $KC/token \
  -d grant_type=authorization_code -d client_id=shop-web \
  -d code="$CODE" -d redirect_uri=http://localhost:5173/callback \
  -d code_verifier="$VERIFIER"

# Device flow
curl -s -X POST $KC/auth/device -d client_id=shop-pos
# → user_code, verification_uri: user mở trên điện thoại, nhập code
curl -s -X POST $KC/token -d grant_type=urn:ietf:params:oauth:grant-type:device_code \
  -d client_id=shop-pos -d device_code="$DEVICE_CODE"

5. Cách hoạt động

Authorization Code + PKCE (shop-web)
SPA ──(1) /auth?code_challenge=H(v)──> Keycloak: kiểm redirect_uri khớp whitelist
User login ở trang Keycloak (SPA không thấy password)
Keycloak ──(2) 302 redirect_uri?code=abc──> SPA
SPA ──(3) POST /token code=abc + code_verifier=v──> Keycloak: H(v) == challenge?
        (CORS: Origin phải nằm trong Web origins)
Keycloak ──(4) access_token, id_token, refresh_token──> SPA

Client Credentials (order-service)
order-service ──POST /token client_id+secret──> Keycloak
             <── access_token (sub = service-account-order-service)
order-service ──Bearer──> payment-service

6. So sánh

FlowCó user?ClientTrạng thái
Auth Code + PKCECóSPA, mobile, web backendMặc định nên dùng
Client CredentialsKhôngService, jobDùng
DeviceCó, trên thiết bị khácTV, POS, CLIDùng khi cần
Password (ROPC)CóBất kỳDeprecated (OAuth 2.1 bỏ)
ImplicitCóSPA cũDeprecated

7. Khi nào nên dùng / không nên dùng

Mobile app tự làm màn login đẹp rồi gửi username/password lên token endpoint (Direct access grants) thì tiện hơn redirect. Sao lại bị cấm?

Tiện thật, nhưng phá mục đích của IdP:

Muốn giao diện đẹp thì làm theme cho trang login của Keycloak (bài 12.6), và mobile mở bằng system browser (Custom Tabs / ASWebAuthenticationSession), không dùng WebView. ROPC chỉ còn chấp nhận được trong test tự động ở realm dev.

Mình gặp hai lỗi:

  • Trang Keycloak báo "Invalid parameter: redirect_uri".
  • SPA gọi /token bị lỗi CORS.

Mình phải sửa ở đâu?

Hai lỗi, hai ô khác nhau trong cấu hình client:

LỗiNguyên nhân hay gặpSửa
Invalid redirect_uriLệch một ký tự: http vs https, thiếu port, dư dấu / cuối, sau proxy thì Keycloak thấy host khácThêm chính xác URI vào Valid redirect URIs; wildcard chỉ ở cuối path và chỉ ở dev
CORS ở /tokenWeb origins trống hoặc khác origin của SPAWeb origins = https://shop.example.com (không có path) hoặc + = lấy theo redirect URIs

Lưu ý: CORS cho API của order-service là chuyện của service đó (bài 11.5), không phải Keycloak.

Phỏng vấn hỏi cấu hình login flow trong Keycloak thì trả lời sao?

Q1. SPA và mobile dùng flow nào, cấu hình gì trong Keycloak?

🇻🇳 Tiếng Việt:

Authorization Code + PKCE với public client: tắt client authentication, bật Standard flow, PKCE S256, tắt Direct access grants, redirect URI chính xác và Web origins đúng origin của SPA. Mobile mở system browser, không dùng WebView.

🇬🇧 English:

Authorization Code with PKCE on a public client: client authentication off, standard flow on, PKCE S256, direct access grants off, exact redirect URIs and web origins set to the SPA origin. Mobile apps use the system browser, not a WebView.

Q2. Service gọi service thì sao?

🇻🇳 Tiếng Việt:

Confidential client bật Service accounts, dùng Client Credentials. Gán role cho service account theo nguyên tắc least privilege, secret lưu trong secret manager hoặc dùng private key JWT.

🇬🇧 English:

A confidential client with service accounts enabled, using Client Credentials. Its service account gets only the roles it needs, and the secret lives in a secret manager, or we use private key JWT authentication.

Q3. Vì sao Resource Owner Password Credentials bị deprecated?

🇻🇳 Tiếng Việt:

Vì app phải nhận password của user, không hỗ trợ MFA, social login, SSO, và OAuth 2.1 đã bỏ nó. Cần UI đẹp thì custom theme của Keycloak.

🇬🇧 English:

Because the application handles the user's password, it cannot support MFA, social login or SSO, and OAuth 2.1 removes it. If we need a custom look, we customize the Keycloak login theme.

Cách nhớ ngắn gọn


Tự kiểm tra

Token endpoint của realm shop là gì?

/realms/shop/protocol/openid-connect/token.

PKCE chống tấn công gì?

Kẻ đánh cắp authorization code không đổi được thành token vì không có code_verifier.

Web origins để làm gì?

Cho phép CORS từ origin của SPA khi gọi endpoint của Keycloak như /token.

Token của service account có sub là ai?

User ẩn service-account-<clientId>; role gán ở tab Service account roles.