FAQ
General
What is Authyra?
Authyra is a pure authentication logic framework for Dart and Flutter. It handles authentication state, session management, and provider orchestration while remaining completely agnostic to your navigation and UI choices.
The core authyra package has no Flutter dependency and runs identically on mobile, web, desktop, backend (Shelf, Dart Frog), and CLI tools.
Why not use Firebase Auth or Supabase Auth?
You can — Authyra doesn't compete with BaaS platforms. The difference is:
| Aspect | Firebase / Supabase | Authyra |
|---|---|---|
| Backend | Managed service | Bring your own |
| Auth logic | In the SDK | In your app (you own it) |
| Navigation control | Opinionated | Fully decoupled |
| Multi-platform | Flutter + limited Dart | Any Dart platform |
Use Authyra when you have your own backend, want full control, or need to run the same auth logic on both a Flutter app and a Dart backend.
Is Authyra production-ready?
v0.1.0 is an MVP release. The API is stable for the core features but the test suite is still being expanded. We recommend:
- Using it in new projects and side projects now.
- Waiting for v1.0.0 (targeting ≥ 90% test coverage) for mission-critical production systems.
Does Authyra work offline?
Session state is cached in memory and persisted to AuthStorage. Your app can:
- Read
Authyra.instance.isAuthenticatedandcurrentUseroffline — no network call required. - React to cached state immediately on startup, before any network request.
Technical
Why two packages? (core vs Flutter)
authyra (pure Dart, v0.1.0):
- Zero Flutter dependency.
- Works on backend, CLI, scripts.
dart testruns without a Flutter device.- Smaller pub.dev bundle.
- Ships:
CredentialsProvider,AuthyraClient,AuthyraInstance,InMemoryStorage.
authyra_flutter (Flutter, v0.1.0):
- Re-exports the entire
authyracore — one import covers everything. GoogleProvider,GitHubOAuth2Provider,AppleProvider,OAuth2Provider,ProxyOAuthProvider.SecureAuthStorage—flutter_secure_storage-backed implementation.OAuth2CallbackHandler— deep-link router for OAuth redirects.
This separation keeps the authentication core clean and reusable while the Flutter layer remains purely additive.
Can I use Authyra without Flutter?
Yes. Just add authyra (not authyra_flutter):
dependencies:
authyra: ^0.1.0
Works in:
- Dart backend services (Shelf, Dart Frog)
- CLI tools
- Scripts
- Unit tests
Does Authyra support multi-account?
Yes — AccountManager ships in the core (authyra package):
final mgr = Authyra.instance.accounts;
// All signed-in accounts, sorted by last activity
final users = await mgr.getAll();
// Activate a different account
await mgr.switchTo(userId);
// Sign out one account, keep others active
await mgr.signOut(userId);
// Sign out every account
await mgr.signOutAll();
// Prune expired sessions
await mgr.cleanExpired();
The number of concurrent accounts is capped by AuthConfig.maxAccounts (default: 5).
Can I use my own state management?
Authyra is completely state-management-agnostic. The authStateChanges stream plugs into anything:
Riverpod:
final authStateProvider = StreamProvider<AuthState>((ref) {
return Authyra.instance.authStateChanges;
});
// In a widget:
final state = ref.watch(authStateProvider).value;
Bloc / Cubit:
class AuthCubit extends Cubit<AuthState> {
late final StreamSubscription<AuthState> _sub;
AuthCubit() : super(AuthState.unauthenticated()) {
_sub = Authyra.instance.authStateChanges.listen(emit);
}
@override
Future<void> close() {
_sub.cancel();
return super.close();
}
}
GoRouter:
GoRouter(
refreshListenable: StreamToListenable(Authyra.instance.authStateChanges),
redirect: (context, state) {
if (!Authyra.instance.isAuthenticated) return '/login';
return null;
},
);
How do I test code that uses Authyra?
Use AuthyraClient directly — no singleton, no Authyra.initialize() required:
import 'package:test/test.dart';
import 'package:authyra/authyra.dart';
void main() {
late AuthyraClient client;
setUp(() async {
client = AuthyraClient(
providers: [
CredentialsProvider(
id: 'email',
authorize: (creds) async {
if (creds?['password'] == 'secret') {
return AuthUser(id: '1', email: creds!['email'] as String);
}
return null;
},
),
],
storage: InMemoryStorage(),
);
await client.initialize();
});
test('sign in succeeds with correct password', () async {
final user = await client.signIn('email', params: {
'email': 'alice@example.com',
'password': 'secret',
});
expect(user.email, equals('alice@example.com'));
});
test('sign in fails with wrong password', () async {
expect(
() => client.signIn('email', params: {
'email': 'alice@example.com',
'password': 'wrong',
}),
throwsA(isA<AuthenticationFailedException>()),
);
});
}
If your test code uses Authyra.instance, call dispose() in tearDown to reset the singleton between tests:
tearDown(() async {
if (Authyra.isInitialized) {
await Authyra.instance.dispose();
}
});
What storage backend should I use?
| Runtime | Recommended |
|---|---|
| Tests / dev | InMemoryStorage (bundled with authyra) |
| Flutter mobile | SecureAuthStorage from authyra_flutter (Keychain / Keystore) |
| Flutter web | SecureAuthStorage from authyra_flutter (Web Crypto) |
| Dart CLI | Encrypted file or OS keyring |
| Backend / Shelf | Redis, encrypted DB column, or a secrets manager |
Never store tokens in SharedPreferences or browser localStorage — they are not encrypted.
How does token refresh work?
When AuthyraClient.refreshSession() is called (manually or triggered by an API 401):
- The active
AuthSessionis retrieved. - If
session.shouldRefreshistrue(token expires withinAuthConfig.refreshBeforeExpiry), the client callsprovider.refreshToken(session.refreshToken). - The provider returns
AuthTokenResultwith the newaccessToken(and optionally a newrefreshToken). - The session is updated in
SessionManagerand re-persisted to storage. authStateChangesemits the refreshedAuthState.authenticated.
If the refresh token is itself expired or invalid, refreshToken returns null, the session is cleared, and AuthState.unauthenticated() is emitted.