Phần lớn bài viết về độ tin cậy của webhook coi một sự kiện bị mất là vấn đề dữ liệu. Bạn thiếu một dòng, sớm muộn sẽ nhận ra, rồi lấy bù (backfill). Phiền, khôi phục được, và gần như vô hình với bất kỳ ai ngoài đội kỹ thuật.
Sự kiện định danh phá vỡ cách nhìn đó, vì hai chiều không đối xứng.
Một sự kiện access.granted không bao giờ tới sẽ tạo ra một người dùng không vào được, và người dùng không vào được sẽ báo cho bạn trong vòng vài phút. Nó tự báo cáo. Hệ thống có sẵn một lớp cảnh báo bằng con người, miễn phí.
Một sự kiện access.revoked không bao giờ tới sẽ tạo ra một người dùng vẫn vào được. Không ai báo chuyện đó. Người được lợi chẳng có lý do gì để nhắc tới, còn mọi người khác tin rằng offboarding đã xong vì nhà cung cấp định danh (identity provider) của bạn nói vậy. Khe hở đó tồn tại cho tới khi một đợt audit phát hiện ra, có thể là một năm sau.
TL;DR
- Lỡ một lần cấp quyền là một ticket hỗ trợ. Lỡ một lần thu hồi quyền là một cánh cửa mở không gắn chuông báo động
- Dashboard của nhà cung cấp định danh hiển thị thu hồi thành công, vì ở phía họ đúng là đã thành công
- Clerk, và mọi dịch vụ khác xây trên Svix, gửi 8 lần trong khoảng 27,5 giờ, rồi vô hiệu hóa endpoint sau 5 ngày thất bại hoàn toàn
- Bản thân trạng thái endpoint bị vô hiệu hóa cũng im lặng, và đó là trạng thái quan trọng nhất
- Hãy coi độ sẵn sàng của bên nhận webhook là một cơ chế kiểm soát truy cập, không phải chuyện vệ sinh hạ tầng
Lỗi mà dashboard không cho bạn thấy
Một admin xóa ai đó khỏi tổ chức. Nhà cung cấp định danh cập nhật bản ghi của chính nó, vô hiệu hóa session của chính nó, rồi bắn organizationMembership.deleted hoặc user.deleted hoặc session.revoked tới endpoint của bạn.
Endpoint của bạn đang deploy dở và trả về 502.
Trên console của nhà cung cấp định danh, tư cách thành viên đã bị xóa. Ở góc nhìn của admin, offboarding đã xong. Ứng dụng của bạn vẫn giữ một dòng ghi rằng người này là thành viên của tổ chức đó, và mọi session đã cache hay API token bạn cấp dựa trên lần cấp quyền ban đầu vẫn còn hiệu lực.
Không có chỗ hiển thị lỗi nào mà con người nhìn vào. Log webhook của nhà cung cấp có một lượt gửi thất bại, nhưng không ai đọc log webhook trừ khi đã nghi ngờ điều gì đó.
Thử lại giúp bạn câu thêm thời gian, rồi dừng
Clerk gửi webhook qua Svix, và khá nhiều sản phẩm định danh và hạ tầng khác cũng vậy. Lịch thử lại (retry) được công bố công khai và đáng biết chính xác, vì nó xác định endpoint của bạn có thể hỏng bao lâu trước khi Svix ngừng thử lại một sự kiện thu hồi quyền.
Tám lần gửi: “Ngay lập tức, 5 giây, 5 phút, 30 phút, 2 giờ, 5 giờ, 10 giờ, 10 giờ (cộng dồn với lần trước)”.
Tổng cộng khoảng 27,5 giờ, thực sự hào phóng, và thuộc hàng dài nhất trong mọi bên gửi ở trang tra cứu nhà cung cấp webhook của chúng tôi. Một khung giờ deploy, một sự cố, một ngày thứ Sáu tồi tệ: đều nằm gọn trong đó. Sau lần gửi thứ tám, Svix gửi một operational webhook loại message.attempt.exhausted, thứ chỉ hữu ích nếu bạn có chỗ để nhận operational webhook.
Rồi đến ngưỡng thứ hai: “Nếu mọi lần gửi tới một endpoint cụ thể đều thất bại trong khoảng 5 ngày, endpoint đó sẽ bị vô hiệu hóa và một operational webhook (EndpointDisabledEvent) sẽ được gửi tới tài khoản của bạn”. Đồng hồ này có điều kiện: “Đồng hồ chỉ bắt đầu chạy sau khi nhiều lượt gửi thất bại trong vòng 24 giờ, với khoảng cách ít nhất 12 giờ giữa lần thất bại đầu tiên và lần cuối cùng”.
Hãy đọc kỹ trạng thái bị vô hiệu hóa. Một khi endpoint đã bị vô hiệu hóa, sự kiện mới không được xếp hàng chờ cho nó. Chúng không được gửi bù khi bạn sửa xong endpoint. Mọi sự kiện thu hồi quyền trong khoảng thời gian đó vẫn chưa được gửi cho tới khi có người nhận ra và phát lại, và thông báo duy nhất là một operational webhook gửi tới một tài khoản mà chưa ai cấu hình đích nhận.
Bất đối xứng là một đầu vào của thiết kế
Khi đã chấp nhận rằng hai chiều mang chi phí khác nhau, một số quyết định bình thường sẽ thay đổi.
Fail closed với những sự kiện gỡ quyền truy cập. Nếu handler (đoạn code xử lý webhook trên server của bạn) không xử lý được session.revoked, hành vi đúng không phải là trả về 200 rồi ghi một dòng cảnh báo. Trả về 5xx để lịch thử lại được kích hoạt, và bảo đảm hệ thống cảnh báo của chính bạn lên tiếng. Với cấp quyền, bạn có thể lạc quan. Với thu hồi quyền thì không.
Đừng chỉ dựa vào webhook. Sự kiện dạng push là một tối ưu về độ trễ, không phải nguồn sự thật. Một lượt đối soát định kỳ với API của nhà cung cấp định danh, hỏi “ai hiện đang được quyền truy cập”, biến một lần thu hồi bị lỡ vĩnh viễn thành một lần tự lành ở lượt quét kế tiếp. Đó cũng là lập luận về mô hình lai trong bài Webhook hay polling, và định danh là trường hợp nó không còn là tùy chọn. Chạy mỗi giờ một lần thì trường hợp xấu nhất là một giờ quyền truy cập lỗi thời, thay vì mãi mãi.
Cho session của chính bạn hết hạn. Nếu ứng dụng của bạn tự cấp session hay token dựa trên một sự kiện định danh, một TTL ngắn kèm bước refresh kiểm tra lại tư cách thành viên nghĩa là một lần thu hồi bị lỡ sẽ tự hết hiệu lực theo thời gian. Token sống lâu biến một webhook bị mất thành quyền truy cập vô thời hạn.
Thứ tự ở đây cũng quan trọng. Một member.added được thử lại sau một member.removed sẽ thêm lại người bạn vừa xóa, dù không sự kiện nào bị xử lý hai lần. Khuôn mẫu version guard, chỉ áp dụng một cập nhật khi dấu thời gian (timestamp) của nó mới hơn của dòng hiện có, nằm trong bài Bạn nhận cùng một webhook hai lần. Với kiểm soát truy cập, nên từ chối thẳng một chuyển trạng thái không hợp lệ thay vì dựa vào việc so sánh dấu thời gian.
Biết trạng thái endpoint bị vô hiệu hóa trông như thế nào. Đây là cảnh báo giá trị nhất trong cả nhóm này. Nếu webhook định danh im lặng lâu hơn khoảng cách bình thường, có gì đó không ổn, và “im lặng” chính xác là triệu chứng của trạng thái bị vô hiệu hóa. Một bước kiểm tra “không có sự kiện định danh nào trong 6 giờ, trong giờ làm việc” sẽ bắt được nó. Không gì khác bắt được.
Làm gì với câu hỏi audit
Nếu bạn phải lo SOC 2, ISO 27001 hay bất kỳ bảng câu hỏi bảo mật nào của khách hàng, trong đó sẽ có một câu về việc thu hồi quyền (deprovisioning) kịp thời. Nếu đường thu hồi quyền của bạn chỉ là một webhook duy nhất không có đối soát, câu trả lời trung thực là: thu hồi quyền diễn ra kịp thời, trừ những lúc nó lặng lẽ không diễn ra, và bạn không thể phân biệt hai trường hợp đó sau khi sự việc đã xảy ra.
Đó là điều khó viết ra giấy. Nhưng nó cũng sửa được bằng hai thứ: một nhật ký bền vững ghi mọi sự kiện định danh đã tới, và một job đối soát có kết quả để bạn chỉ vào. Nhật ký trả lời “chúng ta đã được báo gì, và khi nào”; lượt quét trả lời “và chúng ta đã hành động chưa”. Chúng tôi đã viết về lý do một dòng database có thể sửa đổi là câu trả lời yếu cho câu hỏi thứ nhất trong bài Nhật ký webhook mà mọi chỉnh sửa đều để lại dấu vết.
AnyHook giúp được gì
Vấn đề mang tính cấu trúc là độ sẵn sàng của bên nhận đã trở thành một phần của cơ chế kiểm soát truy cập, và bên nhận xây trên hạ tầng ứng dụng thì thừa hưởng uptime của ứng dụng.
Trỏ nhà cung cấp định danh về in.anyhook.net/you/identity sẽ tách hai thứ đó ra. AnyHook xác nhận và lưu sự kiện tại edge (lớp máy chủ biên của Cloudflare) sau khoảng một giây, nên một lần deploy, một lần cold start hay một sự cố database ở phía bạn hoàn toàn không tạo ra lượt gửi thất bại nào. Từ đó:
- Các lần thử lại tới endpoint của bạn chạy ở mốc 5 phút, 30 phút, 3 giờ, 18 giờ, sau đó hằng ngày. Trên gói Scale (khoảng 6 ngày), thời gian đó dài hơn 27,5 giờ của Svix; trên gói Pro, lần gửi cuối ở khoảng giờ thứ 22 và trên gói Free ở phút thứ 35, sau đó thứ bạn phát lại chính là sự kiện đã lưu
- Mọi sự kiện thu hồi quyền đều được lưu trước khi thử gửi, mã hóa khi lưu trữ, nên câu hỏi “chúng ta đã từng nhận nó chưa” có câu trả lời độc lập với log ứng dụng của bạn
- Với phát lại (replay) theo khoảng thời gian, một khung giờ deploy hỏng chỉ cần chuyển tiếp lại các sự kiện, thay vì rà soát thủ công xem ai lẽ ra đã mất quyền truy cập
- Cảnh báo lỗi ở mốc 1, 5 và 20 lần thất bại liên tiếp tới từ rất lâu trước ngưỡng vô hiệu hóa của bất kỳ nhà cung cấp nào
Không điều nào ở trên thay thế lượt quét đối soát, và bạn vẫn nên chạy nó. Một dịch vụ trung chuyển webhook (webhook relay) lưu mọi sự kiện mà nó đã chấp nhận; chỉ lượt quét mới bắt được những gì chưa bao giờ được gửi.
Điều cần nhớ
Cấp quyền tự sửa sai được vì người bị ảnh hưởng sẽ phàn nàn. Thu hồi quyền thì không, vì người bị ảnh hưởng sẽ không phàn nàn. Chỉ riêng sự bất đối xứng đó đã là lý do webhook định danh xứng đáng với tiêu chuẩn độ tin cậy khắt khe hơn phần còn lại của các tích hợp, dù payload trông giống nhau.
Fail closed với các lần gỡ quyền, cho session của chính bạn hết hạn, chạy lượt quét đối soát và đặt cảnh báo cho những lúc im lặng bất thường. Nếu không, bạn có một cơ chế kiểm soát bảo mật mà kiểu hỏng của nó là im lặng.