The Dart client manages authentication tokens automatically. After the first successful auth call, the access token is stored and injected into every subsequent request without any additional code in the application.
After auth, prefer client.db.listen for live screens instead of polling.
See Streaming and Database.
The X-Auth Header Pattern#
Zonai uses a response-header mechanism to deliver access tokens to the client.
Every auth endpoint — sign-in, sign-up, OTP confirmation, magic-link
confirmation, and token refresh — sets an X-Auth header on the response in
addition to the JSON body:
HTTP/1.1 200 OK
X-Auth: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{ "data": { "accessToken": "eyJ...", "user": { ... } } }
The client's Interceptor reads X-Auth from every incoming response and
persists the value via the configured ZonaiStorage. On every outgoing request
the same interceptor reads the stored token and injects it as:
Authorization: Bearer <token>
This means the application never needs to extract the token from a response body or attach it to a request manually.
Token Lifecycle#
1. Client calls POST /auth/sign-in with credentials
2. Server validates credentials
3. Server sets X-Auth: <accessToken> on the response
4. Interceptor reads X-Auth → stores token via ZonaiStorage
5. All subsequent requests: Interceptor injects Authorization: Bearer <token>
6. When POST /auth/refresh is called:
Server revokes the old token
Server sets X-Auth: <newAccessToken> on the response
Interceptor replaces the stored token automatically
7. On logout, the token is revoked server-side; clear local storage manually
if needed (e.g., ZonaiStorage.none() or clearing the storage directory)
See Session Management for token expiry and refresh details on the server side.
Setting a Token Manually#
If you obtain a token outside of the client — for example from a native auth SDK or a server-side session — you can seed it directly into the server's storage before making requests:
import 'package:zonai_client/server.dart';
import 'package:zonai_client/zonai_client.dart';
final server = Server(
storage: ZonaiStorage(directory: '/var/lib/myapp'),
);
await server.storage.save('token', '<your-access-token>');
final client = ZonaiClient.server(server: server);
Unauthenticated Requests#
If no token is stored and a request reaches the interceptor without an
Authorization header already present, the interceptor skips token injection
and sends the request without authentication. This allows the same client
instance to call both public endpoints (send OTP, health check) and authenticated
endpoints after sign-in.
To disable token storage entirely (e.g. for tests), use ZonaiStorage.none():
final server = Server(storage: ZonaiStorage.none());
final client = ZonaiClient.server(server: server);
await client.health(); // no Authorization header added
