BazaarLinkBazaarLink
Đăng nhập
Tài liệuTham chiếu APITham chiếu SDKSử dụng AgentKỹ năng AI
Mục lục

OAuth PKCE

OAuth PKCE

OAuth 2.0 PKCE cho phép ứng dụng client công khai gọi API thay mặt người dùng.

Khi nào sử dụng OAuth PKCE

  • SPA hoặc ứng dụng di động cần thanh toán theo người dùng
  • SaaS đa thuê
  • Client công khai
  • Cần khóa riêng, có thể thu hồi cho từng người dùng (không tự hết hạn) thay vì một khóa API dùng chung

Người dùng của bạn sẽ thấy gì

Hãy mô tả các bước này trên màn hình đăng nhập của riêng bạn:

  1. Trong ứng dụng của bạn, họ chọn "Đăng nhập bằng BazaarLink" (hoặc tên nút bạn đặt).
  2. Trình duyệt của họ mở trang cấp quyền của BazaarLink. Nếu chưa đăng nhập, họ đăng nhập trước rồi tự động quay lại trang này.
  3. Trang hiển thị địa chỉ ứng dụng của bạn và điều sẽ xảy ra nếu họ cho phép: một khóa API mới được tạo cho ứng dụng, mức sử dụng được tính vào số dư BazaarLink của họ, và họ có thể thu hồi khóa bất cứ lúc nào. Họ có thể đặt tên khóa và đặt hạn mức chi tiêu hằng tháng (cả hai đều không bắt buộc).
  4. Họ nhấp Cho phép và được chuyển về ứng dụng của bạn. Nếu nhấp Từ chối, không có khóa nào được tạo, ứng dụng của bạn không được thông báo và họ thấy trang "Đã từ chối truy cập".
  5. Để thu hồi quyền truy cập sau này, họ mở trang Khóa API (https://bazaarlink.ai/keys) và vô hiệu hóa hoặc xóa khóa được đặt tên theo ứng dụng của bạn.

Cách hoạt động

Bước 1 — Khởi tạo xác thực — Tạo code_verifier ngẫu nhiên và hash SHA-256 làm code_challenge.

https://bazaarlink.ai/auth?callback_url=https://yourapp.com/callback&code_challenge=CODE_CHALLENGE&code_challenge_method=S256

Tham số URL ủy quyền

  • callback_url (bắt buộc) — nơi người dùng quay lại sau khi cho phép. Quy tắc bên dưới.
  • code_challenge (bắt buộc) — SHA-256 của code_verifier được mã hóa base64url (43 ký tự, không có padding).
  • code_challenge_method (bắt buộc) — phải là S256.
  • state (tùy chọn) — giá trị CSRF mờ đục của riêng bạn, từ 1 đến 256 ký tự ASCII in được. BazaarLink trả lại nguyên vẹn dưới dạng &state=… ngay sau code. State sai định dạng sẽ khiến trang cấp quyền hiển thị "Yêu cầu cấp quyền không hợp lệ".
  • key_label (tùy chọn) — tối đa 100 ký tự. Điền sẵn tên khóa trên trang đồng ý (người dùng vẫn có thể thay đổi). Nhãn quá dài hoặc để trống sẽ bị bỏ qua.
  • Headless tools (CLI / desktop, no callback_url) — omit callback_url. After the user allows, the page shows a one-time code (valid 5 minutes, single use) for them to enter into your tool; exchange it exactly like a redirected code. The page cannot show which app is asking, so tell users to enter the code only in a tool they started themselves.

Không được hỗ trợ

  • workspace_id và required_workspace_id — không được hỗ trợ. Khóa luôn thuộc về tài khoản của chính người dùng.
  • code_challenge_method=plain — không được hỗ trợ (chỉ S256).

Quy tắc của callback_url

  • Phải là https://. http:// chỉ được chấp nhận cho localhost, 127.0.0.1 và [::1] (mọi cổng); khi đó trang đồng ý hiển thị cảnh báo phát triển cục bộ.
  • Không có fragment (#), không có tên người dùng hoặc mật khẩu trong URL, tối đa 2048 ký tự.
  • Trang đồng ý hiển thị tên host callback của bạn để người dùng biết ứng dụng nào đang yêu cầu.
  • Sau khi người dùng cho phép, trình duyệt được chuyển đến callback_url của bạn kèm code=… (và state=… nếu bạn đã gửi; query string hiện có được giữ nguyên). Code có hiệu lực 10 phút và chỉ dùng được một lần.

Bước 2 — Đổi code lấy khóa API — Sau khi người dùng ủy quyền yêu cầu, BazaarLink chuyển hướng đến callback_url của bạn kèm một code dùng một lần. Code có hiệu lực 10 phút và chỉ dùng được một lần; code_verifier sai sẽ làm code bị hủy. POST code, code_verifier và code_challenge_method (cả ba đều bắt buộc) để nhận khóa API của người dùng.

curl https://bazaarlink.ai/api/v1/auth/keys \
-H "Content-Type: application/json" \
-d '{
  "code": "<CODE_FROM_CALLBACK_URL>",
  "code_verifier": "<CODE_VERIFIER>",
  "code_challenge_method": "S256"
}'

# Response
# { "key": "sk-bl-...", "user_id": "..." }

Bước 3 — Sử dụng khóa — Khóa trả về là khóa API BazaarLink thông thường thuộc về người dùng đã ủy quyền — mức sử dụng được tính vào số dư của người dùng đó, không phải của bạn.

curl https://bazaarlink.ai/api/v1/chat/completions \
-H "Authorization: Bearer sk-bl-..." \
-H "Content-Type: application/json" \
-d '{"model": "google/gemini-3.5-flash", "messages": [{"role": "user", "content": "Hello"}]}'

Ví dụ JavaScript (trình duyệt)

// 1) Create a verifier + S256 challenge, then send the user to BazaarLink
const b64url = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)))
.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const verifier = b64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = b64url(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier)));
sessionStorage.setItem("bl_verifier", verifier);
// "state" is optional: BazaarLink hands it back unchanged next to the code.
const state = b64url(crypto.getRandomValues(new Uint8Array(16)));
sessionStorage.setItem("bl_state", state);
location.href =
"https://bazaarlink.ai/auth?callback_url=" + encodeURIComponent(location.origin + "/callback") +
"&code_challenge=" + challenge + "&code_challenge_method=S256&state=" + state;

// 2) On /callback: check your own state, then exchange the one-time code for a user key
const params = new URLSearchParams(location.search);
if (params.get("state") !== sessionStorage.getItem("bl_state")) throw new Error("state mismatch");
const res = await fetch("https://bazaarlink.ai/api/v1/auth/keys", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
  code: params.get("code"),
  code_verifier: sessionStorage.getItem("bl_verifier"),
  code_challenge_method: "S256",
}),
});
if (!res.ok) throw new Error((await res.json()).error?.message ?? "exchange failed"); // 403 invalid_code: ask the user to authorize again
const { key } = await res.json(); // use as: Authorization: Bearer <key>
Bảo mật: Chỉ S256
Đặt code_challenge_method thành S256. Chỉ hỗ trợ S256: yêu cầu dùng plain hoặc không có code_challenge_method sẽ bị từ chối với HTTP 400.
Ghi chú
Nếu người dùng từ chối, BazaarLink không chuyển hướng về callback_url của bạn và không thông báo cho ứng dụng của bạn. Chỉ hiển thị trang "Đã từ chối truy cập", nên /auth không thể trở thành open redirect. Hãy xử lý trạng thái chưa được cấp quyền trong ứng dụng của bạn (ví dụ bằng timeout hoặc nút thử lại).

Trang /auth và POST /api/v1/auth/keys được phục vụ từ https://bazaarlink.ai (không phải api.bazaarlink.ai). Khóa bạn nhận được dùng được với cả hai base URL: https://bazaarlink.ai/api/v1 và https://api.bazaarlink.ai/v1.

Nếu người dùng đặt hạn mức chi tiêu khi cấp quyền, đó là hạn mức hằng tháng và được đặt lại theo chu kỳ tháng của tài khoản.

Lỗi

Lỗi dùng định dạng kiểu OpenAI {"error":{"message","type","code"}}. POST /api/v1/auth/keys có thể trả về:

  • 400 invalid_code_challenge_method — thiếu code_challenge_method, là plain, hoặc khác S256.
  • 400 — nội dung JSON không hợp lệ, hoặc thiếu code / code_verifier.
  • 403 invalid_code — code không tồn tại, đã hết hạn hoặc đã dùng, code_verifier sai, hoặc tài khoản không còn được phép. Tất cả các trường hợp này cố ý trả về cùng một thông báo để bên gọi không thể phân biệt. code_verifier sai cũng làm code bị hủy: hãy yêu cầu người dùng ủy quyền lại.
  • 429 rate_limited — hơn 30 lần thử đổi trong 10 phút từ một IP (xem header Retry-After).
  • 429 key_total_cap — tài khoản đã có tối đa 200 khóa API.
  • 405 — chỉ chấp nhận POST.

Tạo mã từ máy chủ của bạn (khóa quản lý)

Chủ sở hữu khóa quản lý (Management Key) cá nhân có thể tạo mã ủy quyền trực tiếp bằng POST /api/v1/auth/keys/code mà không cần đưa người dùng qua trang trình duyệt (tương thích với OpenRouter). Mã gắn với chủ sở hữu khóa quản lý, nên khóa nhận được khi đổi là một khóa tiêu chuẩn trong tài khoản của chủ sở hữu đó, với hạn mức, chu kỳ, thời hạn và tên được chọn tại đây. Dùng khi công cụ của bạn đã có khóa quản lý và muốn trao cho một ứng dụng một khóa bị giới hạn. Bước đổi mã (POST /api/v1/auth/keys) giống như ở trên.

  • callback_url (bắt buộc) và code_challenge + code_challenge_method=S256 (bắt buộc) — cùng quy tắc với URL ủy quyền.
  • key_label (tùy chọn) — tối đa 100 ký tự; tên của khóa. Mặc định: "App: <host callback của bạn>".
  • limit (tùy chọn) — hạn mức chi tiêu bằng USD, từ 0 trở lên; 0 hoặc bỏ trống nghĩa là không giới hạn.
  • usage_limit_type (tùy chọn) — daily, weekly hoặc monthly: tần suất đặt lại hạn mức. Chỉ dùng cùng với limit; mặc định là monthly.
  • expires_at (tùy chọn) — thời điểm khóa hết hạn: thời gian ISO 8601 UTC trong tương lai có kèm giây, ví dụ 2026-12-31T23:59:59Z. Giá trị chỉ chính xác đến phút sẽ bị từ chối. Nếu bỏ trống, khóa vẫn có hiệu lực cho đến khi bị thu hồi.
  • workspace_id — không được hỗ trợ (400). spawn_agent và spawn_cloud được chấp nhận nhưng bị bỏ qua.
curl https://bazaarlink.ai/api/v1/auth/keys/code \
-H "Authorization: Bearer sk-bl-YOUR_MGMT_KEY" \
-H "Content-Type: application/json" \
-d '{
  "callback_url": "https://yourapp.com/callback",
  "code_challenge": "<CODE_CHALLENGE>",
  "code_challenge_method": "S256",
  "key_label": "My app",
  "limit": 10,
  "usage_limit_type": "monthly",
  "expires_at": "2026-12-31T23:59:59Z"
}'

# Response
# { "data": { "id": "<ONE_TIME_CODE>", "app_id": 123456789, "created_at": "2026-10-22T12:00:00.000Z" } }
# Then POST { "code": "<ONE_TIME_CODE>", "code_verifier": "<CODE_VERIFIER>", "code_challenge_method": "S256" } to /api/v1/auth/keys

Phản hồi: { "data": { "id", "app_id", "created_at" } }. id là mã dùng một lần để đổi (có hiệu lực 10 phút, chỉ một lần). BazaarLink không có sổ đăng ký ứng dụng, nên app_id là một số nguyên cố định suy ra từ địa chỉ callback của bạn: cùng một địa chỉ luôn nhận cùng một app_id.

  • 401 unauthorized — không có khóa Bearer, hoặc khóa không tồn tại.
  • 403 management_key_required — khóa không phải khóa quản lý cá nhân (khóa tiêu chuẩn và khóa quản lý của tổ chức bị từ chối).
  • 403 account_not_allowed — chủ sở hữu khóa đã bị đình chỉ hoặc xóa.
  • 400 — invalid_callback_url, invalid_code_challenge_method, invalid_code_challenge, invalid_expires_at, workspace_not_supported, hoặc nội dung yêu cầu sai định dạng.
  • 429 rate_limited — hơn 30 mã mỗi giờ cho một chủ sở hữu hoặc 120 mã mỗi giờ từ một IP (xem Retry-After).

Khóa bạn nhận được

  • Một khóa API BazaarLink tiêu chuẩn (sk-bl-…), cùng loại với khóa tạo trên trang Khóa API. Khóa không tự hết hạn.
  • Mức sử dụng được tính vào số dư BazaarLink của người dùng. Nếu người dùng đặt hạn mức chi tiêu trên trang đồng ý, đó là hạn mức hằng tháng.
  • Hoạt động với cả hai base URL: https://bazaarlink.ai/api/v1 và https://api.bazaarlink.ai/v1.
  • Được tính vào giới hạn 200 khóa của tài khoản và hiển thị trong danh sách Khóa API của người dùng, nơi họ có thể vô hiệu hóa hoặc xóa bất cứ lúc nào.
  • Tài khoản bị đình chỉ hoặc đã xóa không thể cấp quyền cho ứng dụng, và code được cấp trước khi đình chỉ sẽ bị từ chối khi đổi (403 invalid_code).

Bạn nhận được gì

  • Luồng Authorization Code + PKCE
  • Thanh toán theo người dùng: mỗi người dùng tự trả cho mức sử dụng của mình từ số dư của chính họ
  • Mỗi người dùng có một khóa riêng, hiển thị trên trang Khóa API của họ để họ có thể thu hồi
Support
Hi! How can we help you?
Send a message and we'll get back to you soon.
OAuth PKCE | BazaarLink