API Reference

AuthyraClient

Complete API reference for AuthyraClient — the stateless orchestration layer.

Overview

AuthyraClient is the stateless core of Authyra. It orchestrates authentication providers and storage without holding any global state — making it 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,
  AuthConfig                  config = const AuthConfig(),
})
ParameterTypeDescription
providersList<AuthProvider>One or more auth strategies. Each must have a unique id.
storageAuthStoragePersistence backend. Use InMemoryAuthStorage for tests.
configAuthConfigOptional tuning: token lifetime, refresh threshold, max accounts.

The constructor validates provider IDs synchronously and throws ArgumentError 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 SessionRegistry from storage.
  • Restores the active session if one exists.
  • Emits an initial AuthState on authStateStream.

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, {
  Map<String, dynamic>? params,
})
ParameterTypeDescription
providerIdStringThe id of the registered provider to use.
paramsMap<String, dynamic>?Provider-specific input (credentials, OAuth code, etc.).

Returns AuthUser — the authenticated user's profile.

Throws:

  • ProviderNotFoundException — no provider with that id is registered.
  • AuthenticationFailedException — the provider returned null (wrong credentials) or an infrastructure error occurred.
final user = await client.signIn('email', params: {
  'email':    'alice@example.com',
  'password': 'secret',
});

signOut()

Signs out the currently active account.

Future<void> signOut()

Steps:

  1. Retrieves the active session.
  2. If the provider has supportsSignOut: true, calls provider.signOut(userId).
  3. Removes the session from SessionManager.
  4. Emits AuthState.unauthenticated() on authStateStream.
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 ArgumentError 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.switchTo(id);    // activate a different session
await client.accounts.signOut(id);     // sign out one account
await client.accounts.signOutAll();    // clear all sessions

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
  int refreshBeforeExpiry = 300,  // seconds before expiry to trigger refresh
  int maxAccounts        = 5,     // maximum concurrent signed-in accounts
  bool autoRefresh       = true,  // refresh token automatically when expired
})
final client = AuthyraClient(
  providers: [...],
  storage:   storage,
  config: const AuthConfig(
    tokenLifetime:       7200,  // 2 hours
    refreshBeforeExpiry: 600,   // refresh 10 minutes early
    maxAccounts:         3,
  ),
);

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'] as String);
            }
            return null;
          },
        ),
      ],
      storage: InMemoryAuthStorage(),
    );
    await client.initialize();
  });

  test('returns user on valid credentials', () async {
    final user = await client.signIn('email', params: {
      'email':    'alice@example.com',
      'password': 'correct',
    });
    expect(user.email, 'alice@example.com');
  });

  test('throws on invalid credentials', () async {
    expect(
      () => client.signIn('email', params: {'email': 'a@b.com', 'password': 'wrong'}),
      throwsA(isA<AuthenticationFailedException>()),
    );
  });

  test('isAuthenticated after sign in', () async {
    await client.signIn('email', params: {'email': 'a@b.com', 'password': 'correct'});
    final session = await client.getSession();
    expect(session, isNotNull);
  });
}

See Also

Copyright © 2026