API Reference

AuthyraInstance

Complete API reference for AuthyraInstance — the reactive singleton entry point.

Overview

AuthyraInstance is the singleton wrapper around AuthyraClient. It adds:

  • Synchronous state — read currentUser, currentState, and isAuthenticated without await.
  • Reactive streamsauthStateChanges and sessionStream broadcast every state change.
  • Convenience delegationsignIn, signOut, and refreshSession forward to the client.
  • Multi-account APIaccounts exposes AccountManager.

Access it via the Authyra typedef (a short alias for AuthyraInstance):

import 'package:authyra/authyra.dart';

// Initialize once at startup
await Authyra.initialize(client: client);

// Use anywhere
Authyra.instance.isAuthenticated; // bool — synchronous

Initialization

Authyra.initialize()

Static factory. Creates and initialises the singleton. Must be called once before Authyra.instance.

static Future<AuthyraInstance> initialize({
  required AuthyraClient client,
})
void main() async {
  final client = AuthyraClient(
    providers: [CredentialsProvider(id: 'email', authorize: myCallback)],
    storage:   InMemoryAuthStorage(),
  );

  await Authyra.initialize(client: client);
  // Authyra.instance is now available everywhere.
}

Calling initialize a second time returns the existing instance without re-initialising. To reset (e.g., in tests), call dispose() first.


isInitialized

true after initialize() has completed.

static bool get isInitialized

Synchronous State

These getters are safe to call anywhere — including build() methods — without await.

currentUser

AuthUser? get currentUser

The currently active user, or null if not authenticated.

final user = Authyra.instance.currentUser;
if (user != null) print('Hello, ${user.name}');

currentState

AuthState get currentState

The latest AuthState. Starts as AuthState.unauthenticated() before any sign-in.

final state = Authyra.instance.currentState;
print(state.type); // AuthStateType.unauthenticated

isAuthenticated

bool get isAuthenticated

Shorthand for currentState.isAuthenticated.

if (Authyra.instance.isAuthenticated) {
  // show home screen
}

Async Accessors

These reach into storage when the in-memory cache is stale.

getSession()

Future<AuthSession?> getSession()

Returns the full AuthSession for the active user, or null.

final session = await Authyra.instance.getSession();
print(session?.accessToken);
print(session?.expiresAt);

getUser()

Future<AuthUser?> getUser()
final user = await Authyra.instance.getUser();

getAccessToken()

Future<String?> getAccessToken()

Returns the raw access token string, or null when not authenticated.

final token = await Authyra.instance.getAccessToken();
myHttpClient.headers['Authorization'] = 'Bearer $token';

Reactive Streams

authStateChanges

Stream<AuthState> get authStateChanges

Broadcast stream of AuthState. Emits on every sign-in, sign-out, token refresh, and error.

Consecutive identical states are deduplicated (via Equatable) — you won't get spurious rebuilds when the state hasn't actually changed.

Authyra.instance.authStateChanges.listen((AuthState state) {
  switch (state.type) {
    case AuthStateType.authenticated:
      print('Signed in as ${state.user!.email}');
    case AuthStateType.unauthenticated:
      print('Signed out');
    case AuthStateType.error:
      print('Auth error: ${state.error}');
  }
});

GoRouter integration:

GoRouter(
  refreshListenable: StreamToListenable(Authyra.instance.authStateChanges),
  redirect: (context, state) {
    if (!Authyra.instance.isAuthenticated) return '/login';
    return null; // no redirect needed
  },
  routes: [ ... ],
);

Riverpod integration:

final authStateProvider = StreamProvider<AuthState>((ref) {
  return Authyra.instance.authStateChanges;
});

sessionStream

Stream<AuthSession?> get sessionStream

Broadcast stream of the raw AuthSession. Emits null on sign-out.

Authyra.instance.sessionStream.listen((session) {
  if (session?.isExpired ?? false) {
    print('Token expired — refresh needed');
  }
});

Authentication Actions

signIn()

Delegates to AuthyraClient.signIn().

Future<AuthUser> signIn(
  String providerId, {
  Map<String, dynamic>? params,
})
ParameterTypeDescription
providerIdStringThe id of the provider to use.
paramsMap<String, dynamic>?Credentials or OAuth params passed to the provider.

Returns AuthUser on success. Throws on failure — never returns null.

try {
  final user = await Authyra.instance.signIn('email', params: {
    'email':    'alice@example.com',
    'password': 's3cr3t',
  });
  print('Welcome, ${user.name}');
} on AuthenticationFailedException catch (e) {
  print('Invalid credentials: $e');
} on ProviderNotFoundException catch (e) {
  print('No such provider: $e');
}

signOut()

Signs out the currently active account.

Future<void> signOut()

Emits AuthState.unauthenticated() on authStateChanges after completion.

await Authyra.instance.signOut();
print(Authyra.instance.isAuthenticated); // false

refreshSession()

Silently refreshes the access token for the active session.

Future<bool> refreshSession()

Returns true on success, false if the refresh token is expired or the provider does not support refresh.

final ok = await Authyra.instance.refreshSession();
if (!ok) {
  // Force re-authentication
  await Authyra.instance.signOut();
}

Multi-Account

accounts

AccountManager get accounts

Exposes the AccountManager for multi-session operations.

final mgr = Authyra.instance.accounts;

// List all signed-in users (sorted by last activity)
final users = await mgr.getAll();     // List<AuthUser>

// Switch the active account
await mgr.switchTo('user_id_here');

// Sign out a specific account
await mgr.signOut('user_id_here');

// Sign out every account
await mgr.signOutAll();

// Remove expired sessions, returns count removed
final removed = await mgr.cleanExpired();

Lifecycle

dispose()

Tears down the singleton: cancels stream subscriptions, closes controllers, and resets the internal _instance reference.

Future<void> dispose()

After dispose(), Authyra.isInitialized returns false and Authyra.instance throws until initialize() is called again. Useful in tests to reset state between test cases.

tearDown(() async {
  if (Authyra.isInitialized) {
    await Authyra.instance.dispose();
  }
});

Complete Example

main.dart
import 'package:authyra/authyra.dart';

Future<void> main() async {
  // 1. Configure and initialize
  await Authyra.initialize(
    client: AuthyraClient(
      providers: [
        CredentialsProvider.withTokens(
          id: 'api',
          authorize: (creds) async {
            final res = await myApi.post('/auth/login', body: creds);
            if (res.statusCode != 200) return null;
            return AuthSignInResult(
              user:         AuthUser(id: res.data['id'], email: res.data['email']),
              accessToken:  res.data['accessToken'],
              refreshToken: res.data['refreshToken'],
              expiresAt:    DateTime.parse(res.data['expiresAt']),
            );
          },
        ),
      ],
      storage: InMemoryAuthStorage(),
    ),
  );

  // 2. Subscribe to state changes before signing in
  Authyra.instance.authStateChanges.listen((state) {
    print('[auth] ${state.type.name} — user: ${state.user?.email}');
  });

  // 3. Sign in
  final user = await Authyra.instance.signIn('api', params: {
    'email':    'alice@example.com',
    'password': 'secret',
  });
  print('Hello, ${user.name}!');

  // 4. Use synchronous state in rendering logic
  print('isAuthenticated: ${Authyra.instance.isAuthenticated}');

  // 5. Read the access token for HTTP calls
  final token = await Authyra.instance.getAccessToken();
  print('Bearer $token');

  // 6. Sign out
  await Authyra.instance.signOut();
  print('isAuthenticated: ${Authyra.instance.isAuthenticated}'); // false

  // 7. Dispose (optional — mainly for tests)
  await Authyra.instance.dispose();
}

See Also

Copyright © 2026