伺服器端接入
為你的網站整合手機綁定與確認流程,提供 API 參數說明和官方 SDK 範例。
AuthLink 正式 API 為 https://api.getauthlink.com;網站業務後端地址仍填寫你自己的 API,官網網域不用於介面呼叫。
接入流程與信任邊界
- 運營方為網站創建獨立租戶,把憑據交付網站後端。
- 後端從已登錄 session 取得穩定使用者 ID,生成隨機 state、創建冪等鍵和獨立消費冪等鍵。
- 創建 bind / confirm 請求,保存使用者、會話、用途、state、requestId、expiresAt 及冪等鍵,再把公開 ticket 返回瀏覽器。
- 使用者在 AuthLink App 中核對網站、帳號和數字,然後批准請求。
- 後端查詢到 approved / consumed 後,使用保存的 state 和消費冪等鍵調用 consume。
- 校驗回執,在自己的資料庫事務中以 requestId / verificationId 唯一約束記錄確認,才向網頁返回 confirmed。受保護業務檢查該記錄。
沒有 webhook。approved、六位數字以及前端回呼都不能直接放行業務。AuthLink 不接收 action、金額、transactionHash 或業務授權範圍。
租戶與鑒權
向營運方取得本站的 tenantId、apiKey、origin 和 expiresAt。新金鑰永久有效;營運方仍可輪換、撤銷金鑰或停用租戶。舊金鑰保留原有效期限,僅在網站後端安全保存。
| 請求頭 | 要求 |
|---|---|
Authorization
|
Bearer <tenant API key>,全部 integration 接口必填 |
Content-Type
|
POST 使用 application/json;拒絕未知字段和重復 JSON 鍵 |
Idempotency-Key
|
創建和消費必填,16–128 位字母、數字、下划線或連字符。建議 32 位元組安全隨機 Base64URL;同次網路重試復用 |
Origin / Cookie
|
API 不接受瀏覽器 Origin 或 Cookie;網頁通過自己的網站後端接入 |
創建請求與參數
POST /v1/integration/requests · API origin:
{
"externalUserId": "stable-user-id",
"accountLabel": "Account display name",
"purpose": "bind",
"state": "REPLACE_WITH_SAVED_43_CHARACTER_RANDOM_STATE"
}
| 字段 | 類型 / 必填 | 說明 |
|---|---|---|
externalUserId
|
string / 是 | 1–128 位元組可打印 ASCII(0x21–0x7e)。由登錄 session 取得,不可回收或由瀏覽器決定 |
accountLabel
|
string / 是 | 非空,最多 160 個 UTF-8 位元組;拒絕控制字符及指定雙向控制字符 |
purpose
|
string / 是 | 首次綁定 bind,已綁定後確認 confirm
|
state
|
string / 是 | 32 位元組安全隨機數,無 padding Base64URL,43 字符;後端保存並在消費時原樣核對 |
成功返回 HTTP 200 和 ticket:
| 返回字段 | 含義 |
|---|---|
requestId
|
43 字符請求標識 |
purpose / expiresAt
|
請求用途和 UTC ISO 時間;有效期 120 秒 |
qr
|
QR JSON 字符串:v、origin、requestId、token;只交給前端元件或 App,禁止寫入日誌 |
qrSvg
|
伺服器端兼容字段;官方網頁包會自行生成 QR,不使用 innerHTML 插入此字段 |
pairingCode
|
當前六位字符串,保留前導零 |
displayCode
|
value、serverTime、validUntil、periodSeconds:30 |
同一帳號的新請求會拒絕舊的未完成請求。數字更新不延長期限,不需要另建請求。
狀態、消費與帳號接口
| 方法 / 路徑 | 輸入 | 返回 / 說明 |
|---|---|---|
GET /v1/integration/requests/{id}
|
租戶 bearer | requestId、status、expiresAt、displayCode(可選)。new / pending / approved / consumed / rejected / expired |
POST /v1/integration/requests/{id}/consume
|
租戶 bearer、Idempotency-Key,body {"state":"saved-43-character-state"}
|
approved 後領取 confirmed 回執;網路結果不明時復用原消費鍵 |
POST /v1/integration/accounts/status
|
租戶 bearer,body {"externalUserId":"..."}
|
externalUserId、status:unbound/bound/deleted、platform(未綁定為 null) |
POST /v1/integration/accounts/delete
|
租戶 bearer,body {"externalUserId":"..."}
|
{"status":"deleted"};永久撤銷本站 AuthLink 綁定,不能重綁;不刪除 CMS 帳號 |
| consume 返回字段 | 核驗要求 |
|---|---|
status / authenticationPolicy
|
status 必須為 confirmed;authenticationPolicy 由官方 SDK 驗證 |
requestId / tenantId / externalUserId
|
必須對應當前請求、租戶、已登錄使用者和已保存會話 |
purpose / state / expiresAt
|
與後端持久保存值完全一致,期限未過 |
verificationId / platform / consumedAt
|
校驗結果 ID、android / ios 與有效時間;以唯一約束落庫 |
Go SDK 與方法
獨立模塊 authlink.local/sdk,Go 1.22+,只依賴標準庫。適合 Go 網站後端;不需要引入整個 AuthLink 伺服器端或 MySQL 驅動。
安裝與初始化
解壓縮後保留 SDK 自己的 go.mod;僅將 SDK 模組接入你的網站專案。
SDK 當前隨原始碼交付,未發佈公共 Go module。在你的網站 Go 項目中解壓,使用本地 replace:
mkdir -p third_party
tar -xzf authlink-go.tar.gz -C third_party
go mod edit -replace=authlink.local/sdk=./third_party/authlink-go
go mod edit -require=authlink.local/[email protected]
go mod tidy
import authlink "authlink.local/sdk"
client, err := authlink.New(authlink.Config{
Origin: "https://api.getauthlink.com", // API origin, not the marketing site
TenantID: os.Getenv("AUTHLINK_TENANT_ID"),
APIKey: os.Getenv("AUTHLINK_API_KEY"),
})
if err != nil { return err }
// Get userID and label from your authenticated website session.
state, err := authlink.RandomToken()
if err != nil { return err }
createKey, err := authlink.RandomToken()
if err != nil { return err }
consumeKey, err := authlink.RandomToken()
if err != nil { return err }
// Persist state, both keys and user/session first. Reuse them on retries.
ticket, err := client.CreateRequest(ctx, authlink.CreateInput{
ExternalUserID: userID, AccountLabel: label,
Purpose: "bind", State: state,
}, createKey)
if err != nil { return err }
expected := authlink.Expected{
RequestID: ticket.RequestID, ExternalUserID: userID,
Purpose: ticket.Purpose, State: state, ExpiresAt: ticket.ExpiresAt,
}
// Save expected and consumeKey before returning the public ticket.
// Load saved expected / consumeKey after checking session, CSRF and ownership.
status, err := client.Status(ctx, expected.RequestID)
if err != nil { return err }
if status.Status == "approved" || status.Status == "consumed" {
result, err := client.Consume(ctx, expected, consumeKey)
if err != nil { return err }
// The SDK has validated the full receipt. Persist a unique confirmation
// in your own transaction, associated with this user/session and business.
_ = result
}
| Config 參數 | 說明 |
|---|---|
Origin / TenantID / APIKey | 必填,API HTTPS origin(無末尾斜槓)、租戶 ID、私有後端金鑰 |
AllowLocalHTTP | 預設 false;只允許回環 / localhost 開發測試 |
HTTPClient | 可選 *http.Client;SDK 複製後禁止重定向、禁用 CookieJar。transport 需可信,每次請求有 10 秒上限,父 context 和更短的 client timeout 優先 |
| 方法 | 返回與用途 |
|---|---|
CreateRequest(ctx, CreateInput, key) | (Ticket, error),CreateInput:ExternalUserID、AccountLabel、Purpose、State;預設用途 confirm,首次綁定 bind |
Status(ctx, requestID) | (RequestStatus, error),查詢狀態與當前 displayCode,不能直接當作業務確認 |
Consume(ctx, Expected, key) | (Confirmation, error),消費並校驗策略、租戶、使用者、請求、用途、state、期限、結果 ID 和平台 |
WaitForConfirmation(ctx, Expected, key, interval) | (Confirmation, error),可選後端輪詢;approved / consumed 後消費。interval=0 預設 1500 毫秒,允許 100 毫秒–10 秒 |
AccountStatus(ctx, externalUserID) | (AccountStatus, error),查詢 unbound / bound / deleted;Platform 為可空 *string |
DeleteAccount(ctx, externalUserID) | error;永久撤銷本站 AuthLink 綁定,原帳號不可重綁,不刪除 CMS 使用者 |
RandomToken() | (string, error),32 位元組安全隨機 Base64URL;state、創建鍵和消費鍵分別生成並保存 |
ValidateConfirmation(result, expected, tenantID, now) | error,純字段校驗;不是簽章驗證器,不能把瀏覽器自報回執當可信授權 |
Expected 包含 RequestID、ExternalUserID、Purpose、State、ExpiresAt;從後端已保存的請求讀取,保持原 expiresAt 字符串。返回類型均帶協議 JSON tag,可編碼給官方網頁元件。SDK 拒絕未知字段、重復鍵和大小寫別名;協議增加字段時需要同步升級 SDK。
等待、取消與錯誤處理
result, err := client.WaitForConfirmation(ctx, expected, consumeKey, 0)
switch {
case err == nil:
// Persist result in your own transaction before updating UI or authorizing work.
_ = result
case errors.Is(err, authlink.ErrRejected):
// The user rejected the request.
case errors.Is(err, authlink.ErrExpired):
// The request expired.
case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
// This call was canceled or timed out; the platform request is not revoked.
default:
var api *authlink.APIError
if errors.As(err, &api) {
// Handle api.StatusCode / api.Code; retry with the same consumption key.
}
}
Go 完整範例
範例使用兩個階段:create 建立請求,result 查詢並消費已儲存的請求。只在使用者完成手機確認後執行 result。範例不包含網站的工作階段管理和資料庫持久化,不能直接取代業務授權。
展開或收合完整 Go 範例
// This example creates a request, then queries/consumes a previously saved one.
// Supply tenant credentials only through your backend's private environment.
package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"time"
authlink "authlink.local/sdk"
)
func run() error {
mode := flag.String("mode", "create", "create or result")
user := flag.String("user", "", "stable user ID from your authenticated session")
label := flag.String("label", "", "account display label")
purpose := flag.String("purpose", "bind", "bind or confirm")
flag.Parse()
client, err := authlink.New(authlink.Config{Origin: os.Getenv("AUTHLINK_ORIGIN"), TenantID: os.Getenv("AUTHLINK_TENANT_ID"), APIKey: os.Getenv("AUTHLINK_API_KEY")})
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
switch *mode {
case "create":
// For this standalone example, supply already-generated and saved values
// through the environment. A website must persist them before networking.
ticket, err := client.CreateRequest(ctx, authlink.CreateInput{ExternalUserID: *user, AccountLabel: *label, Purpose: *purpose, State: os.Getenv("AUTHLINK_STATE")}, os.Getenv("AUTHLINK_CREATE_KEY"))
if err != nil {
return err
}
// Contains QR token: deliver to the intended user's UI; never log it.
return json.NewEncoder(os.Stdout).Encode(ticket)
case "result":
expected := authlink.Expected{RequestID: os.Getenv("AUTHLINK_REQUEST_ID"), ExternalUserID: *user, Purpose: *purpose, State: os.Getenv("AUTHLINK_STATE"), ExpiresAt: os.Getenv("AUTHLINK_EXPIRES_AT")}
status, err := client.Status(ctx, expected.RequestID)
if err != nil {
return err
}
if status.Status != "approved" && status.Status != "consumed" {
return json.NewEncoder(os.Stdout).Encode(status)
}
result, err := client.Consume(ctx, expected, os.Getenv("AUTHLINK_CONSUME_KEY"))
if err != nil {
return err
}
// In a website, persist result atomically with unique request/result IDs,
// user/session and business association before returning confirmed.
_ = result
fmt.Fprintln(os.Stderr, "Receipt validated; this example does not persist or authorize business.")
return nil
default:
return errors.New("mode must be create or result")
}
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
執行參數
| 參數 | 用途 |
|---|---|
-mode | create 建立請求;result 查詢和消費結果,預設 create。 |
-user | 已登入網站使用者的穩定 ID,由後端工作階段取得。 |
-label | 建立請求時顯示的帳號名稱。 |
-purpose | 首次綁定使用 bind;後續確認使用 confirm,範例預設 bind。 |
後端環境變數
| 變數 | 用途 |
|---|---|
AUTHLINK_ORIGIN / AUTHLINK_TENANT_ID / AUTHLINK_API_KEY | 由營運方提供的 API 位址、租戶 ID 和後端金鑰,僅儲存在網站後端。 |
AUTHLINK_STATE / AUTHLINK_CREATE_KEY / AUTHLINK_CONSUME_KEY | 後端分別產生並持久儲存的 state、建立冪等鍵和消費冪等鍵;重試時重用。 |
AUTHLINK_REQUEST_ID / AUTHLINK_EXPIRES_AT | result 階段從後端儲存的 ticket 讀取請求 ID 和原始到期時間。 |
完整範例已包含在 SDK 整包的 examples/quickstart/main.go 中。可複製上方程式碼儲存為 main.go;憑證透過後端環境注入,不要寫入程式碼或瀏覽器。
# Run from a Go project that already includes the SDK module.
# Supply the private environment variables listed above before running.
export AUTHLINK_ORIGIN=https://api.getauthlink.com
go run . -mode create -user stable-user-id -label "Example account" -purpose bind
# After phone approval, load the SAME saved request context before running:
go run . -mode result -user stable-user-id -purpose bind
Node SDK 與方法
// Copy /sdk/node/ and /sdk/browser/ with their relative paths. Server import only.
import { AuthLink, randomToken } from './sdk/node/authlink.mjs';
const auth = new AuthLink({
origin: 'https://api.getauthlink.com',
tenantId: process.env.AUTHLINK_TENANT_ID,
apiKey: process.env.AUTHLINK_API_KEY,
});
// user must come from your authenticated session. This is the creation stage.
const state = randomToken();
const createKey = randomToken();
const consumeKey = randomToken();
// Persist these values and user/session before networking. Reuse on retries.
const ticket = await auth.createRequest({
externalUserId: user.id, accountLabel: user.displayName,
purpose: 'bind', state,
}, createKey);
const expected = { requestId: ticket.requestId, expiresAt: ticket.expiresAt,
externalUserId: user.id, purpose: 'bind', state };
// Save expected, consumeKey and session association before returning ticket.
// Your result endpoint loads the saved request context:
const status = await auth.status(expected.requestId);
if (['approved', 'consumed'].includes(status.status)) {
const verified = await auth.consume(expected, consumeKey);
// The SDK has verified the receipt. Persist a unique confirmation atomically.
// Return verified to the browser only after the database commit succeeds.
}
| 構造參數 / 方法 | 說明 |
|---|---|
origin / tenantId / apiKey
|
必填;API HTTPS origin(無末尾斜槓)、租戶 ID 和私有金鑰 |
allowLocalHttp
|
預設 false;僅本機回環開發 |
createRequest(input, key)
|
創建;input 字段見上表,purpose 未傳時預設 confirm |
status(requestId, signal?)
|
查詢狀態;signal 為可選 AbortSignal |
consume(expected, key, signal?)
|
消費並校驗回執,返回已驗證的結果 |
waitForConfirmation(expected, options)
|
後端輪詢工具;options.idempotencyKey 與 onConfirmed 必填;可選 onRejected / onExpired、signal、intervalMs(預設 1500,範圍 100–10000 毫秒);仍由業務持久化結果 |
accountStatus(id) / deleteAccount(id)
|
查詢 / 永久撤銷本站帳號綁定 |
安裝說明、方法參數和使用範例均在本頁。Node SDK 與所需依賴已合併為一個壓縮包,解壓縮後保留 node 與 browser 的相對目錄;網頁元件透過 npm 安裝。
mkdir -p sdk
tar -xzf authlink-node.tar.gz -C sdk
# Import ./sdk/node/authlink.mjs from your backend project.
錯誤處理
| HTTP | 常見 code | 處理 |
|---|---|---|
| 400 | INVALID_REQUEST / INVALID_SUBJECT / INVALID_DISPLAY_TEXT / INVALID_IDEMPOTENCY_KEY | 按字段約束修正輸入;不要自動生成新請求掩蓋未知結果 |
| 401 | UNAUTHORIZED / STATE_MISMATCH / SERVER_CREDENTIAL_REQUIRED | 檢查私有憑據、state、Host 和是否繞過網站後端 |
| 404 | NOT_FOUND | 請求或發佈渠道不存在 |
| 409 | APPROVAL_REQUIRED / REQUEST_EXPIRED / REQUEST_USED / IDEMPOTENCY_CONFLICT / ACCOUNT_ALREADY_BOUND / ACCOUNT_NOT_BOUND | 按業務狀態處理,過期後由使用者重新發起,保留冪等恢復語義 |
| 429 | RATE_LIMITED / BUSY | 退避重試,復用原冪等鍵 |
| 503 | ATTESTATION_UNAVAILABLE | 驗證服務暫時無法使用,請稍後重試 |
錯誤 body 為 {"code":"..."}。未知錯誤不當成成功。接口響應不能共享緩存,禁止記錄 bearer、二維碼 token、完整證明和原始秘密。