Voo AuthStack Client

Version Flutter License

A comprehensive Flutter SDK for Voo AuthStack - a centralized authentication platform. Supports email/password authentication, OAuth providers, automatic token refresh, and provider linking.

Features

  • Email/Password Authentication - Register and login with email and password
  • OAuth Providers - Login with Google, GitHub, Microsoft, Apple, Discord
  • Centralized OAuth - AuthStack handles OAuth app configuration, no OAuth credentials needed
  • Provider Credentials - Get provider access tokens to call GitHub API, Google API, etc.
  • Token Management - Automatic token storage and refresh
  • Provider Linking - Link/unlink multiple OAuth providers to a single account
  • Dio Interceptor - Automatic token refresh with request queuing
  • Type Safe - Fully typed with strong type constraints
  • Clean Architecture - Following best practices with VooCore integration

Installation

Add to your pubspec.yaml:

dependencies:
  voo_authstack_client: ^0.1.3

Or for local development in the VooFlutter monorepo:

dependencies:
  voo_authstack_client:
    path: packages/dev/voo_authstack_client

Quick Start

1. Initialize the Service

import 'package:voo_authstack_client/voo_authstack_client.dart';

final authService = VooAuthstackService(
  config: VooAuthstackConfig(
    baseUrl: 'https://api.authstack.voostack.com',
  ),
);

// Initialize (checks for stored tokens)
await authService.initialize();

2. Register a New User

try {
  final result = await authService.register(
    email: 'user@example.com',
    password: 'securePassword123',
    firstName: 'John',
    lastName: 'Doe',
  );

  print('Welcome, ${result.user.fullName}!');
} on VooAuthstackException catch (e) {
  print('Registration failed: ${e.message}');
}

3. Login with Email/Password

try {
  final result = await authService.loginWithEmail(
    email: 'user@example.com',
    password: 'securePassword123',
  );

  print('Logged in as ${result.user.email}');
} on VooAuthstackException catch (e) {
  if (e.code == 'invalid-credentials') {
    print('Invalid email or password');
  }
}

4. Login with OAuth Provider

// With access token from OAuth provider
final result = await authService.loginWithOAuthToken(
  provider: OAuthProvider.google,
  token: googleAccessToken,
);

// With authorization code
final result = await authService.loginWithOAuthCode(
  provider: OAuthProvider.github,
  code: authorizationCode,
  redirectUri: 'https://yourapp.com/callback',
);

5. Listen to Auth State Changes

authService.statusStream.listen((status) {
  switch (status) {
    case AuthStatus.authenticated:
      print('User is logged in');
      break;
    case AuthStatus.unauthenticated:
      print('User is logged out');
      break;
    case AuthStatus.authenticating:
      print('Login in progress...');
      break;
    case AuthStatus.refreshing:
      print('Refreshing token...');
      break;
    case AuthStatus.error:
      print('Authentication error');
      break;
  }
});

authService.userStream.listen((user) {
  if (user != null) {
    print('Current user: ${user.fullName}');
  }
});

OAuth Providers

Supported OAuth providers:

Provider Enum Value
Google OAuthProvider.google
GitHub OAuthProvider.github
Microsoft OAuthProvider.microsoft
Apple OAuthProvider.apple
Discord OAuthProvider.discord

Provider Linking

Link multiple OAuth providers to a single account:

// Get linked providers
final providers = await authService.getLinkedProviders();
for (final provider in providers) {
  print('${provider.providerType}: ${provider.email}');
}

// Link a new provider
final linked = await authService.linkProviderWithToken(
  provider: OAuthProvider.github,
  token: githubAccessToken,
);
print('Linked ${linked.providerType}');

// Unlink a provider
await authService.unlinkProvider(OAuthProvider.github);

Centralized OAuth Flow

AuthStack handles OAuth app configuration centrally, so you don't need your own OAuth credentials. Use getProviderAuthUrl() to start an OAuth flow:

// Get the OAuth authorization URL for GitHub
final authUrl = await authService.getProviderAuthUrl(
  provider: OAuthProvider.github,
  redirectUri: 'https://yourapp.com/oauth/callback',
);

// Redirect user to authUrl.authorizationUrl
// After authorization, use the code to link the provider
final code = getCodeFromCallback(); // Your callback handling
final linked = await authService.linkProviderWithCode(
  provider: OAuthProvider.github,
  code: code,
  redirectUri: 'https://yourapp.com/oauth/callback',
);

Provider Credentials

After linking an OAuth provider, get the provider's access token to make API calls:

// Get GitHub credentials to interact with GitHub API
final credentials = await authService.getProviderCredentials(
  OAuthProvider.github,
);

// Check if token is expired
if (credentials.isExpired) {
  // Re-authenticate with GitHub
}

// Use credentials.accessToken with GitHub API
final github = GitHub(auth: Authentication.withToken(credentials.accessToken));
final repos = await github.repositories.listRepositories().toList();

// Or use with HttpClient
final response = await http.get(
  Uri.parse('https://api.github.com/user/repos'),
  headers: {'Authorization': 'Bearer ${credentials.accessToken}'},
);

The ProviderCredentials model includes:

  • accessToken - OAuth access token for the provider
  • refreshToken - Refresh token (if available)
  • expiresAt - Token expiration time
  • scopes - Granted OAuth scopes
  • isExpired - Whether the token has expired

Token Management

Custom Token Storage

Implement TokenStorage for persistent storage:

class SecureTokenStorage implements TokenStorage {
  final FlutterSecureStorage _storage = FlutterSecureStorage();

  @override
  Future<void> saveTokens(AuthTokens tokens) async {
    await _storage.write(key: 'auth_tokens', value: jsonEncode(tokens.toJson()));
  }

  @override
  Future<AuthTokens?> getTokens() async {
    final data = await _storage.read(key: 'auth_tokens');
    if (data == null) return null;
    return AuthTokens.fromJson(jsonDecode(data));
  }

  @override
  Future<void> deleteTokens() async {
    await _storage.delete(key: 'auth_tokens');
  }

  @override
  Future<bool> hasTokens() async {
    return await _storage.containsKey(key: 'auth_tokens');
  }
}

// Use custom storage
final authService = VooAuthstackService(
  config: VooAuthstackConfig(baseUrl: 'https://api.authstack.voostack.com'),
  tokenStorage: SecureTokenStorage(),
);

Auto-Refresh Interceptor

Add the interceptor to your Dio instance for automatic token refresh:

final dio = Dio();
dio.interceptors.add(AuthInterceptor(
  authService: authService,
  refreshBuffer: Duration(minutes: 5), // Refresh 5 min before expiry
));

// All requests will now automatically include auth headers
// and refresh tokens when needed
final response = await dio.get('/api/protected-resource');

Configuration

VooAuthstackConfig(
  // Required
  baseUrl: 'https://api.authstack.voostack.com',

  // Optional
  connectTimeout: Duration(seconds: 30),
  receiveTimeout: Duration(seconds: 30),
  autoRefreshBuffer: Duration(minutes: 5),
)

Error Handling

All auth errors throw VooAuthstackException:

try {
  await authService.loginWithEmail(email: email, password: password);
} on VooAuthstackException catch (e) {
  switch (e.code) {
    case 'invalid-credentials':
      // Wrong email or password
      break;
    case 'unauthenticated':
      // User not logged in
      break;
    case 'token-expired':
      // Token has expired
      break;
    case 'refresh-failed':
      // Failed to refresh token
      break;
    case 'network-error':
      // Network connectivity issue
      break;
    case 'email-in-use':
      // Email already registered
      break;
    case 'provider-already-linked':
      // OAuth provider already linked
      break;
    case 'provider-not-linked':
      // OAuth provider not linked
      break;
    default:
      print('Error: ${e.message}');
  }
}

API Reference

VooAuthstackService

Method Description
initialize() Check for stored tokens and restore session
register() Register new user with email/password
loginWithEmail() Login with email and password
loginWithOAuthToken() Login with OAuth provider token
loginWithOAuthCode() Login with OAuth authorization code
refreshToken() Manually refresh the access token
logout() Logout and clear tokens
getCurrentUser() Fetch current user info
getLinkedProviders() Get all linked OAuth providers
linkProviderWithToken() Link OAuth provider with token
linkProviderWithCode() Link OAuth provider with code
unlinkProvider() Unlink an OAuth provider
getProviderAuthUrl() Get OAuth authorization URL for a provider
getProviderCredentials() Get access token for a linked provider
dispose() Clean up resources

Properties

Property Type Description
status AuthStatus Current auth status
currentUser UserInfo? Current user info
currentTokens AuthTokens? Current tokens
isAuthenticated bool Whether user is authenticated
statusStream Stream<AuthStatus> Auth status changes
userStream Stream<UserInfo?> User changes
authenticatedDio Dio Dio instance with auth headers

Example

See the example folder for a complete sample application demonstrating:

  • Email/password registration and login
  • OAuth provider authentication
  • Auth state management
  • Provider linking/unlinking
  • Token refresh

Run the example:

cd example
flutter run -d chrome

Architecture

lib/
├── voo_authstack_client.dart    # Main export
└── src/
    ├── enums/
    │   ├── auth_status.dart        # Authentication states
    │   └── oauth_provider.dart     # OAuth provider enum
    ├── exceptions/
    │   └── voo_authstack_exception.dart
    ├── interceptors/
    │   └── auth_interceptor.dart   # Auto-refresh interceptor
    ├── models/
    │   ├── auth_result.dart        # Login/register result
    │   ├── auth_tokens.dart        # JWT tokens
    │   ├── linked_provider.dart    # Linked OAuth provider
    │   ├── provider_auth_url.dart  # OAuth authorization URL
    │   ├── provider_credentials.dart # Provider access token
    │   └── user_info.dart          # User information
    └── services/
        ├── auth_service.dart       # Main auth service
        └── token_storage.dart      # Token storage interface

Learn More

License

MIT License - see LICENSE for details.


Built by VooStack

Need help with Flutter development or authentication solutions?

Contact Us

VooStack builds enterprise Flutter applications and developer tools. We're here to help with your next project.

Libraries

voo_authstack_client
Flutter SDK for Voo AuthStack - a centralized authentication platform.