Storage
AuthStorage is the single persistence interface used by Authyra's SessionManager. The core package ships no concrete implementation by design — you supply a backend suited to your runtime.
Interface
abstract class AuthStorage {
Future<void> initialize();
Future<String?> read(String key);
Future<void> write(String key, String value);
Future<bool> delete(String key);
Future<void> clear();
Future<bool> containsKey(String key);
Future<List<String>> getKeysWithPrefix(String prefix);
}
| Method | Description |
|---|---|
initialize() | One-time setup — key derivation, DB connection, schema migration. Must be idempotent. |
read(key) | Return the stored string or null if absent |
write(key, value) | Persist value under key, overwriting any existing entry. Must survive process restarts. |
delete(key) | Remove key. Return true if it existed, false if absent. Never throws for missing keys. |
clear() | Delete all entries — irreversible. Call only during full sign-out-all or factory reset. |
containsKey(key) | true if key is present |
getKeysWithPrefix(prefix) | Enumerate all keys starting with prefix — used for multi-account session discovery |
Choosing a backend
| Runtime | Recommended |
|---|---|
| Flutter mobile | SecureAuthStorage from authyra_flutter (Keychain on iOS, Keystore on Android) |
| Flutter web | SecureAuthStorage from authyra_flutter (Web Crypto encrypted localStorage) |
| Tests / dev | InMemoryStorage from authyra (no persistence — resets between test cases) |
| Dart CLI | Encrypted file or OS keyring |
| Backend / Shelf | Redis, encrypted DB column, or a secrets manager |
SharedPreferences, plain localStorage, or unencrypted files. Always use an encrypted store in production.Flutter — SecureAuthStorage
SecureAuthStorage wraps flutter_secure_storage and is the ready-to-use implementation for Flutter apps. Import it from authyra_flutter:
import 'package:authyra_flutter/authyra_flutter.dart';
await Authyra.initialize(
client: AuthyraClient(
providers: [...],
storage: SecureAuthStorage(),
),
);
No configuration is needed. flutter_secure_storage selects the platform-native encrypted store automatically:
- iOS / macOS → Keychain
- Android → EncryptedSharedPreferences (Android Keystore)
- Web →
localStoragewith Web Crypto AES-GCM encryption
Tests — InMemoryStorage
InMemoryStorage stores everything in a Map<String, String> — fast, portable, and resets between test cases automatically:
import 'package:test/test.dart';
import 'package:authyra/authyra.dart';
void main() {
late AuthyraClient client;
setUp(() async {
client = AuthyraClient(
providers: [myProvider],
storage: InMemoryStorage(), // fresh instance per test
);
await client.initialize();
});
test('session is stored after sign-in', () async {
await client.signIn('email', params: {
'email': 'alice@example.com', 'password': 'secret',
});
final token = await client.getAccessToken();
expect(token, isNotNull);
});
}
Never use InMemoryStorage in production — sessions are lost on process restart.
Implementing a custom backend
Implement AuthStorage to connect any storage system. Below is a Redis example for a Dart backend:
import 'package:authyra/authyra.dart';
import 'package:resp_client/resp_client.dart';
class RedisAuthStorage implements AuthStorage {
final RespClient _client;
final String _namespace;
RedisAuthStorage(this._client, {String namespace = 'authyra'})
: _namespace = namespace;
String _k(String key) => '$_namespace:$key';
@override
Future<void> initialize() async {
// Verify the Redis connection is alive
await _client.command(['PING']);
}
@override
Future<String?> read(String key) async {
final result = await _client.command(['GET', _k(key)]);
return result.payload as String?;
}
@override
Future<void> write(String key, String value) async {
await _client.command(['SET', _k(key), value]);
}
@override
Future<bool> delete(String key) async {
final deleted = await _client.command(['DEL', _k(key)]);
return (deleted.payload as int) > 0;
}
@override
Future<void> clear() async {
final keys = await getKeysWithPrefix('');
if (keys.isNotEmpty) {
await _client.command(['DEL', ...keys.map(_k)]);
}
}
@override
Future<bool> containsKey(String key) async {
final result = await _client.command(['EXISTS', _k(key)]);
return (result.payload as int) > 0;
}
@override
Future<List<String>> getKeysWithPrefix(String prefix) async {
final result = await _client.command(['KEYS', '${_k(prefix)}*']);
final fullKeys = (result.payload as List).cast<String>();
// Strip the namespace prefix before returning
return fullKeys
.map((k) => k.substring('$_namespace:'.length))
.toList();
}
}
Pass it to AuthyraClient:
final client = AuthyraClient(
providers: [myProvider],
storage: RedisAuthStorage(redisClient),
);
await client.initialize();
Key structure
SessionManager stores all session data under a single registry key by default:
authyra:session_registry → { JSON-encoded SessionRegistry }
getKeysWithPrefix is used by multi-account flows to enumerate sessions. When implementing a custom backend, ensure getKeysWithPrefix('') (empty prefix) returns all keys managed by this storage instance so that clear() can remove them all.
Security checklist
- Storage backend uses OS-level encryption (Keychain, Keystore, Web Crypto, Redis at-rest encryption)
-
clear()is called on sign-out-all and account deletion flows -
initialize()is awaited before any other storage operation -
writeis durable — survives process restarts and power events -
writeis atomic — no partial writes that could corrupt session data
See also
- Sessions → — what gets stored in each session
- Custom Storage guide →
- Flutter Setup →