Vì sao app không nên sở hữu hóa đơn inference
Gần như mọi app đang dựng đều muốn LLM ở đâu đó: hộp chat, nút tóm tắt, agent làm việc. Nhưng vừa thêm là bạn thừa kế bài toán billing. Nếu ship key nhà cung cấp của mình, mọi request user gửi nằm trên hóa đơn bạn — một user hăng có thể chạy bill lớn hơn số họ trả bạn, và bạn thành người đo, rate-limit, và canh gian lận inference mà bạn chẳng muốn chạy.
Lối thoát thường gặp là bắt từng user dán key OpenAI hoặc Anthropic thô. Chạy được, nhưng trải nghiệm tệ: họ quản secret không hiểu, bạn lưu credential sống lâu làm được nhiều hơn gọi model, và một lỗi log là lộ. Không lựa chọn nào là thứ bạn muốn: để user trả inference của họ, và đừng đụng key của họ.
Đó đúng là thứ "Sign in with AnyRouter" cho bạn. AnyRouter là nền app dựng quanh — gateway đã có tài khoản user, credit hoặc free tier, và mọi model trong một catalog. Thêm nút đăng nhập, user duyệt một lần, app nhận key tạm, chỉ inference, scoped đúng user đó. Mọi request tính vào tài khoản họ, không phải bạn. Bạn ship sản phẩm; AnyRouter lo billing model, failover nhà cung cấp, và audit log.
Luồng chạy thế nào
Đây là OAuth 2.1 authorization-code chuẩn với PKCE. App của bạn là public client — không có client secret phải giữ, nên chạy được từ frontend chỉ trình duyệt. User được đẩy tới màn consent AnyRouter, duyệt scope chỉ inference, callback đổi code lấy key sk-ar-v1-* sống ngắn.
sequenceDiagram
participant App as Your app
participant AR as AnyRouter
participant User
App->>AR: (once) POST /oauth/register {app_type:"signin"}
AR-->>App: client_id (public, safe to embed)
User->>App: clicks "Sign in with AnyRouter"
App->>AR: GET /oauth/authorize?client_id&redirect_uri&code_challenge
AR->>User: consent screen — your app name + inference scope
User->>AR: Approve
AR-->>App: redirect_uri?code=...
App->>AR: POST /oauth/token {code, code_verifier}
AR-->>App: access_token = sk-ar-v1-* (expires in 30d)
App->>AR: POST /chat/completions (Bearer sk-ar-v1-*)
Note over AR: billed to the user's accountMọi endpoint dưới đây nằm dưới https://anyrouter.dev/api/v1/mcp/oauth/* — cùng gateway OAuth thống nhất AnyRouter dùng cho MCP và managed access enterprise. Đăng ký một lần, rồi chạy cặp authorize/token mỗi user.
Bước 1 — Đăng ký app một lần
Đăng ký mở: không cần tài khoản AnyRouter để đăng ký app, không client secret. Gửi tên app, redirect URI chính xác (chỉ https, localhost được lúc dev), và app_type: "signin".
curl https://anyrouter.dev/api/v1/mcp/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My AI App",
"redirect_uris": ["https://myapp.example/callback"],
"app_type": "signin",
"origin_url": "https://myapp.example"
}'Response chứa client_id. Nó công khai — nhúng vào frontend không lo. App signin vào flagged cho review admin nhẹ và hiện trong admin console AnyRouter; đó là cách app lạm dụng bị suspend, và không chặn user đăng nhập.
Bước 2 — Đưa user tới consent
Khi bấm nút, sinh PKCE verifier và challenge S256 trong trình duyệt, cất chúng (cộng state chống CSRF), rồi redirect tới authorize endpoint.
<button id="signin">Sign in with AnyRouter</button>
<script>
const CLIENT_ID = "your_client_id"
const REDIRECT_URI = "https://myapp.example/callback"
const b64url = (b) =>
btoa(String.fromCharCode(...new Uint8Array(b)))
.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")
document.getElementById("signin").onclick = async () => {
const verifier = b64url(crypto.getRandomValues(new Uint8Array(32)))
const challenge = b64url(
await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier)),
)
const state = b64url(crypto.getRandomValues(new Uint8Array(16)))
sessionStorage.setItem("ar_verifier", verifier)
sessionStorage.setItem("ar_state", state)
const url = new URL("https://anyrouter.dev/api/v1/mcp/oauth/authorize")
url.search = new URLSearchParams({
client_id: CLIENT_ID,
redirect_uri: REDIRECT_URI,
response_type: "code",
code_challenge: challenge,
code_challenge_method: "S256",
state,
}).toString()
location.href = url.toString()
}
</script>User tới màn consent AnyRouter — có Clerk, nên họ đăng nhập tài khoản AnyRouter nếu chưa — thấy tên app và đúng scope được xin, rồi duyệt. AnyRouter 302 về redirect_uri với ?code=…&state=….
Bước 3 — Đổi code lấy key
Trên trang callback, kiểm state khớp thứ bạn lưu, rồi POST code và PKCE verifier tới token endpoint. Nó nhận cả form-encoded và JSON.
const params = new URLSearchParams(location.search)
if (params.get("state") !== sessionStorage.getItem("ar_state")) {
throw new Error("State mismatch — restart sign-in")
}
const res = await fetch("https://anyrouter.dev/api/v1/mcp/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "authorization_code",
code: params.get("code"),
redirect_uri: "https://myapp.example/callback",
client_id: "your_client_id",
code_verifier: sessionStorage.getItem("ar_verifier"),
}),
})
const token = await res.json()
// {
// "access_token": "sk-ar-v1-...",
// "token_type": "Bearer",
// "scope": "inference read:profile",
// "expires_in": 2592000
// }
saveTokenForThisUser(token.access_token)Lưu một token mỗi user. Giá trị sk-ar-v1-* đó là key AnyRouter thường từ góc app — nhưng chỉ chạy inference, scoped đúng user này, và hết hạn (mặc định 30 ngày).
Bước 4 — Chạy inference như user
Dùng token đúng như mọi API key AnyRouter. Mọi request đo theo số dư user đã đăng nhập — credit của họ, hoặc free tier nếu họ đang ở đó. Bạn không thấy hóa đơn model.
const res = await fetch("https://anyrouter.dev/api/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "google/gemini-3.5-flash",
messages: [{ role: "user", content: "Hello from my app!" }],
}),
})Vì đây là key AnyRouter thật, bạn nhận cả gateway miễn phí: mọi model trong catalog theo id, failover tự động khi nhà cung cấp bị rate-limit, và dialect OpenAI, Anthropic, Responses cùng một endpoint. Để hiện badge "Signed in as …", scope read:profile cho gọi GET /api/v1/me lấy display name, avatar, và gói.
User thấy và kiểm soát gì
Màn consent cố ý hẹp. Token sign-in mang đúng hai scope, không gì khác — xin thêm không có hiệu lực, AnyRouter kẹp lại hai cái này:
| Scope | Cho phép gì |
|---|---|
| inference | Gọi /chat/completions, /messages, /responses, /embeddings — tính vào user |
| read:profile | GET /api/v1/me — display name, avatar, gói |
Phần quan trọng là token sign-in **không** làm được gì: không tạo hay thu hồi API key, không đọc usage hay billing, không đổi setting tài khoản, không thừa quyền admin. Không có quyền quản trị nào — tệ nhất token bị lộ chỉ tiêu một ngân sách inference có trần của chính user trước khi hết hạn.
User vẫn kiểm soát phía kia. Từ **Dashboard → Connected apps** họ thu hồi app bất cứ lúc nào, vô hiệu mọi token bạn đã nhận cho user đó. Cả hết hạn và thu hồi hiện cùng cách với code bạn — 401. Không refresh token ở v1: coi mọi 401 là "đăng nhập lại" và chạy lại luồng authorize im lặng. Nếu user vẫn đăng nhập AnyRouter, họ đi qua consent rồi về với token mới trong vài giây.
Dựng trên nền, không vòng quanh nó
Điểm chính: inference thôi là bài của bạn. Bạn không cấp key, không ứng hóa đơn model, không lưu secret nhà cung cấp của ai, không dựng hệ đo. User mang tài khoản họ đã có; AnyRouter lo tiền và model. App chỉ gọi endpoint.
Tham chiếu endpoint đầy đủ, scope, và snippet trình duyệt từ đầu đến cuối.
Đọc hướng dẫn Sign in with AnyRouterBài liên quan
Use AnyRouter with the Vercel AI SDK
@anyr/ai-sdk-provider wraps the AnyRouter gateway as a standard Vercel AI SDK provider. Call createAnyRouter(), pass a provider/model id, and generateText, streamText, and generateObject work unchanged — switching models is a one-string edit.
One API for every LLM
One OpenAI-compatible endpoint reaches every model in the catalog — 170+ models addressed as provider/model, with automatic failover across upstreams, routing presets, and streaming. Point your existing client at AnyRouter and change a model with a string, not a rewrite.
Gửi request đầu trong hai phút
Bắt đầu miễn phí với key của bạn, hoặc nạp rồi trả theo token. Go: $4 credit/tháng — $2/tháng, hoặc miễn phí khi góp một provider key.
Bắt đầu miễn phí