Voo AuthStack Client
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 |
|---|---|
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 providerrefreshToken- Refresh token (if available)expiresAt- Token expiration timescopes- Granted OAuth scopesisExpired- 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?
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.