AuthyraClient
Overview
AuthyraClient is the dependency-injected core of Authyra. It holds real internal state (the session registry, an event bus, its initialized flag) but no global state: nothing is static or ambient, so it's fully injectable and trivially testable.
import 'package:authyra/authyra.dart';
final client = AuthyraClient(
providers: [GoogleProvider(clientId: 'YOUR_CLIENT_ID')],
storage: InMemoryStorage(),
config: const AuthConfig(),
);
You rarely interact with AuthyraClient directly in application code, AuthyraInstance (via Authyra.instance) is the app-facing singleton. Use AuthyraClient directly when writing tests or building backend services.
Constructor
AuthyraClient({
required List<AuthProvider> providers,
required AuthStorage storage,
AuthCallbacks? callbacks,
AuthConfig config = const AuthConfig(),
List<AuthyraPlugin>? plugins,
})
| Parameter | Type | Description |
|---|---|---|
providers | List<AuthProvider> | One or more auth strategies. Each must have a unique id. |
storage | AuthStorage | Persistence backend. Use InMemoryStorage for tests. |
callbacks | AuthCallbacks? | Optional gate for sign-in/sign-out/switch/refresh/remove; see Callbacks. |
config | AuthConfig | Optional tuning: token lifetime, auto-refresh, refresh threshold. |
plugins | List<AuthyraPlugin>? | Optional observers (audit logging, analytics); can't block an action. |
The constructor validates provider IDs synchronously and throws ProviderAlreadyRegisteredException on duplicates.
Methods
initialize()
Prepares the client for use. Must be called once before any other method.
Future<void> initialize()
What it does:
- Calls
storage.initialize(). - Loads and deserialises the
SessionRegistryfrom storage. - Restores the active session if one exists.
- Emits an initial
AuthStateonauthStateStream.
Throws: StorageException if the storage backend cannot be initialised.
await client.initialize();
signIn()
Authenticates a user via the named provider.
Future<AuthUser> signIn(
String providerId, {
AuthSignInParams? params,
})
| Parameter | Type | Description |
|---|---|---|
providerId | String | The id of the registered provider to use. |
params | AuthSignInParams? | Typed, provider-specific input: CredentialsSignInParams, OAuth2SignInParams, or your own subclass. |
Returns AuthUser, the authenticated user's profile.
Throws:
ProviderNotFoundException, no provider with thatidis registered.AuthenticationFailedException, the provider returnednull(wrong credentials), a callback denied the sign-in, or an infrastructure error occurred.
final user = await client.signIn('email', params: CredentialsSignInParams(
email: 'alice@example.com',
password: 'secret',
));
signOut()
Signs out the currently active account.
Future<void> signOut()
Steps:
- Retrieves the active session.
- If the provider has
supportsSignOut: true, callsprovider.signOut(userId). - Removes the session from
SessionManager. - Emits
AuthState.unauthenticated()onauthStateStream.
await client.signOut();
refreshSession()
Silently refreshes the active session's access token.
Future<bool> refreshSession()
Returns true if the token was refreshed successfully, false if the refresh token is invalid or the provider does not support refresh.
Called automatically by AuthyraInstance, you rarely need to call this directly.
final refreshed = await client.refreshSession();
if (!refreshed) {
// Token is invalid, the session has been cleared.
}
getSession()
Returns the active AuthSession, or null if no user is signed in.
Future<AuthSession?> getSession()
final session = await client.getSession();
print(session?.accessToken);
getUser()
Returns the active AuthUser, or null if not authenticated.
Future<AuthUser?> getUser()
registerProvider()
Dynamically adds a provider after construction.
void registerProvider(AuthProvider provider)
Useful for providers that require runtime configuration (e.g., a tenant-specific OAuth client ID):
client.registerProvider(
OAuth2Provider(config: tenantConfig),
);
Throws InvalidProviderConfigException if provider.id is empty, or ProviderAlreadyRegisteredException if a provider with the same id is already registered.
Properties
authStateStream
Broadcast stream of AuthState changes. Deduplicated, identical consecutive states are not re-emitted.
Stream<AuthState> get authStateStream
client.authStateStream.listen((state) {
if (state.isAuthenticated) {
print('Signed in as ${state.user!.email}');
}
});
sessionStream
Broadcast stream of the raw AuthSession, or null on sign-out.
Stream<AuthSession?> get sessionStream
accounts
Lazy accessor for the AccountManager. Manages the multi-account session registry.
AccountManager get accounts
await client.accounts.getAll(); // List<AuthUser>
await client.accounts.getAllSessions(); // List<AuthSession>, with tokens/expiry
await client.accounts.switchTo(id); // activate a different session
await client.accounts.signOut(id); // sign out one account
await client.accounts.signOutAll(); // clear all sessions
await client.accounts.cleanExpired(); // prune expired sessions, returns count removed
await client.accounts.isValid(id); // has a registered, non-expired session?
await client.accounts.getTokenFor(id); // access token for a non-active account
See AccountManager API → for the full surface.
isInitialized
true after initialize() has completed successfully.
bool get isInitialized
AuthConfig
Fine-tune the client's session behaviour:
const AuthConfig({
int tokenLifetime = 3600, // seconds before access token expires
bool autoRefresh = true, // refresh token automatically when expiring
int refreshBeforeExpiry = 300, // seconds before expiry to trigger refresh
})
final client = AuthyraClient(
providers: [...],
storage: storage,
config: const AuthConfig(
tokenLifetime: 7200, // 2 hours
refreshBeforeExpiry: 600, // refresh 10 minutes early
),
);
There is no account-count limit in AuthConfig; SessionManager keeps every signed-in account until you explicitly sign it out.
Testing
Prefer AuthyraClient directly in unit tests, no singleton to reset between tests:
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 == 'correct') {
return AuthUser(id: '1', email: creds!.email);
}
return null;
},
),
],
storage: InMemoryStorage(),
);
await client.initialize();
});
test('returns user on valid credentials', () async {
final user = await client.signIn('email', params: CredentialsSignInParams(
email: 'alice@example.com',
password: 'correct',
));
expect(user.email, 'alice@example.com');
});
test('throws on invalid credentials', () async {
expect(
() => client.signIn('email', params: CredentialsSignInParams(email: 'a@b.com', password: 'wrong')),
throwsA(isA<AuthenticationFailedException>()),
);
});
test('isAuthenticated after sign in', () async {
await client.signIn('email', params: CredentialsSignInParams(email: 'a@b.com', password: 'correct'));
final session = await client.getSession();
expect(session, isNotNull);
});
}