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

Lesson 10: Authentication Flows - Customize authentication flows

Understand Authentication Flows in Keycloak, Browser Flow, Direct Grant Flow, Registration Flow, Reset Credentials Flow, First Broker Login Flow. Create custom flows, add executions and sub-flows, conditional authenticators (Condition - sub-flow executed, Condition - client scope), Step-up Authentication, ACR to Level of Authentication (LoA) mapping and session limits.

🔒 DevSecOps — Lesson 10 Lesson 10: Authentication Flows - Customization authentication flow

Keycloak from Basic to Advanced

Part 3: Authentication, MFA and Identity Brokering

xdev.asia

1. Authentication Flows — Overview

Authentication Flow in Keycloak is the sequence of authentication steps that a user must go through when logging in, registering, or performing security actions. Each flow consists of ordered executions (authenticators) and can be nested via sub-flows.

To view and manage flows, go to Admin Console → Authentication → Flows.

1.1 Built-in Authentication Flows

Keycloak provides default flows:

FlowDescriptionWhen is triggered
Browser FlowBrowser login flowUser accessing application for the first time or session expired
Direct Grant FlowDirect authentication with username/password (Resource Owner Password)API call with grant_type=password
Registration FlowNew account registration flowUser clicks "Register" on login page
Reset Credentials FlowReset password flowUser click "Forgot Password"
First Broker Login FlowFlow for handling first login via Identity ProviderUser logging in via social login for the first time
Docker Authentication FlowAuthentication for Docker registryDocker client pull/push images
HTTP Challenge FlowAuthentication via HTTP headersNon-browser clients (Kerberos, X.509)

1.2 Flow Types

Each flow can contain the following types of elements:

TypeDescription
AuthenticatorA specific authentication step (for example, Username Password Form)
Sub-flowSub-flow contains multiple authenticators — allowing for complex logic
FormDisplays a form for the user to enter information (username, password, OTP...)

2. Browser Flow — Details

The default

Browser Flow has the following structure:

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

How it works:

  1. Cookie: If the user already has a valid session cookie → skip all, login successfully
  2. Kerberos: Disabled by default — if enabled, try Kerberos ticket
  3. Identity Provider Redirector: If there is kc_idp_hint → redirect to that IdP
  4. Forms: Display login form
    • Requires username + password
    • If the user has configured OTP → request to enter OTP code

2.1 Execution Requirements

Each execution in the flow has a requirement that defines the behavior:

RequirementDescriptionWhen to use
RequiredRequired and successfulUsername/Password, OTP when configured
AlternativeOne of the alternatives succeeds enoughCookies OR Forms — just 1 pass
ConditionalSub-flow only executes when the condition is trueConditional OTP — only request OTP if user has setup
DisabledSkip completelyDisable a step without deleting

Important rule:

  • If all executions in the flow are Alternative → only need 1 pass
  • If there is at least 1 Required → all Required must pass, Alternative is ignored
  • Conditional is often used with sub-flow: the first step is the condition checker, the following steps are authenticators

3. Create Custom Authentication Flow

Built-in flows cannot be edited directly. You need duplicate then customize.

3.1 Duplicate and Edit

  1. Go to Authentication → Flows
  2. Select the flow you want to copy, for example Browser
  3. Click Action → Duplicate
  4. New name: My Custom Browser Flow
  5. The new flow will appear with all executions identical to the original

3.2 Add Execution

After duplicating, you can add/delete/reorder executions:

  1. Trong custom flow, click "Add step"
  2. Select authenticator from the list:
    • Username Password Form — Username + password entry form
    • OTP Form — OTP input form code
    • Cookie — Check session cookie
    • Identity Provider Redirector — Redirect to external IdP
    • Deny Access — Deny access
    • Allow Access — Allow access
    • Username Form — Enter only username (separate password)
    • Password Form — Enter only password
    • WebAuthn Authenticator — Authenticate with security key
    • WebAuthn Passwordless Authenticator — Passwordless Authentication
  3. Set appropriate requirements (Required, Alternative, Conditional, Disabled)

3.3 Add Sub-flow

Sub-flow allows grouping multiple executions, creating more complex logic:

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

How to add sub-flow:

  1. Click "Add sub-flow"
  2. Name, for example: MFA Sub-flow
  3. Set requirement: Conditional
  4. Add executions to sub-flow

4. Conditional Authenticators

Conditional authenticators allow to check the condition before executing the sub-flow. If the condition is not met → the entire sub-flow is skipped.

4.1 Available Conditions

ConditionDescription
Condition - User ConfiguredUser has configured the corresponding credential (OTP, WebAuthn...)
Condition - User RoleUser has a specific role
Condition - User AttributeUser has a specific attribute with the desired value
Condition - Client ScopeRequest that contains the specific scope (e.g. acr_values)
Condition - Sub-flow ExecutedThe previous sub-flow was executed successfully

4.2 Example: Conditional OTP according to Role

Request OTP only for users with 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)

Configuration Condition - User Role:

  1. Add Condition - User Role to sub-flow
  2. Click the ⚙️ (Settings) icon next to condition
  3. Enter:
    • Alias: Check Admin Role
    • User role: admin (or realm-management.manage-users for client role)
    • Negate output: Off (On if you want to apply to users who do NOT have roles)

4.3 Condition - Client Scope

Requires MFA when client requests special scope:

# 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 and Level of Authentication (LoA)

Step-up Authentication allows requiring a higher level of authentication for sensitive actions, without forcing the user to completely re-authenticate.

5.1 ACR and Speaker Mapping

ACR (Authentication Context Class Reference) is a claim in the ID Token indicating authentication level has been performed. Keycloak map ACR values ​​to Level of Authentication (LoA) — an integer.

ACR to Speaker mapping configuration:

  1. Go to Authentication → Flows
  2. Open the flow in use (eg: Browser Flow)
  3. Each sub-flow can be assigned a 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 claims specific LoA via acr_values or 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

Results in Token ID:

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

5.3 Check the LoA in the application

// 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 processes grant_type=password — direct authentication without going through the 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

⚠️ Note: Direct Grant (Resource Owner Password Credentials) is not recommended in production. Authorization Code Flow + PKCE should be used instead.

7. Registration Flow

Registration Flow controls the new account registration process:

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 Enable Registration

  1. Go to Realm Settings → Login
  2. On User registration: On
  3. Optional: Turn on Email as username so users can use email as 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

ReCAPTCHA configuration:

  1. Sign up for Google reCAPTCHA v3 at https://www.google.com/recaptcha/admin
  2. In the flow, click ⚙️ next to Recaptcha
  3. Enter:
    • Recaptcha Site Key: your-site-key
    • Recaptcha Secret: your-secret-key
    • Use Recaptcha.net: On (if needed for China)

8. Reset Credentials Flow

Flow handles password reset:

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 allows limiting the number of concurrent sessions per user.

9.1 Configuring Session Limits in Authentication Flow

Add User Session Limits authenticator to 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)

Configure User Session Limits:

  1. Click ⚙️ next to User Session Limits
  2. Configuration:
    • Max Realm Sessions: Total maximum number of sessions in the realm (eg: 3)
    • Max Client Sessions: Maximum number of sessions for 1 client (for example: 1)
    • Behavior when limit reached:
      • Deny new session — Deny new login
      • Terminate oldest session — Terminate oldest session
    • Error message: Custom message when denied (eg: "You have reached your login session limit")

10. Bind Flow to Realm and Client

10.1 Bind Flow cho Realm

After creating the custom flow, bind it as the default flow for realm:

  1. Go to Authentication → Flows
  2. Click tab "Bindings" (or Required Actions)
  3. Select flow for each 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 for specific Client

You can override realm flow for each client:

  1. Go to Clients → select client
  2. Tab "Advanced"
  3. Section "Authentication Flow Overrides":
    • Browser Flow: Select a flow other than realm default
    • Direct Grant Flow: Select another flow

11. Dynamic Flow Selection with Client Policies

From Keycloak 25+, you can use Client Policies to automatically select authentication flow based on client properties.

11.1 Create Client Policy for Flow Selection

  1. Go to Realm Settings → Client Policies → Policies
  2. Create new policy: Secure Clients MFA Policy
  3. Add Condition:
    • Type: client-scopes
    • Scopes: ["mfa-required"]
  4. Add Profile (from 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 are included in 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. Summary

ConceptDescription
Authentication FlowAuthentication step sequence — can be nested via sub-flows
Execution RequirementsRequired, Alternative, Conditional, Disabled
Conditional AuthenticatorsCheck conditions before executing sub-flow
Step-up AuthenticationRequires higher LoA for sensitive action
ACR to LoA MappingMap ACR values sang numeric levels
Session LimitsConcurrent sessions limits per user
Flow BindingBind flows at realm level or per-client override