Multi-Account
Authyra treats multiple independent sessions as a first-class concept: sign in a personal account and a work account, keep both persisted, and switch between them without re-authenticating.
Signing in a second account
Every signIn() call activates the account it just authenticated. Sign in as many as you like, one at a time:
// Personal Gmail (now active)
await Authyra.instance.signIn('google');
// Work account via your own backend (now active)
await Authyra.instance.signIn('email', params: CredentialsSignInParams(
email: 'alice@work.com',
password: workPassword,
));
// Both are registered; the work account is currently active.
Listing accounts
final users = await Authyra.instance.accounts.getAll(); // List<AuthUser>, most recent first
final sessions = await Authyra.instance.accounts.getAllSessions(); // with tokens/expiry
final active = Authyra.instance.accounts.active; // currently active AuthUser?
ListView(
children: [
for (final user in await accounts.getAll())
ListTile(
title: Text(user.name ?? user.email ?? user.id),
selected: user.id == accounts.active?.id,
onTap: () => accounts.switchTo(user.id),
),
],
)
Switching
try {
await Authyra.instance.accounts.switchTo(userId);
} on AccountNotFoundException {
showError('Account not found');
} on TokenExpiredException {
showError('That session expired, please sign in again');
}
switchTo marks the target session as used and emits AccountSwitchEvent plus an updated AuthState.authenticated(user).
Removing accounts
// Sign out one account, keep the rest active
await Authyra.instance.accounts.signOut(userId);
// Sign out everything
await Authyra.instance.accounts.signOutAll();
If you sign out the active account and others remain, Authyra automatically activates the most recently used one, no manual re-selection needed.
Cleaning up expired sessions
final removed = await Authyra.instance.accounts.cleanExpired();
Runs automatically during initialize(). Call it again on app-resume if you want stale accounts pruned from the switcher UI without waiting for the next launch.
Gating switches and removals
class WorkspacePolicy extends AuthCallbacks {
@override
Future<CallbackResult> onBeforeAccountSwitch(AuthUser fromUser, String toUserId) async {
if (!await currentWorkspaceAllows(toUserId)) {
return const CallbackResult.deny('Not permitted in this workspace');
}
return const CallbackResult.allow();
}
@override
Future<CallbackResult> onBeforeAccountRemove(AuthUser user) async {
if (await isLastAdminAccount(user)) {
return const CallbackResult.deny('Cannot remove the last admin account');
}
return const CallbackResult.allow();
}
}
Linking is not this {#linking-is-not-this}
AuthSession.linkedAccounts exists as a data structure for account-linking (one identity, several providers), but no built-in provider populates it with more than one entry: GoogleProvider, GitHubProvider, etc. each derive AuthUser.id from that provider's own subject claim. Signing in via Google and then via GitHub today registers as two separate accounts in SessionRegistry, switchable via AccountManager like any other pair, not one linked identity with two ways to authenticate. There's no built-in "connect another provider to my current account" flow yet.