Installation & Setup
Install neuctra_authix_dart_package, create one shared NeuctraAuthix instance, and teach it how to keep a session across app restarts.
1. Install
1dart pub add neuctra_authix_dart_packageThe package needs Dart ^3.0.0 and pulls in a single dependency, package:http. It runs on every Dart platform: Flutter mobile, web, desktop, and plain Dart on the server or CLI.
2. Import
One import exposes the client, config, every parameter and response class, Page, SessionStore and the whole error hierarchy.
import 'package:neuctra_authix_dart_package/neuctra_authix_dart_package.dart';3. Configure
| Param | Type | Required | Description |
|---|---|---|---|
| appId | String | Yes | The app every route is scoped to. Injected into request bodies and into app-scoped route paths. |
| apiKey | String | Yes | ak_live_… — full account authority. Keep it on a trusted server; never ship it in an app or web build. |
| baseUrl | String | No | Defaults to https://server.authix.neuctra.com/api. A trailing slash is stripped for you. |
| appName | String? | No | Optional display label. Cosmetic. |
| timeout | Duration | No | Per-request timeout. Defaults to 30 seconds. |
1// server/lib/services/authix.dart
2import 'dart:io';
3import 'package:neuctra_authix_dart_package/neuctra_authix_dart_package.dart';
4
5/// API keys have full account authority. Keep this client on a trusted server.
6final authix = NeuctraAuthix(
7 NeuctraAuthixConfig(
8 appId: Platform.environment['AUTHIX_APP_ID']!,
9 apiKey: Platform.environment['AUTHIX_API_KEY']!,
10 appName: 'MyApp',
11 // baseUrl defaults to the production API — override it to point at a
12 // local server during development.
13 ),
14);Keep the key on a server
This key grants full account authority. Do not include it in a Flutter build, --dart-define, assets, or source. Have the app call a backend that stores the key privately.
4. Pass credentials at build time
Dart has no .env at runtime the way a web bundler does. Use --dart-define so keys stay out of source control.
1# Run this in a trusted Dart server environment.
2AUTHIX_API_KEY=ak_live_xxxxxxxx_xxxxxxxxxxxxxxxx \
3AUTHIX_APP_ID=your_app_id_here dart run bin/server.dart5. Constructor options
| Option | Type | Purpose |
|---|---|---|
| client | http.Client? | Supply your own client for retries, logging, proxies, or BrowserClient on web. The SDK only closes clients it created. |
| session | SessionStore? | Share one cookie jar across several client instances. |
| timeout | Duration | Per-request timeout. Defaults to 30 seconds; a timeout throws with status 0. |
1final authix = NeuctraAuthix(
2 config,
3 client: myHttpClient, // optional — bring your own http.Client
4 session: SessionStore(), // optional — share a jar between clients
5 timeout: const Duration(seconds: 15), // optional — defaults to 30s
6);
7
8// Release the underlying client when you are done with it.
9authix.close();6. Sessions that survive a restart
In a browser the user agent stores the HTTP-only authix_user_session cookie for you. Dart has no cookie jar, so the SDK ships SessionStore: it captures Set-Cookie from auth responses and replays it on every later call. It lives in memory, so persist it yourself.
1import 'package:flutter_secure_storage/flutter_secure_storage.dart';
2
3const _storage = FlutterSecureStorage();
4const _key = 'authix_session';
5
6/// Call once during app start-up, before the first API call.
7Future<void> restoreSession() async {
8 final saved = await _storage.read(key: _key);
9 if (saved != null) authix.session.setSessionCookie(saved);
10}
11
12/// Call after a successful login.
13Future<void> persistSession() async {
14 final cookie = authix.session.sessionCookie;
15 if (cookie != null) await _storage.write(key: _key, value: cookie);
16}
17
18/// Call after logout.
19Future<void> forgetSession() => _storage.delete(key: _key);Then restore it before your first API call:
1Future<void> main() async {
2 WidgetsFlutterBinding.ensureInitialized();
3
4 await restoreSession();
5
6 // checkUserSession never throws — a dead cookie simply returns false
7 // and is cleared from the store for you.
8 final session = await authix.checkUserSession();
9
10 runApp(MyApp(isLoggedIn: session.authenticated));
11}| Member | Type | Use |
|---|---|---|
| session.hasSession | bool | Is a session cookie currently held? |
| session.sessionCookie | String? | Raw authix_user_session value — this is what you persist. |
| session.setSessionCookie(v) | void | Seed a restored cookie on app start. |
| session.clearSession() | void | Drop the Neuctra Authix cookie locally, forcing a signed-out state. |
| session.clear() | void | Drop every stored cookie. |
7. Flutter Web
On web the browser owns cookies and JavaScript cannot read an HTTP-only one. Hand the SDK a BrowserClient with credentials enabled and skip the persistence step entirely — the browser handles it.
1import 'package:http/browser_client.dart';
2
3// On Flutter Web the browser owns the cookie jar, so let it send
4// credentials itself. SessionStore stays empty — that is expected.
5final authix = NeuctraAuthix(
6 config,
7 client: BrowserClient()..withCredentials = true,
8);CORS on web
Credentialed cross-origin requests require the API to send Access-Control-Allow-Credentials: true and an explicit origin — a wildcard * is rejected by the browser. Configure the API's CORS settings to allow your web origin.
8. Error handling
Every failure is an AuthixException, mapped to the most specific subclass the response allows — so you can catch the case you actually handle instead of inspecting status codes by hand. Catching AuthixException still covers everything.
| Type | status | When |
|---|---|---|
| ConfigurationError | — | Bad client setup or a missing required argument. Thrown before any request is sent. |
| NetworkError | — | Timeout, offline, DNS or TLS failure. No response was received. |
| ValidationError | 400 | The API rejected the request — including a filter on a field that is not searchable. |
| AuthenticationError | 401 | Missing, malformed, expired or revoked credential. NoUserSessionError is the subclass for 'nobody is signed in'. |
| InsufficientScopeError | 403 | An API key on an API key endpoint. Carries a .hint naming the fix. |
| PermissionDeniedError | 403 | Authenticated but not permitted — an unverified account, or a plan limit reached. |
| NotFoundError | 404 | The app, user, or record does not exist under this account. |
| VersionConflictError | 409 | The record changed since you read it. Carries currentVersion and expectedVersion; nothing was written. |
| RateLimitError | 429 | Rate limited, or the monthly quota is exhausted. Carries .resetDate. |
| ServerError | 5xx | The API failed to process the request. |
1try {
2 await authix.loginUser(
3 const LoginParams(email: '[email protected]', password: 'wrong'),
4 );
5} on ConfigurationError catch (err) {
6 // A required argument was empty — no request was sent.
7 print(err.message);
8} on NetworkError catch (err) {
9 // Timeout, offline, DNS or TLS failure.
10 print(err.message);
11} on AuthenticationError catch (err) {
12 // 401 — wrong password, or an expired or revoked credential.
13 print(err.message);
14} on AuthixApiError catch (err) {
15 // Anything else the API rejected.
16 print('${err.status} ${err.code}: ${err.message}');
17 print(err.payload); // the decoded response body
18}The two you will most often handle explicitly:
1// Optimistic concurrency: pass the version you last read.
2try {
3 await authix.updateUserData(
4 UpdateUserDataParams(
5 userId: userId,
6 dataId: record.id,
7 data: {'status': 'shipped'},
8 version: record.version,
9 ),
10 );
11} on VersionConflictError catch (err) {
12 // Another device wrote first. Nothing was overwritten.
13 print('now at version ${err.currentVersion}');
14}
15
16// Wrong key for the endpoint — nearly always a ak_ key on a server-only route.
17try {
18 await authix.getAllUsersFromApp(const GetAllAppUsersParams());
19} on InsufficientScopeError catch (err) {
20 print(err.hint); // explains which credential the endpoint needs
21}checkUserSession() never throws — any failure resolves to CheckSessionResponse(authenticated: false) and clears the stored cookie. logoutUser() clears the local session even when the server call fails, so a failed logout cannot leave a usable session on the device.
9. End-to-end flow
1// 1. Create the account — the session cookie is captured automatically
2final signup = await authix.signupUser(
3 const SignupParams(
4 name: 'John Doe',
5 email: '[email protected]',
6 password: 'API123',
7 username: 'johndoe',
8 ),
9);
10
11final userId = signup['user']?['id']?.toString() ?? '';
12
13// 2. Verify the email address
14await authix.requestEmailVerificationOTP(
15 userId: userId,
16 email: '[email protected]',
17);
18await authix.verifyEmail(email: '[email protected]', otp: '123456');
19
20// 3. Persist the session so it survives an app restart
21await persistSession();
22
23// 4. Store something for this user
24await authix.addUserData(
25 AddUserDataParams(
26 userId: userId,
27 dataCategory: 'notes',
28 data: {'title': 'My first note'},
29 ),
30);
31
32// 5. Log out
33await authix.logoutUser(onLoggedOut: forgetSession);Production notes
- Load the key from server environment configuration.
- Never include
AUTHIX_API_KEYin a Flutter build. Mobile binaries can be inspected and the key grants full account access. - Persist
session.sessionCookiein secure storage, notSharedPreferences. - Call
authix.close()in short-lived Dart processes such as CLIs and tests.
You are set up
- Auth & User Management — signup, login, sessions, profile.
- User Data Management — per-user records and search.
- App Data Management — shared app-level records.
Related