v1.0.3

Client Integration

Last updated


This page lists which cookies and headers a client sends, which responses it receives, and how browser and non-browser clients each integrate.

Request Inputs


SourceNamePurpose
Cookieaccess_token (AccessTokenCookieKey)Access Token; takes precedence over the Authorization header
HeaderAuthorization: Bearer <token>Access Token when no cookie is present
HeaderX-Refresh-IDRefresh ID; takes precedence over the cookie
Cookierefresh_id (RefreshIdCookieKey)Refresh ID
HeaderX-Device-FPSupplies the device fingerprint directly
HeaderX-Device-IDDevice ID; takes precedence over the cookie
Cookieconn.device.idDevice ID

Response Outputs


WhenSet-CookieHeader
Create() loginaccess_token, refresh_id, conn.device.id—
Refresh: re-sign onlyaccess_tokenX-New-Access-Token
Refresh: full reissueaccess_token, refresh_id—
Revoke()Clears access_token, refresh_id—
Every fingerprint computation without X-Device-FPconn.device.id (90 days)—

Cookies default to HttpOnly, http.SameSiteLaxMode, Path /, and not Secure; override them with Cookie in Configuration.

Browsers


Same-site web pages need nothing extra: cookies are sent and updated automatically and refresh is transparent to the frontend. Watch for:
CaseHandling
Cross-site APISend with credentials: "include"; allow credentials in backend CORS and set cookies to http.SameSiteNoneMode with Secure
Several requests fired right after the Access Token expiresSome receive 429; retry after a short delay (see Transparent Refresh)
ProductionSet Cookie.Secure to true

  1. async function api(path, init = {}) {
  2. for (let attempt = 0; attempt < 3; attempt++) {
  3. const res = await fetch(path, { ...init, credentials: "include" });
  4. if (res.status !== 429) return res;
  5. await new Promise(r => setTimeout(r, 300));
  6. }
  7. throw new Error(`refresh contention on ${path}`);
  8. }

Non-Browser Clients (Mobile Apps, CLIs, Services)


RequirementReason
Generate one stable value per device and send it as X-Device-FP on every requestWith an unrecognized UA the fingerprint changes every time (see Device Fingerprint)
Send both the Access Token and the Refresh IDVerification compares the Refresh ID inside the token (step 7 of Verification Flow)
Parse Set-Cookie on every responseOn full reissue the new Refresh ID appears only in Set-Cookie: refresh_id, with no matching header
Replace the local Access Token when X-New-Access-Token arrivesThat is the new token on re-sign only

An HTTP client with a cookie jar (Go's net/http/cookiejar, OkHttp's cookie jar) that stores access_token/refresh_id is the least error-prone way to catch full reissues.
  1. # Login
  2. curl -s -H "X-Device-FP: device-7f3a9c" -c jar.txt -b jar.txt -X POST http://localhost:8080/login
  3. # Send the same fingerprint every time and write response cookies back to the jar
  4. curl -s -H "X-Device-FP: device-7f3a9c" -c jar.txt -b jar.txt http://localhost:8080/me -D -

Passing credentials as headers:
  1. curl -s http://localhost:8080/me \
  2. -H "X-Device-FP: device-7f3a9c" \
  3. -H "Authorization: Bearer <token>" \
  4. -H "X-Refresh-ID: <refresh_id>" \
  5. -D -

Error Handling


StatusClient action
401Send the user to login
429Retry after a short delay
400Credentials do not match the device or are malformed; clear local credentials and log in again
5xxServer or Redis failure; retryable

See Status Codes and Error Tags for the full mapping.
中文