# SAML, SCIM và bảng câu hỏi bảo mật: những ràng buộc FDE gặp từ hợp đồng doanh nghiệp đầu tiên

> Bản demo có thể chạy hoàn hảo, nhưng nếu bạn không trả lời được câu "nhân viên nghỉ việc thì bao lâu sau mất quyền truy cập?", đội bảo mật của khách sẽ không cho bạn lên production.

Bản gốc: https://fdetimes.net/vi/bach-khoa/sso-saml-iam-trong-du-an-fde/

Ngày thứ ba ở site khách hàng, bản demo chạy tốt và người dùng nghiệp vụ đã muốn dùng thật. Rồi bạn nhận được một email từ đội bảo mật của khách, đính kèm bảng câu hỏi dài vài chục dòng. Dòng đầu tiên hỏi ứng dụng có hỗ trợ SAML SSO không.

Theo một playbook về onboarding SSO doanh nghiệp đăng trên CIAM Compass, hợp đồng B2B SaaS doanh nghiệp đầu tiên thường đi kèm một bảng câu hỏi bảo mật yêu cầu SAML SSO.

Một hướng dẫn nghề nghiệp FDE năm 2026 của Georgia Southern còn xếp SSO doanh nghiệp, các ràng buộc tuân thủ như SOC 2, HIPAA, FedRAMP và data residency vào phần lớn công việc thực tế của vai trò này.

Phần lớn developer chỉ coi đăng nhập là một form có username và password. FDE thì phải coi danh tính là một hệ thống có vòng đời: người dùng vào bằng cách nào, được quyền gì, và khi nào bị mất quyền. Bạn trả lời rõ được ba câu đó thì mới lấy được lòng tin của đội bảo mật.

## Đăng nhập và quản lý tài khoản là hai bài toán khác nhau

SAML là giao thức dựa trên XML, dùng để trao đổi dữ liệu xác thực với identity provider. Khi nhân viên của khách bấm "Đăng nhập bằng tài khoản công ty", IdP như Okta hay Entra ID xác thực người đó rồi gửi về ứng dụng của bạn một assertion có chữ ký, cho biết người này là ai và thuộc những nhóm nào.

SCIM giải một bài toán khác. Đây là giao thức REST + JSON dùng để đồng bộ dữ liệu danh tính, tức là IdP chủ động gọi API của bạn để tạo, sửa hoặc vô hiệu hóa tài khoản. Authgear tóm tắt khác biệt này rất gọn: SAML cho người dùng đăng nhập, còn SCIM tạo và quản lý tài khoản của họ.

Ở giữa hai thứ này là JIT (just-in-time) provisioning. Lần đầu một người đăng nhập qua SAML, ứng dụng tự tạo tài khoản dựa trên thông tin trong assertion. Cách này nhanh và rẻ, nhưng có một điểm yếu mà đội bảo mật nào cũng sẽ hỏi tới.

**Điểm mấu chốt:** JIT chỉ chạy khi có người đăng nhập, nên nó không bao giờ thu hồi được quyền của người đã nghỉ việc.

Bài viết của Clerk về SCIM 2.0 nói thẳng rằng JIT không thể deprovision. Một nhân viên bị khóa tài khoản trong IdP sẽ không đăng nhập lại nữa, nên ứng dụng không bao giờ nhận được tín hiệu nào. Nếu họ còn session đang mở hoặc API token cũ, tài khoản trong hệ thống của bạn vẫn còn hiệu lực.

SCIM khắc phục điều đó vì lệnh deactivate chạy độc lập, không cần chờ ai đăng nhập.

## Một ví dụ: bệnh viện dùng Entra ID

Thử hình dung bạn triển khai một công cụ phân tích dữ liệu cho một bệnh viện chịu ràng buộc HIPAA, và bệnh viện dùng Microsoft Entra ID. Trước khi viết dòng cấu hình nào, câu hỏi đầu tiên cần chốt là khách bắt buộc SAML hay chấp nhận OIDC.

Playbook của CIAM Compass khuyên hỏi thẳng xem đội mua sắm của khách có yêu cầu cụ thể là SAML không.

Một cuộc trao đổi discovery có thể diễn ra như sau.

> **FDE:** Bên anh yêu cầu bắt buộc SAML, hay chỉ cần SSO qua Entra ID là được?
> **Trưởng nhóm IAM:** Chính sách ghi SAML, còn OIDC thì phải hỏi lại đội mua sắm.
> **FDE:** Assertion bên anh có gửi kèm group claim không? Hiện bên anh đang có những nhóm nào liên quan đến công cụ này?
> **Trưởng nhóm IAM:** Có.

Một nhóm cho bác sĩ phân tích, một nhóm cho ban quản trị.
> **FDE:** Khi một nhân viên nghỉ việc, chính sách nội bộ yêu cầu họ mất quyền truy cập trong bao lâu?

Câu hỏi cuối cùng là quan trọng nhất. Nó quyết định bạn có thể dừng ở JIT hay phải làm SCIM ngay từ đầu.

Khi có group claim, bước tiếp theo là ánh xạ chúng sang mô hình vai trò của ứng dụng, đúng như playbook khuyến nghị. Nguyên tắc ở đây là từ chối mặc định: người không thuộc nhóm nào đã khai báo thì không được vào.

```python
GROUP_TO_ROLE = {
"hosp-tool-admins": "admin",
"hosp-clinical-analysts": "analyst",
}
ROLE_PRIORITY = ("admin", "analyst")

def resolve_role(group_claims: list[str]) -> str | None:
roles = {GROUP_TO_ROLE[g] for g in group_claims if g in GROUP_TO_ROLE}
for role in ROLE_PRIORITY:
if role in roles:
return role
return None  # không khớp nhóm nào: từ chối, không gán "viewer" mặc định

def on_saml_login(assertion):
role = resolve_role(assertion.groups)
if role is None:
raise PermissionError("User không thuộc nhóm được cấp quyền")
user = upsert_user(email=assertion.email, role=role)  # JIT
return start_session(user)
```

Hàm `on_saml_login` cập nhật lại vai trò mỗi lần người dùng đăng nhập, nên nếu ai đó bị chuyển khỏi nhóm admin thì lần đăng nhập sau họ sẽ mất quyền admin. Nhưng nếu họ không bao giờ đăng nhập lại, mọi thứ vẫn giữ nguyên.

Đó là chỗ SCIM phải vào cuộc, với một endpoint nhận lệnh vô hiệu hóa, đánh dấu tài khoản là inactive, hủy toàn bộ session và thu hồi token ngay lập tức.

## Chuẩn trên giấy khác với IdP ngoài đời

Đến đây nhiều người nghĩ cứ đọc RFC rồi làm đúng chuẩn là xong. Clerk cảnh báo rằng RFC 7644 định nghĩa một giao thức rất rộng, nhưng các IdP doanh nghiệp chỉ triển khai một tập con thực dụng của nó. Code chạy được với IdP này có thể lỗi với IdP khác, nên phải test riêng với từng IdP mà khách thật sự dùng.

Độ trễ cũng khác nhau. Provisioning gửi đi từ Okta gần như real-time, còn Microsoft Entra ID đồng bộ gia tăng, chỉ gửi phần thay đổi, theo các chu kỳ có lịch cố định. Với bệnh viện trong ví dụ, như vậy là từ lúc IT khóa một tài khoản trong Entra ID đến lúc ứng dụng của bạn nhận lệnh deactivate sẽ có một khoảng trễ.

Bạn phải nói rõ khoảng trễ đó với đội bảo mật, đừng hứa "thu hồi tức thì".

## Các bước tự làm trong tuần đầu

Hãy bắt đầu bằng việc đọc kỹ bảng câu hỏi bảo mật và xác định yêu cầu thật sự: SAML hay OIDC, IdP nào, có group claim không, và thời hạn thu hồi quyền là bao lâu. Sau đó bạn thiết kế bảng ánh xạ nhóm sang vai trò và gửi cho khách duyệt trước khi code, vì tên nhóm là dữ liệu của họ chứ không phải của bạn.

Tiếp theo, làm JIT trước để người dùng vào được hệ thống, và ghi rõ trong tài liệu rằng JIT chưa xử lý được việc thu hồi quyền. Nếu khách có nhiều người dùng hoặc chính sách thu hồi quyền chặt, đưa SCIM vào kế hoạch ngay.

Playbook của CIAM Compass cũng cho rằng JIT chỉ đủ cho giai đoạn đầu, còn B2B SaaS ở quy mô lớn cần thêm SCIM.

Bước cuối cùng thường khó nhất và chẳng liên quan gì đến code: xin credential production. Hướng dẫn của Georgia Southern gọi thẳng đây là chuyện chính trị nội bộ khi làm việc với đội bảo mật của khách. Bạn càng chuẩn bị sẵn câu trả lời về deprovisioning, phân quyền và audit log, cuộc đàm phán này càng ngắn.

## Những lỗi hay gặp

Lỗi phổ biến nhất là gán vai trò mặc định cho người không khớp nhóm nào. Trên môi trường dev thì tiện, nhưng ở môi trường HIPAA, đó là một lỗ hổng phân quyền. Lỗi thứ hai là tưởng rằng khóa tài khoản trong IdP thì ứng dụng tự biết, trong khi với JIT, ứng dụng không bao giờ nhận được tín hiệu đó.

Lỗi thứ ba là chỉ test với một IdP rồi coi như đã hỗ trợ "SCIM chuẩn". Lỗi thứ tư là hứa về thời gian thu hồi quyền mà không biết chu kỳ đồng bộ của IdP phía khách. Lỗi cuối cùng là đợi tới khi đội bảo mật hỏi mới bắt đầu nghĩ về những chuyện này.

## Bài tập cho tuần này

Lấy một ứng dụng bạn đang làm, rồi vẽ ra dòng thời gian từ lúc một nhân viên bị khóa trong IdP đến lúc họ thật sự mất mọi quyền truy cập: session, API token, job đang chạy. Đánh dấu bước nào phụ thuộc vào việc họ đăng nhập lại, rồi làm lại phép tính đó cho cả Okta lẫn Entra ID.

Nếu bạn trả lời được câu hỏi đó bằng một con số và một sơ đồ, bạn đã nói được cùng ngôn ngữ với đội bảo mật. Với một FDE, đó thường là cách nhanh nhất để được cấp quyền lên production.

**Thử ngay tuần này:**

- Tạo một tài khoản Okta developer miễn phí, cấu hình SAML cho một ứng dụng nhỏ của bạn rồi in ra toàn bộ assertion để xem group claim thật trông ra sao.
- Soạn sẵn một danh sách câu hỏi discovery về SSO gồm: SAML hay OIDC, IdP nào, có gửi group claim không, cần SCIM không, và thời hạn thu hồi quyền tối đa là bao lâu.
- Thêm vào CV một dòng mô tả cụ thể việc bạn đã làm với SSO hoặc provisioning, ví dụ "tích hợp SAML SSO và SCIM deprovisioning với Okta", thay vì chỉ ghi "có kinh nghiệm bảo mật".

## Nguồn

- [B2B Enterprise SSO Onboarding: A 60-Day Playbook, CIAM Compass](https://guptadeepak.com/ciam-compass/playbooks/b2b-enterprise-sso-onboarding/)

- [SCIM vs SAML: What's the Difference and When to Use Each](https://www.authgear.com/post/scim-vs-saml)

- [SCIM 2.0 explained: a practical guide for SaaS auth - Part 2](https://clerk.com/articles/scim-2-0-explained-a-practical-guide-for-saas-auth-2.md)

- [What Is a Forward Deployed Engineer? Complete 2026 Guide](https://ocpd.georgiasouthern.edu/blog/2026/08/05/what-is-a-forward-deployed-engineer-complete-2026-guide/)
