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 streams, authStateChanges and sessionStream broadcast every state change.
  • Convenience delegation, signIn, signOut, and refreshSession forward to the client.
  • Multi-account API, accounts 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:   InMemoryStorage(),
  );

  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, {
  AuthSignInParams? params,
})
ParameterTypeDescription
providerIdStringThe id of the provider to use.
paramsAuthSignInParams?Typed params for the provider: CredentialsSignInParams, OAuth2SignInParams, or your own subclass.

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

try {
  final user = await Authyra.instance.signIn('email', params: CredentialsSignInParams(
    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: InMemoryStorage(),
    ),
  );

  // 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: CredentialsSignInParams(
    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();
}
Copyright © 2026