Authentication
BioTime SDK authenticates against the device's /api-token-auth/ endpoint and manages the resulting token for you, via BioTime\Auth\TokenManager.
How it works
Your code calls a resource method
│
▼
BioTime\Client
│
▼
TokenManager::getToken()
│
┌────┴─────┐
│ Cached? │
└────┬─────┘
Yes │ No
┌────┘ └──────────────┐
▼ ▼
Use cached token POST /api-token-auth/
│ (username + password)
│ │
│ ▼
│ Cache the token
│ │
└───────────┬───────────┘
▼
Send the actual API request
│
┌───────┴────────┐
200/2xx 401 Unauthorized
│ │
▼ ▼
Return result Forget cached token, force
re-authenticate, retry once
│
┌──────┴──────┐
Success Still fails
│ │
▼ ▼
Return result Throw ApiExceptionToken acquisition
TokenManager::authenticate() sends the configured username/password as form parameters to POST /api-token-auth/. The device is expected to respond with a JSON body containing a token field. If the request fails, returns a non-2xx status, or the response has no token, an AuthenticationException is thrown.
Token caching
Where the token is stored depends on whether a PSR-16 cache was provided in Config:
- With a cache (
Config::$cacheset) — the token is written via$cache->set($tokenCacheKey, $token, $tokenTtlSeconds), so it survives across requests/processes if your cache backend does (e.g. Redis, Memcached, a database-backed cache, or Laravel's cache store). - Without a cache — the token is kept in an in-memory static array (
TokenManager::$memoryStore) that only lives for the current PHP process. This means a plain PHP script or a single job run reuses the token across multiple calls within that run, but a fresh HTTP request in a typical web app starts with no cached token unless you configure a real cache.
See Caching for how to plug in a PSR-16 cache, and Laravel integration for how the framework's cache store is wired up automatically.
Token expiration
The device's own token lifetime is not queried by the SDK — instead, tokenTtlSeconds (default 43200, i.e. 12 hours, matching the device's own default) controls how long the SDK's cache entry is considered valid. When the cache entry expires, the next call re-authenticates.
Automatic re-authentication on 401
Every authenticated request goes through Client::authenticatedRequest(), which:
- Sends the request with the current token.
- If the response is
401 Unauthorized, it forgets the cached token, forces a freshgetToken(forceRefresh: true)call, and retries the request once with the new token. - If the retry also fails, the failure (including a
401again) is surfaced as anApiException/AuthenticationExceptionlike any other failed request.
This means an expired token is transparent to your application code in the common case — you don't need to catch a 401 and manually retry yourself.
What you don't need to do
You never call TokenManager directly, and you never attach the Authorization header yourself — every resource method goes through Client, which does this for every request. You only need to supply username/password via Configuration.