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

Bài 5: Roles, Permissions và Access Control

Realm roles, client roles, composite roles, role mappings cho users và groups, default roles, service account roles. Fine-grained admin permissions V2, realm administration delegation, resource-specific permissions, policies và permission evaluation.

Keycloak RBAC & Fine-grained Permissions

Mô hình RBAC và Fine-grained Admin Permissions V2 trong Keycloak

1. Tổng quan về Roles trong Keycloak

Roles trong Keycloak là cơ chế chính để phân quyền truy cập. Ứng dụng kiểm tra roles của user (thông qua claims trong token) để quyết định user được phép làm gì. Keycloak hỗ trợ hai loại roles: Realm Roles và Client Roles.

Realm Roles vs Client Roles

Đặc điểmRealm RolesClient Roles
Phạm viToàn bộ realmChỉ trong client cụ thể
Use caseVai trò chung (Admin, User, Manager)Vai trò riêng cho ứng dụng (editor, viewer)
NamespaceUnique trong realmUnique trong client
Token claimrealm_access.rolesresource_access.{client}.roles

2. Realm Roles

2.1 Realm Roles mặc định

Keycloak tạo sẵn một số realm roles:

  • default-roles-{realm} — composite role chứa các roles mặc định cho users mới

  • offline_access — cho phép lấy offline token (refresh token dài hạn)

  • uma_authorization — cho phép sử dụng UMA (User-Managed Access)

2.2 Tạo Realm Role

Qua Admin Console:

  1. Click Realm roles trong sidebar

  2. Click Create role

  3. Nhập:

    • Role name: admin
    • Description: Full administrator access
  4. Click Save

Qua Admin CLI:

# Tạo realm roles
bin/kcadm.sh create roles -r my-company -s name=admin -s description="Full administrator access"
bin/kcadm.sh create roles -r my-company -s name=manager -s description="Manager with limited admin access"
bin/kcadm.sh create roles -r my-company -s name=user -s description="Regular user"
bin/kcadm.sh create roles -r my-company -s name=viewer -s description="Read-only access"

Xem danh sách realm roles

bin/kcadm.sh get roles -r my-company --fields name,description

Qua REST API:

# Tạo realm role
curl -s -X POST \
  "http://localhost:8080/admin/realms/my-company/roles" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "admin",
    "description": "Full administrator access",
    "composite": false
  }'

Lấy danh sách realm roles

curl -s -X GET
"http://localhost:8080/admin/realms/my-company/roles"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].name'

3. Client Roles

Client roles được sử dụng khi ứng dụng cần roles riêng — ví dụ ứng dụng CMS có roles editor, author, reviewer khác với roles của ứng dụng HR.

3.1 Tạo Client Role

Qua Admin Console:

  1. Vào Clients → chọn client (ví dụ: my-web-app)

  2. Tab Roles

  3. Click Create role

  4. Nhập name và description

Qua CLI:

# Lấy client ID
CLIENT_UUID=$(bin/kcadm.sh get clients -r my-company -q clientId=my-web-app --fields id --format csv --noquotes)

Tạo client roles

bin/kcadm.sh create clients/$CLIENT_UUID/roles -r my-company
-s name=editor -s description="Can create and edit content"

bin/kcadm.sh create clients/$CLIENT_UUID/roles -r my-company
-s name=author -s description="Can create content"

bin/kcadm.sh create clients/$CLIENT_UUID/roles -r my-company
-s name=reviewer -s description="Can review and approve content"

bin/kcadm.sh create clients/$CLIENT_UUID/roles -r my-company
-s name=content-admin -s description="Full content management"

Xem client roles

bin/kcadm.sh get clients/$CLIENT_UUID/roles -r my-company --fields name,description

Qua REST API:

curl -s -X POST \
  "http://localhost:8080/admin/realms/my-company/clients/$CLIENT_UUID/roles" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "editor",
    "description": "Can create and edit content"
  }'

3.2 Client Roles trong Token

Client roles xuất hiện trong access token dưới claim resource_access:

{
  "realm_access": {
    "roles": ["user", "offline_access"]
  },
  "resource_access": {
    "my-web-app": {
      "roles": ["editor", "author"]
    },
    "my-api": {
      "roles": ["read", "write"]
    },
    "account": {
      "roles": ["manage-account", "view-profile"]
    }
  }
}

4. Composite Roles

Composite roles là roles chứa một hoặc nhiều roles con (realm roles và/hoặc client roles). Khi user được gán composite role, user tự động có tất cả roles con.

4.1 Tạo Composite Role

Qua Admin Console:

  1. Vào Realm roles → chọn role (ví dụ: admin)

  2. Tab Action → Add associated roles

  3. Chọn các roles cần thêm (realm roles và/hoặc client roles)

  4. Click Assign

Qua CLI:

# Tạo composite role: "manager" chứa "user" và "viewer"
bin/kcadm.sh add-roles \
  -r my-company \
  --rname manager \
  --rolename user \
  --rolename viewer

Thêm client roles vào composite role

bin/kcadm.sh add-roles
-r my-company
--rname manager
--cclientid my-web-app
--rolename editor
--rolename reviewer

Ví dụ hierarchy:

admin (composite)
├── manager (composite)
│   ├── user (realm role)
│   ├── viewer (realm role)
│   ├── my-web-app/editor (client role)
│   └── my-web-app/reviewer (client role)
├── my-web-app/content-admin (client role)
└── account/manage-account (client role)

Lưu ý: Khi user được gán role admin, user sẽ có tất cả roles trong cây hierarchy: admin, manager, user, viewer, editor, reviewer, content-admin, manage-account.

4.2 Xem Composite Roles

# Xem roles con của composite role
bin/kcadm.sh get-roles -r my-company --rname admin --effective

REST API

curl -s -X GET
"http://localhost:8080/admin/realms/my-company/roles/admin/composites"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].name'

5. Role Mappings

5.1 Gán Role cho User

Qua Admin Console:

  1. Vào Users → chọn user

  2. Tab Role mapping

  3. Click Assign role

  4. Chọn realm roles hoặc filter by client để chọn client roles

  5. Click Assign

Qua CLI:

# Gán realm roles cho user
bin/kcadm.sh add-roles \
  -r my-company \
  --uusername john.doe \
  --rolename admin

Gán client roles cho user

bin/kcadm.sh add-roles
-r my-company
--uusername john.doe
--cclientid my-web-app
--rolename editor

Xem roles của user (bao gồm effective roles từ composite và groups)

bin/kcadm.sh get-roles
-r my-company
--uusername john.doe
--effective

Qua REST API:

# Lấy role representation
ROLE_ID=$(curl -s -X GET \
  "http://localhost:8080/admin/realms/my-company/roles/admin" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq -r '.id')

ROLE_NAME=$(curl -s -X GET
"http://localhost:8080/admin/realms/my-company/roles/admin"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq -r '.name')

Gán realm role cho user

curl -s -X POST
"http://localhost:8080/admin/realms/my-company/users/$USER_ID/role-mappings/realm"
-H "Authorization: Bearer $ACCESS_TOKEN"
-H "Content-Type: application/json"
-d "[$(curl -s -X GET
"http://localhost:8080/admin/realms/my-company/roles/admin"
-H "Authorization: Bearer $ACCESS_TOKEN")]"

5.2 Gán Role cho Group

Khi gán role cho group, tất cả members của group (và sub-groups) sẽ kế thừa role đó:

# Qua CLI
bin/kcadm.sh add-roles \
  -r my-company \
  --gname Engineering \
  --rolename user

bin/kcadm.sh add-roles \
  -r my-company \
  --gname Engineering \
  --cclientid my-web-app \
  --rolename viewer

# Qua REST API
curl -s -X POST \
  "http://localhost:8080/admin/realms/my-company/groups/$GROUP_ID/role-mappings/realm" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d "[$(curl -s -X GET \
    "http://localhost:8080/admin/realms/my-company/roles/user" \
    -H "Authorization: Bearer $ACCESS_TOKEN")]"

5.3 Effective Roles

Effective roles của user = roles được gán trực tiếp + roles kế thừa từ groups + roles từ composite roles:

# Xem effective realm roles
curl -s -X GET \
  "http://localhost:8080/admin/realms/my-company/users/$USER_ID/role-mappings/realm/composite" \
  -H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].name'

Xem effective client roles

curl -s -X GET
"http://localhost:8080/admin/realms/my-company/users/$USER_ID/role-mappings/clients/$CLIENT_UUID/composite"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].name'

6. Default Roles

Default roles tự động được gán cho mọi user mới khi tạo tài khoản hoặc đăng ký.

6.1 Cấu hình Default Roles

Qua Admin Console:

  1. Vào Realm roles

  2. Tìm role default-roles-{realm} (ví dụ: default-roles-my-company)

  3. Click vào role → tab Action → Add associated roles

  4. Chọn roles muốn set làm default

Qua CLI:

# Thêm role vào default roles
bin/kcadm.sh add-roles \
  -r my-company \
  --rname default-roles-my-company \
  --rolename user \
  --rolename offline_access

Thêm client role vào default roles

bin/kcadm.sh add-roles
-r my-company
--rname default-roles-my-company
--cclientid my-web-app
--rolename viewer

Sau đó, mọi user mới tạo sẽ tự động có roles: user, offline_access, my-web-app/viewer.

7. Service Account Roles

Service accounts được sử dụng cho communication giữa services (machine-to-machine) — không cần user interaction.

7.1 Bật Service Account cho Client

  1. Vào Clients → chọn hoặc tạo client

  2. Tab Settings:

    • Client authentication: ON
    • Service accounts roles: ON
    • Authorization: OFF (trừ khi cần authorization services)
  3. Click Save

7.2 Gán Role cho Service Account

# Qua Admin Console:
# Clients → chọn client → tab "Service account roles" → Assign role

Qua CLI - lấy service account user

SA_USER_ID=$(bin/kcadm.sh get clients/$CLIENT_UUID/service-account-user -r my-company --fields id --format csv --noquotes)

Gán realm roles

bin/kcadm.sh add-roles
-r my-company
--uid $SA_USER_ID
--rolename admin

Gán client roles (realm-management) cho API access

bin/kcadm.sh add-roles
-r my-company
--uid $SA_USER_ID
--cclientid realm-management
--rolename manage-users
--rolename view-users
--rolename manage-clients

7.3 Sử dụng Service Account

# Lấy access token cho service account (client credentials grant)
ACCESS_TOKEN=$(curl -s -X POST \
  "http://localhost:8080/realms/my-company/protocol/openid-connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=my-backend-service" \
  -d "client_secret=YOUR_CLIENT_SECRET" | jq -r '.access_token')

Sử dụng token để gọi API

curl -s -X GET
"http://localhost:8080/admin/realms/my-company/users"
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.[].username'

8. Fine-grained Admin Permissions V2

Fine-grained admin permissions V2 (từ Keycloak 26+) cho phép kiểm soát chi tiết ai có thể quản lý resources nào trong Admin Console — thay vì chỉ dùng realm-management client roles thô.

8.1 Bật Fine-grained Admin Permissions

  1. Vào Realm settings → General

  2. Tìm Admin Permissions → bật Fine-grained admin permissions (V2)

  3. Keycloak sẽ tạo permission management resources trong realm

Lưu ý: Đây là tính năng preview trong Keycloak 26.x. Trong production, cần đánh giá kỹ trước khi bật.

8.2 Resource Permissions

Sau khi bật, bạn có thể tạo permissions cho các resources:

Users permissions:

PermissionMô tả
viewXem danh sách và chi tiết users
manageTạo, sửa, xóa users
map-rolesGán/gỡ roles cho users
manage-group-membershipThêm/xóa users khỏi groups
impersonateImpersonate users

Groups permissions:

PermissionMô tả
viewXem groups
manageTạo, sửa, xóa groups
view-membersXem members của group
manage-membersThêm/xóa members
manage-membershipQuản lý group membership

Clients permissions:

PermissionMô tả
viewXem clients
manageTạo, sửa, xóa clients
configureThay đổi client settings
map-rolesTạo/gán client roles

Roles permissions:

PermissionMô tả
viewXem roles
manageTạo, sửa, xóa roles
map-roleGán roles cho users/groups

8.3 Tạo Permission

  1. Vào Realm settings → Admin permissions

  2. Chọn resource type (Users, Groups, Clients, Roles)

  3. Click vào permission cần cấu hình (ví dụ: manage cho Users)

  4. Thêm policies để xác định ai có permission này

8.4 Policies

Policies xác định điều kiện để cấp permission. Keycloak V2 hỗ trợ các loại policies:

Role-based Policy:

// Cho phép users có role "hr-admin" quản lý users
{
  "type": "role",
  "name": "HR Admin Policy",
  "description": "Users with hr-admin role",
  "roles": [
    {
      "id": "{role-id-of-hr-admin}",
      "required": true
    }
  ]
}

User-based Policy:

// Cho phép specific users
{
  "type": "user",
  "name": "Specific Admin Policy",
  "users": [
    "{user-id-of-admin-1}",
    "{user-id-of-admin-2}"
  ]
}

Group-based Policy:

// Cho phép members của group
{
  "type": "group",
  "name": "Admin Group Policy",
  "groups": [
    {
      "id": "{group-id-of-admins}",
      "extendChildren": true
    }
  ]
}

Client-based Policy:

// Cho phép specific clients (service accounts)
{
  "type": "client",
  "name": "Backend Service Policy",
  "clients": [
    "{client-id-of-backend-service}"
  ]
}

8.5 Ví dụ thực tế: HR Admin chỉ quản lý Users

Yêu cầu: User có role hr-admin chỉ được phép xem và quản lý users, không được quản lý clients hay realm settings.

  1. Tạo realm role hr-admin:

    bin/kcadm.sh create roles -r my-company \
      -s name=hr-admin \
      -s description="HR Administrator - can manage users only"
  2. Bật Fine-grained admin permissions V2

  3. Tạo Role-based Policy cho hr-admin:

    • Vào Admin permissions → Policies tab
    • Create policy → Role-based
    • Name: "HR Admin Policy"
    • Chọn role: hr-admin
  4. Gán policy vào Users permissions:

    • Users → permission view → Add policy "HR Admin Policy"
    • Users → permission manage → Add policy "HR Admin Policy"
  5. Gán role cho user:

    bin/kcadm.sh add-roles -r my-company \
      --uusername hr-manager \
      --rolename hr-admin

Giờ user hr-manager có thể đăng nhập Admin Console và chỉ thấy menu Users.

8.6 Permission Evaluation

Bạn có thể test permissions bằng cách sử dụng Evaluation tab:

  1. Vào Admin permissions → Evaluate

  2. Chọn user hoặc client cần test

  3. Chọn resource type và permission

  4. Click Evaluate để xem kết quả (PERMIT hoặc DENY)

9. Dedicated Realm Admin Consoles

Keycloak cho phép tạo admin accounts riêng cho mỗi realm — không cần truy cập master realm:

9.1 Tạo Realm Admin

# Tạo user trong realm
bin/kcadm.sh create users -r my-company \
  -s username=realm-admin \
  -s [email protected] \
  -s enabled=true \
  -s emailVerified=true

bin/kcadm.sh set-password -r my-company
--username realm-admin
--new-password "RealmAdmin@123"

Gán realm-management client roles

bin/kcadm.sh add-roles -r my-company
--uusername realm-admin
--cclientid realm-management
--rolename realm-admin

9.2 Realm Management Client Roles

Client realm-management có sẵn các roles để kiểm soát quyền admin:

RoleMô tả
realm-adminFull admin access cho realm
manage-usersQuản lý users
view-usersXem users
manage-clientsQuản lý clients
view-clientsXem clients
manage-realmQuản lý realm settings
view-realmXem realm settings
manage-identity-providersQuản lý identity providers
manage-eventsQuản lý events
manage-authorizationQuản lý authorization
impersonationImpersonate users
query-usersTìm kiếm users
query-groupsTìm kiếm groups
query-clientsTìm kiếm clients
query-realmsTìm kiếm realms

Ví dụ: Tạo limited admin chỉ quản lý users và groups:

bin/kcadm.sh add-roles -r my-company \
  --uusername limited-admin \
  --cclientid realm-management \
  --rolename manage-users \
  --rolename view-users \
  --rolename query-users \
  --rolename query-groups

10. Sử dụng Roles trong ứng dụng

10.1 Kiểm tra Roles từ Access Token

Decoded access token chứa roles:

{
  "sub": "user-uuid",
  "realm_access": {
    "roles": [
      "admin",
      "manager",
      "user",
      "default-roles-my-company",
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "my-web-app": {
      "roles": [
        "editor",
        "content-admin"
      ]
    },
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  }
}

10.2 Ví dụ trong Spring Boot

// SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/admin/**").hasRole("admin")
            .requestMatchers("/api/content/**").hasRole("editor")
            .requestMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated()
        )
        .oauth2ResourceServer(oauth2 -> oauth2
            .jwt(jwt -> jwt
                .jwtAuthenticationConverter(jwtAuthenticationConverter())
            )
        );
    return http.build();
}

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
    converter.setAuthoritiesClaimName("realm_access.roles");
    converter.setAuthorityPrefix("ROLE_");
    
    JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
    jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
    return jwtConverter;
}

}

10.3 Ví dụ trong Node.js (Express)

// middleware/auth.js
const jwt = require('jsonwebtoken');

function hasRealmRole(role) { return (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token provided' });

try {
  const decoded = jwt.decode(token);
  const roles = decoded.realm_access?.roles || [];
  
  if (roles.includes(role)) {
    req.user = decoded;
    next();
  } else {
    res.status(403).json({ error: 'Insufficient permissions' });
  }
} catch (err) {
  res.status(401).json({ error: 'Invalid token' });
}

}; }

function hasClientRole(clientId, role) { return (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token provided' });

try {
  const decoded = jwt.decode(token);
  const roles = decoded.resource_access?.[clientId]?.roles || [];
  
  if (roles.includes(role)) {
    req.user = decoded;
    next();
  } else {
    res.status(403).json({ error: 'Insufficient permissions' });
  }
} catch (err) {
  res.status(401).json({ error: 'Invalid token' });
}

}; }

// Sử dụng app.get('/api/admin/users', hasRealmRole('admin'), (req, res) => { // Only admin can access });

app.post('/api/content', hasClientRole('my-web-app', 'editor'), (req, res) => { // Only editors can create content });

11. Bài tập thực hành

  1. Tạo Realm Roles: super-admin, manager, staff, viewer

  2. Tạo Client Roles cho client my-web-app: content-editor, content-reviewer, content-publisher

  3. Tạo Composite Roles:

    • super-admin chứa: manager + tất cả client roles
    • manager chứa: staff + content-reviewer
    • staff chứa: viewer + content-editor
  4. Gán roles cho groups:

    • Group Engineering: realm role staff
    • Group Engineering/Backend: client role content-editor
  5. Bật Fine-grained admin permissions V2 và tạo:

    • Role-based policy cho hr-admin
    • Gán policy vào Users view/manage permissions
    • Test với user có role hr-admin
  6. Tạo Service Account cho client my-backend-service với roles manage-users, view-users và test bằng client credentials grant

12. Tổng kết

Trong bài này, bạn đã học:

  • Phân biệt Realm Roles và Client Roles

  • Tạo Composite Roles với hierarchy phân quyền

  • Role Mappings cho users và groups (trực tiếp và kế thừa)

  • Cấu hình Default Roles cho users mới

  • Sử dụng Service Account Roles cho machine-to-machine communication

  • Fine-grained Admin Permissions V2 với policies (role-based, user-based, group-based, client-based)

  • Tạo dedicated realm admin với limited permissions

  • Kiểm tra roles trong ứng dụng (Spring Boot, Node.js)

Bài tiếp theo sẽ hướng dẫn về Clients, Client Scopes và OpenID Connect trong Keycloak.