Chuyển đến nội dung chính

Bài 10: Authentication Flows - Tùy chỉnh luồng xác thực

Hiểu Authentication Flows trong Keycloak, Browser Flow, Direct Grant Flow, Registration Flow, Reset Credentials Flow, First Broker Login Flow. Tạo custom flows, thêm executions và sub-flows, conditional authenticators (Condition - sub-flow executed, Condition - client scope), Step-up Authentication, ACR to Level of Authentication (LoA) mapping và session limits.

🔒 DevSecOps — Bài 10 Bài 10: Authentication Flows - Tùy chỉnh luồng xác thực

Keycloak từ Cơ bản đến Nâng cao

Phần 3: Authentication, MFA và Identity Brokering

xdev.asia

1. Authentication Flows — Tổng quan

Authentication Flow trong Keycloak là chuỗi các bước xác thực mà user phải trải qua khi đăng nhập, đăng ký, hoặc thực hiện các hành động bảo mật. Mỗi flow bao gồm các executions (authenticators) được sắp xếp theo thứ tự và có thể lồng nhau qua sub-flows.

Để xem và quản lý flows, vào Admin Console → Authentication → Flows.

1.1 Built-in Authentication Flows

Keycloak cung cấp sẵn các flows mặc định:

FlowMô tảKhi nào được trigger
Browser FlowLuồng đăng nhập qua browserUser truy cập ứng dụng lần đầu hoặc session hết hạn
Direct Grant FlowXác thực trực tiếp bằng username/password (Resource Owner Password)API call với grant_type=password
Registration FlowLuồng đăng ký tài khoản mớiUser click "Register" trên login page
Reset Credentials FlowLuồng đặt lại mật khẩuUser click "Forgot Password"
First Broker Login FlowLuồng xử lý lần đầu đăng nhập qua Identity ProviderUser đăng nhập qua social login lần đầu
Docker Authentication FlowXác thực cho Docker registryDocker client pull/push images
HTTP Challenge FlowXác thực qua HTTP headersNon-browser clients (Kerberos, X.509)

1.2 Flow Types

Mỗi flow có thể chứa các loại phần tử sau:

TypeMô tả
AuthenticatorMột bước xác thực cụ thể (ví dụ: Username Password Form)
Sub-flowFlow con chứa nhiều authenticators — cho phép tạo logic phức tạp
FormHiển thị form cho user nhập thông tin (username, password, OTP...)

2. Browser Flow — Chi tiết

Browser Flow mặc định có cấu trúc như sau:

Browser Flow
├── Cookie (Alternative)              → Kiểm tra SSO session cookie
├── Kerberos (Disabled)               → Xác thực Kerberos (tắt mặc định)
├── Identity Provider Redirector (Alternative) → Redirect đến IdP nếu có
└── Forms (Alternative)               → Sub-flow xử lý form login
    ├── Username Password Form (Required) → Nhập username + password
    └── Browser - Conditional OTP (Conditional) → Sub-flow OTP
        ├── Condition - User Configured (Required) → Kiểm tra user đã setup OTP
        └── OTP Form (Required)           → Nhập mã OTP

Cách hoạt động:

  1. Cookie: Nếu user đã có session cookie hợp lệ → skip toàn bộ, đăng nhập thành công
  2. Kerberos: Disabled mặc định — nếu enabled sẽ thử Kerberos ticket
  3. Identity Provider Redirector: Nếu có kc_idp_hint → redirect đến IdP đó
  4. Forms: Hiển thị login form
    • Yêu cầu username + password
    • Nếu user đã cấu hình OTP → yêu cầu nhập OTP code

2.1 Execution Requirements

Mỗi execution trong flow có một requirement xác định hành vi:

RequirementMô tảKhi nào sử dụng
RequiredBắt buộc phải thực hiện và thành côngUsername/Password, OTP khi đã cấu hình
AlternativeMột trong các alternatives thành công là đủCookie HOẶC Forms — chỉ cần 1 pass
ConditionalSub-flow chỉ thực thi khi điều kiện đúngConditional OTP — chỉ yêu cầu OTP nếu user đã setup
DisabledBỏ qua hoàn toànTạm tắt một bước mà không xóa

Quy tắc quan trọng:

  • Nếu tất cả executions trong flow là Alternative → chỉ cần 1 cái pass
  • Nếu có ít nhất 1 Required → tất cả Required phải pass, Alternative bị bỏ qua
  • Conditional thường dùng với sub-flow: bước đầu tiên là condition checker, các bước sau là authenticators

3. Tạo Custom Authentication Flow

Built-in flows không thể chỉnh sửa trực tiếp. Bạn cần duplicate rồi customise.

3.1 Duplicate và Chỉnh sửa

  1. Vào Authentication → Flows
  2. Chọn flow muốn copy, ví dụ Browser
  3. Click Action → Duplicate
  4. Đặt tên mới: My Custom Browser Flow
  5. Flow mới sẽ xuất hiện với toàn bộ executions giống bản gốc

3.2 Thêm Execution

Sau khi duplicate, bạn có thể thêm/xóa/sắp xếp lại executions:

  1. Trong custom flow, click "Add step"
  2. Chọn authenticator từ danh sách:
    • Username Password Form — Form nhập username + password
    • OTP Form — Form nhập OTP code
    • Cookie — Kiểm tra session cookie
    • Identity Provider Redirector — Redirect đến external IdP
    • Deny Access — Từ chối truy cập
    • Allow Access — Cho phép truy cập
    • Username Form — Chỉ nhập username (tách riêng password)
    • Password Form — Chỉ nhập password
    • WebAuthn Authenticator — Xác thực bằng security key
    • WebAuthn Passwordless Authenticator — Xác thực không mật khẩu
  3. Đặt requirement phù hợp (Required, Alternative, Conditional, Disabled)

3.3 Thêm Sub-flow

Sub-flow cho phép nhóm nhiều executions lại, tạo logic phức tạp hơn:

My Custom Browser Flow
├── Cookie (Alternative)
├── Identity Provider Redirector (Alternative)
└── My Login Forms (Alternative)              ← Sub-flow
    ├── Username Password Form (Required)
    └── MFA Sub-flow (Conditional)            ← Sub-flow lồng nhau
        ├── Condition - User Configured (Required)
        ├── OTP Form (Alternative)            ← Cho chọn OTP...
        └── WebAuthn Authenticator (Alternative) ← ...hoặc Security Key

Cách thêm sub-flow:

  1. Click "Add sub-flow"
  2. Đặt tên, ví dụ: MFA Sub-flow
  3. Set requirement: Conditional
  4. Thêm executions vào sub-flow

4. Conditional Authenticators

Conditional authenticators cho phép kiểm tra điều kiện trước khi thực thi sub-flow. Nếu điều kiện không thoả → toàn bộ sub-flow bị bỏ qua.

4.1 Các Condition có sẵn

ConditionMô tả
Condition - User ConfiguredUser đã cấu hình credential tương ứng (OTP, WebAuthn...)
Condition - User RoleUser có role cụ thể
Condition - User AttributeUser có attribute cụ thể với giá trị mong muốn
Condition - Client ScopeRequest có chứa scope cụ thể (ví dụ: acr_values)
Condition - Sub-flow ExecutedSub-flow trước đó đã được thực thi thành công

4.2 Ví dụ: Conditional OTP theo Role

Yêu cầu OTP chỉ cho users có role admin:

My Custom Browser Flow
├── Cookie (Alternative)
└── Forms (Alternative)
    ├── Username Password Form (Required)
    └── Admin OTP Sub-flow (Conditional)
        ├── Condition - User Role (Required)    → Config: role = "admin"
        └── OTP Form (Required)

Cấu hình Condition - User Role:

  1. Thêm Condition - User Role vào sub-flow
  2. Click biểu tượng ⚙️ (Settings) bên cạnh condition
  3. Nhập:
    • Alias: Check Admin Role
    • User role: admin (hoặc realm-management.manage-users cho client role)
    • Negate output: Off (On nếu muốn áp dụng cho user KHÔNG có role)

4.3 Condition - Client Scope

Yêu cầu MFA khi client request scope đặc biệt:

# Authorization request yêu cầu MFA
GET /realms/myrealm/protocol/openid-connect/auth?
  client_id=my-app&
  scope=openid profile mfa-required&
  response_type=code&
  redirect_uri=https://myapp.example.com/callback
My Custom Browser Flow
├── Cookie (Alternative)
└── Forms (Alternative)
    ├── Username Password Form (Required)
    └── MFA When Requested (Conditional)
        ├── Condition - Client Scope (Required)  → Config: scope = "mfa-required"
        └── OTP Form (Required)

5. Step-up Authentication và Level of Authentication (LoA)

Step-up Authentication cho phép yêu cầu mức xác thực cao hơn cho các hành động nhạy cảm, mà không bắt user xác thực lại hoàn toàn.

5.1 ACR và LoA Mapping

ACR (Authentication Context Class Reference) là claim trong ID Token cho biết mức độ xác thực đã được thực hiện. Keycloak map ACR values sang Level of Authentication (LoA) — một số nguyên.

Cấu hình ACR to LoA mapping:

  1. Vào Authentication → Flows
  2. Mở flow đang sử dụng (ví dụ: Browser Flow)
  3. Mỗi sub-flow có thể gán một LoA level
My Step-up Browser Flow
├── Cookie (Alternative)                          → LoA: không set
└── Login Forms (Alternative)
    ├── Username Password Form (Required)         → LoA Level 1
    └── Step-up MFA (Conditional)
        ├── Condition - Level of Authentication (Required)
        └── OTP Sub-flow (Conditional)            → LoA Level 2
            ├── Condition - User Configured (Required)
            └── OTP Form (Required)

Default LoA mapping (Authentication → Flows → gear icon):

# Trong Realm Settings → General → ACR to LoA Mapping:
# Hoặc cấu hình trong flow
{
  "acr_to_loa_mapping": {
    "urn:keycloak:loa:1": 1,    // Password only
    "urn:keycloak:loa:2": 2,    // Password + OTP
    "urn:keycloak:loa:3": 3,    // Password + Security Key
    "gold": 2,                   // Custom ACR value
    "platinum": 3                // Custom ACR value
  }
}

5.2 Request Step-up Authentication

Client yêu cầu LoA cụ thể qua acr_values hoặc claims parameter:

# Sử dụng acr_values (voluntary — không bắt buộc)
GET /realms/myrealm/protocol/openid-connect/auth?
  client_id=my-app&
  scope=openid&
  acr_values=gold&
  response_type=code&
  redirect_uri=https://myapp.example.com/callback

# Sử dụng claims parameter (essential — bắt buộc LoA)
GET /realms/myrealm/protocol/openid-connect/auth?
  client_id=my-app&
  scope=openid&
  claims={"id_token":{"acr":{"essential":true,"values":["gold"]}}}&
  response_type=code&
  redirect_uri=https://myapp.example.com/callback

Kết quả trong ID Token:

{
  "acr": "gold",
  "sub": "user-123",
  "iss": "https://keycloak.example.com/realms/myrealm",
  ...
}

5.3 Kiểm tra LoA trong ứng dụng

// Spring Security — kiểm tra ACR level
@GetMapping("/sensitive-action")
public ResponseEntity<?> sensitiveAction(
        @AuthenticationPrincipal OidcUser user) {
    
    String acr = user.getIdToken().getClaimAsString("acr");
    
    if (!"gold".equals(acr)) {
        // Redirect user để step-up authentication
        String stepUpUrl = keycloakBaseUrl + 
            "/realms/myrealm/protocol/openid-connect/auth" +
            "?client_id=my-app" +
            "&scope=openid" +
            "&acr_values=gold" + 
            "&prompt=login" +
            "&response_type=code" +
            "&redirect_uri=" + redirectUri;
        
        return ResponseEntity.status(302)
            .header("Location", stepUpUrl)
            .build();
    }
    
    return ResponseEntity.ok("Sensitive data here");
}

6. Direct Grant Flow

Direct Grant Flow xử lý grant_type=password — xác thực trực tiếp không qua browser:

Direct Grant Flow (mặc định)
├── Username Validation (Required)    → Kiểm tra username tồn tại
├── Password (Required)               → Verify password
└── Direct Grant - Conditional OTP (Conditional)
    ├── Condition - User Configured (Required)
    └── OTP (Required)
# Ví dụ: Direct Grant request
curl -X POST \
  https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=password' \
  -d 'client_id=my-backend' \
  -d 'client_secret=my-secret' \
  -d '[email protected]' \
  -d 'password=user-password' \
  -d 'totp=123456'    # Nếu user đã setup OTP

⚠️ Lưu ý: Direct Grant (Resource Owner Password Credentials) không được khuyến khích trong production. Nên sử dụng Authorization Code Flow + PKCE thay thế.

7. Registration Flow

Registration Flow kiểm soát quá trình đăng ký tài khoản mới:

Registration Flow (mặc định)
└── Registration Form (Required)
    ├── Registration User Profile (Required)  → Nhập thông tin profile
    ├── Password Validation (Required)        → Nhập + confirm password
    └── Recaptcha (Disabled)                  → reCAPTCHA (tắt mặc định)

7.1 Bật Registration

  1. Vào Realm Settings → Login
  2. Bật User registration: On
  3. Tuỳ chọn: Bật Email as username để user dùng email làm username

7.2 Custom Registration Flow

My Registration Flow
└── Registration Form (Required)
    ├── Registration User Profile (Required)
    ├── Password Validation (Required)
    ├── Recaptcha (Required)                → Bật reCAPTCHA
    └── Terms and Conditions (Required)     → Yêu cầu đồng ý điều khoản

Cấu hình reCAPTCHA:

  1. Đăng ký Google reCAPTCHA v3 tại https://www.google.com/recaptcha/admin
  2. Trong flow, click ⚙️ bên cạnh Recaptcha
  3. Nhập:
    • Recaptcha Site Key: your-site-key
    • Recaptcha Secret: your-secret-key
    • Use Recaptcha.net: On (nếu cần cho China)

8. Reset Credentials Flow

Flow xử lý quá trình đặt lại mật khẩu:

Reset Credentials Flow (mặc định)
├── Choose User (Required)                → User nhập username/email
├── Send Reset Email (Required)           → Gửi email reset link
├── Reset Password (Required)             → Form nhập mật khẩu mới
└── Reset - Conditional OTP (Conditional) → OTP nếu đã cấu hình
    ├── Condition - User Configured (Required)
    └── Reset OTP (Required)

9. Session Limits

Keycloak cho phép giới hạn số lượng sessions đồng thời mỗi user.

9.1 Cấu hình Session Limits trong Authentication Flow

Thêm User Session Limits authenticator vào flow:

My Custom Browser Flow
├── Cookie (Alternative)
└── Forms (Alternative)
    ├── Username Password Form (Required)
    ├── Browser - Conditional OTP (Conditional)
    │   ├── Condition - User Configured (Required)
    │   └── OTP Form (Required)
    └── User Session Limits (Required)

Cấu hình User Session Limits:

  1. Click ⚙️ bên cạnh User Session Limits
  2. Cấu hình:
    • Max Realm Sessions: Tổng số sessions tối đa trong realm (ví dụ: 3)
    • Max Client Sessions: Số sessions tối đa cho 1 client (ví dụ: 1)
    • Behavior when limit reached:
      • Deny new session — Từ chối đăng nhập mới
      • Terminate oldest session — Đá session cũ nhất
    • Error message: Custom message khi bị deny (ví dụ: "Bạn đã đạt giới hạn phiên đăng nhập")

10. Bind Flow vào Realm và Client

10.1 Bind Flow cho Realm

Sau khi tạo custom flow, bind nó làm flow mặc định cho realm:

  1. Vào Authentication → Flows
  2. Click tab "Bindings" (hoặc Required Actions)
  3. Chọn flow cho từng binding:
    • Browser Flow: My Custom Browser Flow
    • Direct Grant Flow: Direct Grant
    • Registration Flow: My Registration Flow
    • Reset Credentials Flow: Reset Credentials

10.2 Bind Flow cho Client cụ thể

Bạn có thể override realm flow cho từng client:

  1. Vào Clients → chọn client
  2. Tab "Advanced"
  3. Mục "Authentication Flow Overrides":
    • Browser Flow: Chọn flow khác realm default
    • Direct Grant Flow: Chọn flow khác

11. Dynamic Flow Selection với Client Policies

Từ Keycloak 25+, bạn có thể sử dụng Client Policies để tự động chọn authentication flow dựa trên client properties.

11.1 Tạo Client Policy cho Flow Selection

  1. Vào Realm Settings → Client Policies → Policies
  2. Tạo policy mới: Secure Clients MFA Policy
  3. Thêm Condition:
    • Type: client-scopes
    • Scopes: ["mfa-required"]
  4. Thêm Profile (từ Client Profiles):
    • Profile executor: override browser flow
// Client Policy — ví dụ export JSON
{
  "policies": [
    {
      "name": "Secure Clients MFA Policy",
      "description": "Enforce MFA for clients with mfa-required scope",
      "enabled": true,
      "conditions": [
        {
          "condition": "client-scopes",
          "configuration": {
            "scopes": ["mfa-required"],
            "type": "DEFAULT"
          }
        }
      ],
      "profiles": ["mfa-enforced-profile"]
    }
  ]
}

12. Export/Import Authentication Flows

Authentication flows được bao gồm trong realm export:

# Export realm bao gồm flows
/opt/keycloak/bin/kc.sh export \
  --dir /opt/keycloak/data/export \
  --realm myrealm

# Trong file realm-export.json, flows nằm ở:
# "authenticationFlows": [...]
# "authenticationExecutions": [...]

Partial import via Admin REST API:

# Lấy danh sách flows
curl -s -X GET \
  "https://keycloak.example.com/admin/realms/myrealm/authentication/flows" \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq '.[].alias'

# Export 1 flow cụ thể
FLOW_ID=$(curl -s -X GET \
  "https://keycloak.example.com/admin/realms/myrealm/authentication/flows" \
  -H "Authorization: Bearer $ADMIN_TOKEN" | \
  jq -r '.[] | select(.alias=="My Custom Browser Flow") | .id')

curl -s -X GET \
  "https://keycloak.example.com/admin/realms/myrealm/authentication/flows/$FLOW_ID/executions" \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq .

13. Tóm tắt

Khái niệmMô tả
Authentication FlowChuỗi bước xác thực — có thể lồng nhau qua sub-flows
Execution RequirementsRequired, Alternative, Conditional, Disabled
Conditional AuthenticatorsKiểm tra điều kiện trước khi thực thi sub-flow
Step-up AuthenticationYêu cầu LoA cao hơn cho hành động nhạy cảm
ACR to LoA MappingMap ACR values sang numeric levels
Session LimitsGiới hạn concurrent sessions per user
Flow BindingBind flows ở realm level hoặc per-client override