Đặc vụ của bạn cần đọc lịch của khách hàng, gửi tin nhắn từ tài khoản của họ, hoặc tạo một yêu cầu hỗ trợ dưới tên của họ. Phiên bản nhanh gọn là sử dụng một tài khoản dịch vụ có quyền truy cập rộng và hành động thông qua nó. Mọi hành động đều hiển thị dưới dạng “tích hợp”, không ai có thể biết người dùng nào đã kích hoạt cái gì, và một thông tin đăng nhập bị lộ sẽ làm lộ mọi tài khoản mà bạn tương tác.
Phiên bản đúng là ủy quyền được ủy nhiệm (delegated authorization): người dùng cấp cho đặc vụ của bạn một mã thông báo có phạm vi, có thể thu hồi, đặc vụ hành động như người dùng đó, và dấu vết kiểm toán sẽ ghi rõ tên của họ. Đây là lý do OAuth 2.0 được xây dựng. Điều khiến nó trở nên khó khăn cho các đặc vụ là OAuth giả định có trình duyệt và một người có mặt để nhấp vào “Cho phép”, trong khi các đặc vụ chạy nền vào lúc 3 giờ sáng.
Hướng dẫn này bao gồm luồng OAuth nào phù hợp với đặc vụ, cách xác định phạm vi và lưu trữ mã thông báo, cách xử lý làm mới và thu hồi, cũng như cách kiểm thử toàn bộ quy trình mà không cần tài khoản thật. Nếu bạn vẫn đang lựa chọn giữa xác thực bằng khóa và ủy quyền được ủy nhiệm, bài so sánh về khóa API và OAuth của chúng tôi là nơi để bắt đầu.
Apidog giúp giải quyết phần mà các nhóm thường đánh giá thấp: thực hành mọi nhánh của luồng, bao gồm hết hạn và thu hồi, trước khi đặc vụ gặp chúng trong môi trường sản xuất.
Tài khoản dịch vụ hay truy cập được ủy nhiệm
Hãy chọn một cách cẩn thận, vì hai mô hình này thất bại theo những cách khác nhau.
Một tài khoản dịch vụ là danh tính riêng của đặc vụ của bạn, với các quyền hạn riêng của nó. Nó phù hợp với công việc mà đặc vụ thực hiện thay mặt bạn: đọc cơ sở dữ liệu của riêng bạn, gọi các dịch vụ nội bộ của riêng bạn, chạy các công việc theo lịch trình trên hạ tầng của bạn. Hãy giới hạn phạm vi chặt chẽ, như trong bài viết của chúng tôi về khóa API đặc quyền tối thiểu cho đặc vụ AI, và xoay vòng nó.
Truy cập được ủy nhiệm là đặc vụ hành động như một người dùng cụ thể, với các quyền hạn của người dùng đó và không hơn. Nó được yêu cầu bất cứ khi nào dữ liệu thuộc về người khác. Ba thuộc tính khiến nó đáng để bỏ thêm công sức: người dùng có thể thấy những gì đã được cấp, người dùng có thể thu hồi nó, và mọi hành động đều mang danh tính của họ trong nhật ký.
Chế độ thất bại cần tránh là tài khoản dịch vụ có quyền truy cập toàn tổ chức được sử dụng để hành động “như” người dùng. Nó hoạt động, và điều đó có nghĩa là một thông tin đăng nhập bị rò rỉ duy nhất sẽ làm lộ tất cả mọi người, không có khả năng thu hồi theo từng người dùng và không có dấu vết kiểm toán trung thực.
Luồng nào phù hợp với một đặc vụ
OAuth 2.0 định nghĩa một số loại cấp quyền, và chỉ một vài trong số đó có ý nghĩa ở đây. Thông số kỹ thuật OAuth 2.0 có đầy đủ; đây là những loại bạn sẽ sử dụng.
Mã ủy quyền với PKCE. Luồng tiêu chuẩn để hành động như một người dùng. Người dùng được chuyển hướng đến nhà cung cấp, phê duyệt các phạm vi, và dịch vụ của bạn trao đổi mã để lấy mã thông báo. PKCE bảo vệ việc trao đổi và hiện là khuyến nghị mặc định cho mọi loại ứng dụng khách, theo Thực hành tốt nhất về bảo mật OAuth 2.0 hiện tại. Hướng dẫn chi tiết của chúng tôi về cấp quyền mã ủy quyền bao gồm các cơ chế từng bước.
Điểm đặc biệt của đặc vụ: luồng này chỉ chạy một lần, với sự có mặt của con người, tại thời điểm kết nối. Đặc vụ không bao giờ chạy nó. Nó sử dụng mã thông báo làm mới mà luồng đã tạo ra. Tách biệt hai khoảnh khắc đó trong thiết kế của bạn và hầu hết sự khó khăn sẽ biến mất.
Thông tin đăng nhập của ứng dụng khách. Máy-tới-máy, không có người dùng liên quan. Đúng cho tài khoản dịch vụ và sai khi hành động như một người dùng, bởi vì không có người dùng nào để đồng ý.
Cấp quyền ủy quyền thiết bị. Dành cho các đặc vụ trên máy không có trình duyệt. Người dùng nhận được một mã và phê duyệt trên điện thoại của họ. Hữu ích cho các đặc vụ CLI và môi trường không có giao diện người dùng đồ họa (headless).
Trao đổi mã thông báo. RFC 8693 cho phép một dịch vụ đổi một mã thông báo lấy một mã thông báo hẹp hơn. Đây là cách bạn cấp cho một đặc vụ phụ một mã thông báo giới hạn trong một phạm vi cho một nhiệm vụ, được tạo ra từ cấp quyền rộng hơn của người dùng, mà không cần đưa cho nó mã thông báo gốc. Nếu bạn chạy hệ thống đa đặc vụ, đây là cơ chế giúp thông tin đăng nhập cho từng đặc vụ trở nên khả thi, và nó phù hợp với các quy tắc ranh giới trong bài viết của chúng tôi về chuyển giao đa đặc vụ.
Giới hạn phạm vi chặt chẽ, và theo từng đặc vụ
Phạm vi là nơi truy cập được ủy nhiệm phát huy giá trị, và là nơi hầu hết các triển khai trở nên lười biếng bằng cách yêu cầu mọi thứ mà ứng dụng có thể cần.
Chỉ yêu cầu những gì đặc vụ này thực hiện. Một đặc vụ lập lịch chỉ cần quyền ghi lịch và không cần gì khác. Không phải thư, không phải danh bạ, không phải tệp. Người dùng đọc màn hình đồng ý, và một danh sách dài vừa là vấn đề về độ tin cậy vừa là vấn đề về phạm vi ảnh hưởng. Bài giải thích của chúng tôi về phạm vi OAuth 2 bao gồm cách các nhà cung cấp mô hình hóa chúng.
Yêu cầu tăng dần. Yêu cầu tối thiểu tại thời điểm kết nối, sau đó yêu cầu thêm khi người dùng yêu cầu một tính năng cần đến nó. Sự đồng ý gắn liền với một yêu cầu cụ thể dễ được cấp hơn và dễ biện minh hơn.
Cấp cho mỗi đặc vụ một mã thông báo riêng. Nếu một đặc vụ nghiên cứu và một đặc vụ thanh toán cùng hành động cho cùng một người dùng, hãy tạo hai mã thông báo với các phạm vi khác nhau thay vì chia sẻ một mã. Khi đó, một đặc vụ nghiên cứu bị xâm phạm không thể thực hiện hoàn tiền, và nhật ký cho bạn biết đặc vụ nào đã hành động.
Ưu tiên các phạm vi đọc theo mặc định và yêu cầu leo thang rõ ràng cho các quyền ghi. Kết hợp điều này với một cổng phê duyệt cho các cuộc gọi phá hoại, như trong bài viết của chúng tôi về hàng rào bảo vệ đặc vụ AI, để một mã thông báo có thể ghi không phải là thứ duy nhất ngăn cách đặc vụ với một lỗi lầm.
Lưu trữ, làm mới và thu hồi
Mã thông báo là thông tin đăng nhập, vì vậy hãy xử lý chúng như thông tin đăng nhập.
Lưu trữ. Mã hóa mã thông báo làm mới khi không sử dụng, được khóa theo từng người dùng. Không bao giờ ghi chúng vào nhật ký, không bao giờ đặt chúng vào lời nhắc (prompts), và không bao giờ để mô hình nhìn thấy chúng. Một mã thông báo trong ngữ cảnh là một mã thông báo trong kho lưu trữ dấu vết của bạn, nhật ký của nhà cung cấp, và có thể là một bản tóm tắt. Bài viết của chúng tôi về theo dõi các cuộc gọi công cụ của đặc vụ bao gồm việc biên tập tại ranh giới thay vì tại thời điểm đọc.
Làm mới. Mã thông báo truy cập được thiết kế để có thời gian tồn tại ngắn. Đặc vụ không bao giờ nên tự quản lý điều này; một trình quản lý mã thông báo nằm phía trước ứng dụng khách HTTP sẽ làm mới khi thời gian hết hạn gần đến và thử lại cuộc gọi một lần khi gặp 401.
class TokenManager:
def __init__(self, store, provider):
self.store, self.provider = store, provider
def access_token(self, user_id, agent_scope):
rec = self.store.get(user_id, agent_scope)
if rec.expires_in() > 60:
return rec.access_token
fresh = self.provider.refresh(rec.refresh_token, scope=agent_scope)
self.store.save(user_id, agent_scope, fresh) # rotation: store the new refresh token
return fresh.access_token
Hai chi tiết quan trọng. Các nhà cung cấp ngày càng xoay vòng mã thông báo làm mới, cấp một mã mới sau mỗi lần làm mới và vô hiệu hóa mã cũ, vì vậy hãy lưu trữ mã mới ngay lập tức nếu không người dùng sẽ bị khóa. Và tuần tự hóa việc làm mới cho mỗi người dùng, vì hai lần làm mới đồng thời với một nhà cung cấp xoay vòng sẽ cạnh tranh và một lần sẽ thất bại.
Thu hồi. Người dùng thu hồi quyền truy cập, mã thông báo hết hạn, quản trị viên xóa tài khoản. Đặc vụ phải xử lý 401 và 403 như là lỗi cuối cùng chứ không phải lỗi có thể thử lại. Việc thử lại lỗi xác thực không bao giờ hữu ích và có thể kích hoạt các biện pháp bảo vệ chống lạm dụng. Trả về một thông báo rõ ràng nêu tên người dùng và phạm vi để con người có thể hành động, theo các mẫu lỗi trong bài viết của chúng tôi về thiết kế lỗi API cho đặc vụ.
Vấn đề đồng ý
Phần khó xử của đặc vụ kết hợp với OAuth: sự đồng ý cần có con người, và các đặc vụ chạy tự động.
Tách biệt thời điểm kết nối khỏi thời điểm chạy và nó trở nên dễ quản lý. Tại thời điểm kết nối, một người ủy quyền một lần, bằng trình duyệt, và bạn lưu trữ một mã thông báo làm mới. Tại thời điểm chạy, đặc vụ sử dụng cấp quyền đó mà không có sự can thiệp của con người. Điều này hoạt động cho các đặc vụ theo lịch trình và chạy nền, vốn là phần lớn các đặc vụ.
Hai giới hạn cần lên kế hoạch. Các cấp quyền hết hạn, đôi khi sau nhiều tháng không sử dụng, đôi khi theo chính sách. Phát hiện một cấp quyền đã hết hạn, dừng chạy, và thông báo cho người dùng, thay vì thất bại âm thầm mỗi đêm. Và sự đồng ý có một giới hạn phạm vi: một đặc vụ cần một phạm vi mà người dùng chưa bao giờ cấp phải yêu cầu thay vì tự ý leo thang.
Đối với bất kỳ hành động nào có rủi ro cao, hãy thêm một cổng thứ hai tại thời điểm hành động. Mã thông báo chứng minh đặc vụ có thể hành động; một cổng phê duyệt quyết định liệu nó có nên làm vậy hay không. Đó là những câu hỏi khác nhau và cả hai đều cần có câu trả lời.
Kiểm thử luồng trước khi một đặc vụ gặp nó
Các đường dẫn mã xác thực là phần ít được kiểm thử nhất trong hầu hết các tích hợp, bởi vì việc thực hiện chúng thủ công có nghĩa là nhấp qua các màn hình của nhà cung cấp.
Xây dựng năm trường hợp này:
- Luồng lý tưởng. Một mã thông báo truy cập hợp lệ, một cuộc gọi thành công. Điểm khởi đầu.
- Mã thông báo truy cập đã hết hạn. Nhà cung cấp trả về
401, trình quản lý làm mới, cuộc gọi thử lại một lần và thành công. Đây là đường dẫn thực tế phổ biến nhất và thường ít được kiểm thử nhất. - Mã thông báo làm mới bị thu hồi. Việc làm mới trả về
invalid_grant. Đặc vụ phải dừng và báo cáo, không lặp lại. - Phạm vi không đủ. Một
403với lỗi phạm vi. Đặc vụ không được thử lại, và phải nói rõ phạm vi nào bị thiếu. - Làm mới đồng thời. Hai cuộc gọi cho cùng một người dùng cùng một lúc. Chỉ nên có một lần làm mới.
Chạy chúng với các đối tượng giả lập. Trong Apidog, bạn có thể định nghĩa điểm cuối mã thông báo và các điểm cuối được bảo vệ, sau đó giả lập mỗi phản hồi bao gồm cả phần thân lỗi, để toàn bộ ma trận chạy mà không cần chạm vào nhà cung cấp thực. Bài viết của chúng tôi về chạy đặc vụ với đối tượng giả lập thay vì môi trường sản xuất bao gồm thói quen rộng hơn, và hướng dẫn kiểm thử API OAuth 2 của chúng tôi bao gồm chi tiết cấp yêu cầu.

Ba tích hợp và những gì chúng cần
Một trợ lý lịch. Đọc tình trạng sẵn sàng và đặt cuộc họp cho một người dùng. Truy cập được ủy nhiệm, hai phạm vi, đồng ý tại thời điểm kết nối trong trình duyệt, chạy nền sau đó. Lỗi thú vị là việc thu hồi: người dùng ngắt kết nối tích hợp và quá trình chạy hàng đêm phải nhận biết và dừng lại thay vì thử lại một cấp quyền đã chết trong một tuần.
Một đặc vụ hỗ trợ trong hộp thư chung. Hành động trên các yêu cầu hỗ trợ thuộc về một nhóm. Ở đây, vấn đề danh tính trở nên rõ ràng hơn. Hành động như tài khoản chung của nhóm là có thể bào chữa, vì tài nguyên thực sự thuộc về nhóm, nhưng mọi phản hồi sau đó đều trông giống hệt nhau trong nhật ký kiểm toán. Tốt hơn là một danh tính bot với các phạm vi riêng của nó cùng với một bản ghi về người dùng nào đã kích hoạt hoạt động, điều này giữ nguyên việc quy trách nhiệm mà không giả vờ đặc vụ là một người.
Một đặc vụ vận hành nội bộ. Khởi động lại dịch vụ và đọc bảng điều khiển trong hạ tầng của riêng bạn. Không có dữ liệu người dùng, không ủy nhiệm. Một tài khoản dịch vụ với phạm vi hẹp là câu trả lời đúng, và công việc tập trung vào việc xoay vòng và phạm vi ảnh hưởng hơn là vào sự đồng ý.
Ranh giới phân chia là quyền sở hữu. Nếu dữ liệu thuộc về một người nào đó có thể muốn thu hồi quyền truy cập của bạn một cách hợp lý, hãy sử dụng xác thực được ủy nhiệm. Nếu nó thuộc về bạn, hãy sử dụng tài khoản dịch vụ và dành công sức cho việc giới hạn phạm vi.
Giữ yếu tố con người trong việc quy trách nhiệm
Xác thực được ủy nhiệm trả lời “thay mặt cho ai”. Nó không trả lời “theo yêu cầu của ai”, và đối với công việc của đặc vụ, bạn muốn cả hai. Một mã thông báo chứng minh đặc vụ có thể hành động như một người dùng; nó không ghi lại người nào đã yêu cầu thực hiện.
Giữ danh tính thứ hai đó bên cạnh công việc. Nơi các đặc vụ thực hiện các nhiệm vụ được giao, lớp quản lý công việc là nơi tự nhiên: một nhiệm vụ Sharkly ghi lại người chịu trách nhiệm về công việc cùng với Đặc vụ hoặc Phi hành đoàn được giao thực hiện nó, điều này giữ cho trách nhiệm của con người và việc thực hiện của đặc vụ là hai sự thật riêng biệt, rõ ràng. Tài liệu Sharkly mô tả sự phân tách đó một cách chi tiết. Dù bạn lưu trữ nó như thế nào, câu hỏi kiểm toán sau một sự cố thường là “ai đã yêu cầu điều này,” và một mã thông báo đơn thuần không thể trả lời được.

Không để mô hình giữ thông tin đăng nhập
Một quy tắc kiến trúc ngăn chặn hầu hết các sự cố xác thực trong hệ thống đặc vụ: mô hình không bao giờ nhìn thấy mã thông báo.
Mã thông báo được chèn bởi bộ thực thi ở lớp HTTP, sau khi mô hình đã chọn công cụ và tạo ra các đối số. Lược đồ công cụ không có tham số token, lời nhắc không chứa thông tin đăng nhập, và phản hồi mà mô hình đọc đã bị loại bỏ tiêu đề Authorization.
Điều này quan trọng hơn đối với các đặc vụ so với các ứng dụng khách thông thường vì cách dữ liệu đầu vào của mô hình di chuyển. Bất kỳ điều gì trong ngữ cảnh đều có thể được tóm tắt thành một chuyển giao, ghi vào một dấu vết, lặp lại trong một thông báo lỗi, hoặc trả về cho người dùng đã yêu cầu đặc vụ giải thích. Không có đường dẫn nào trong số đó là có ý đồ xấu; tất cả đều là các tính năng bình thường nhưng sẽ trở thành lỗ hổng ngay khi thông tin đăng nhập nằm trong phạm vi.
Quy tắc tương tự áp dụng cho danh tính người dùng. Bộ thực thi biết người dùng nào mà lần chạy này hành động thay mặt, và nó chọn mã thông báo từ đó. Việc để mô hình đặt tên người dùng là một quyết định ủy quyền được đưa ra bởi thành phần ít đoán trước nhất trong hệ thống.
Danh sách kiểm tra
- Truy cập được ủy nhiệm bất cứ khi nào dữ liệu thuộc về người dùng, tài khoản dịch vụ chỉ dành cho tài nguyên của riêng bạn.
- Mã ủy quyền với PKCE tại thời điểm kết nối, cấp quyền thiết bị cho máy không có giao diện đồ họa.
- Phạm vi được yêu cầu cho mỗi đặc vụ, tối thiểu, được tăng cường dần dần.
- Đặc vụ phụ nhận mã thông báo đã được trao đổi, không phải bản sao cấp quyền của người dùng.
- Mã thông báo làm mới được mã hóa khi không sử dụng và không bao giờ trong lời nhắc, nhật ký hoặc dấu vết.
- Làm mới được xử lý bởi trình quản lý mã thông báo, tuần tự hóa theo từng người dùng, xoay vòng được duy trì.
401và403được coi là lỗi cuối cùng, với một thông báo nêu tên người dùng và phạm vi.- Các cấp quyền đã hết hạn được phát hiện và hiển thị cho người dùng, không được thử lại hàng đêm.
- Các hành động có rủi ro cao được bảo vệ bằng phê duyệt ngoài mã thông báo.
- Tất cả năm kịch bản xác thực được kiểm thử với các đối tượng giả lập trong CI.
Xác thực được ủy nhiệm phức tạp hơn so với khóa chia sẻ, và nó mang lại cho bạn hai điều bạn cần khi một đặc vụ hành động thay mặt người khác: người dùng có thể thu hồi nó, và nhật ký cho biết ai đã làm gì. Tải xuống Apidog để xây dựng luồng mã thông báo và các trường hợp thất bại của nó trước khi một đặc vụ chạy nó mà không cần giám sát.
Các câu hỏi thường gặp
Đặc vụ có thể tự hoàn thành luồng đồng ý OAuth không? Không, và nó không nên cố gắng. Sự đồng ý yêu cầu một người quyết định cấp quyền gì. Hãy để một người ủy quyền một lần thông qua luồng trình duyệt thông thường, sau đó để đặc vụ sử dụng cấp quyền đã tạo.
Mỗi đặc vụ có nên có ứng dụng khách OAuth riêng không? Tách biệt các ứng dụng khách cho mỗi tích hợp sản phẩm, và tách biệt các mã thông báo cho mỗi đặc vụ trong đó, thường là thông qua trao đổi mã thông báo. Các ứng dụng khách riêng biệt hữu ích khi các nhà cung cấp áp dụng giới hạn tỷ lệ theo từng ứng dụng khách hoặc khi bạn muốn thu hồi độc lập.
Điều gì xảy ra nếu mã thông báo làm mới bị xoay vòng và tôi bỏ lỡ mã mới? Người dùng bị khóa và phải kết nối lại. Duy trì mã thông báo làm mới mới trong cùng giao dịch tiêu thụ mã cũ, và tuần tự hóa việc làm mới cho mỗi người dùng để hai tác nhân không thể cạnh tranh.
Có an toàn không khi để mô hình nhìn thấy mã thông báo truy cập? Không. Mã thông báo thuộc về lớp HTTP, được chèn bởi bộ thực thi của bạn. Bất cứ điều gì mô hình nhìn thấy đều có thể kết thúc trong một dấu vết, một bản tóm tắt hoặc một phản hồi, như đã đề cập trong bài viết của chúng tôi về khóa API đặc quyền tối thiểu cho đặc vụ AI.
Làm thế nào để kiểm tra đặc vụ nào đã làm gì? Ghi nhật ký ID người dùng, tên đặc vụ, phạm vi được sử dụng và định danh mã thông báo trên mỗi cuộc gọi, không bao giờ ghi nhật ký bản thân mã thông báo. Bài viết của chúng tôi về theo dõi các cuộc gọi công cụ của đặc vụ bao gồm định dạng bản ghi.
Điều gì xảy ra nếu nhà cung cấp không hỗ trợ trao đổi mã thông báo? Lưu trữ các cấp quyền riêng biệt cho mỗi đặc vụ nơi nhà cung cấp cho phép nhiều cấp quyền, hoặc áp đặt giới hạn phạm vi trong cổng API của riêng bạn để các cuộc gọi của mỗi đặc vụ được lọc theo các hoạt động được phép trước khi chúng rời khỏi mạng của bạn.
