Flutter Integration
This guide covers everything you need to integrate AuthStack into your Flutter application using the official SDK.
Installation
Section titled “Installation”flutter pub add voo_authstack_clientOr add to your pubspec.yaml:
dependencies: voo_authstack_client: ^0.1.6Quick Start
Section titled “Quick Start”Initialize the Service
Section titled “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();Email/Password Authentication
Section titled “Email/Password Authentication”// Registertry { 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}');}
// Logintry { final result = await authService.loginWithEmail( email: 'user@example.com', password: 'securePassword123', ); print('Logged in as ${result.user.email}');} on VooAuthstackException catch (e) { print('Login failed: ${e.message}');}OAuth Providers
Section titled “OAuth Providers”Centralized OAuth Flow (Recommended)
Section titled “Centralized OAuth Flow (Recommended)”AuthStack handles OAuth app configuration centrally. You don’t need your own OAuth credentials:
// Get the OAuth authorization URLfinal authUrl = await authService.getProviderAuthUrl( provider: OAuthProvider.github, redirectUri: 'https://yourapp.com/oauth/callback',);
// Redirect user to authUrl.authorizationUrl// After callback, link the providerfinal linked = await authService.linkProviderWithCode( provider: OAuthProvider.github, code: codeFromCallback, redirectUri: 'https://yourapp.com/oauth/callback',);With Your Own OAuth Credentials
Section titled “With Your Own OAuth Credentials”If you have your own OAuth app configured:
// Google (with ID token)final result = await authService.loginWithOAuthToken( provider: OAuthProvider.google, token: googleIdToken,);
// GitHub (with authorization code)final result = await authService.loginWithOAuthCode( provider: OAuthProvider.github, code: authorizationCode, redirectUri: 'https://yourapp.com/callback',);
// Microsoftawait authService.loginWithOAuthCode( provider: OAuthProvider.microsoft, code: authorizationCode, redirectUri: redirectUri,);
// Appleawait authService.loginWithOAuthToken( provider: OAuthProvider.apple, token: identityToken,);
// Discordawait authService.loginWithOAuthCode( provider: OAuthProvider.discord, code: authorizationCode, redirectUri: redirectUri,);Using AuthStack as Your Identity Provider
Section titled “Using AuthStack as Your Identity Provider”If your application uses AuthStack as its identity provider (users log in through AuthStack’s OAuth flow and your backend exchanges the code), use exchangeCodeViaBackend().
When to Use Each Method
Section titled “When to Use Each Method”| Scenario | Method |
|---|---|
| User logs in with Google/GitHub via your app | loginWithOAuthCode() |
| Your backend exchanges code from AuthStack OAuth flow | exchangeCodeViaBackend() |
| Frontend receives tokens from your backend | exchangeCodeViaBackend() |
Backend Token Exchange Flow
Section titled “Backend Token Exchange Flow”When your backend handles the AuthStack OAuth code exchange:
// After user is redirected back from AuthStack with authorization codefinal tokens = await authService.exchangeCodeViaBackend( backendUrl: 'https://api.myapp.com/auth/oauth/authstack/callback', code: authorizationCode, redirectUri: 'https://myapp.com/callback',);
// Your backend:// 1. Receives the code// 2. Exchanges it with AuthStack using ExchangeCodeAsync()// 3. Creates/looks up local user// 4. Returns your own JWT tokens// 5. Flutter SDK stores the tokens automaticallyThis is useful when:
- Your backend issues its own JWT tokens
- You need to create local user records
- You want to map AuthStack users to your own user system
Provider Credentials
Section titled “Provider Credentials”After linking a provider, get their access token to make API calls:
// Get GitHub credentialsfinal credentials = await authService.getProviderCredentials( OAuthProvider.github,);
// Check if token is expiredif (credentials.isExpired) { // Re-authenticate with GitHub}
// Use credentials.accessToken with provider APIsfinal response = await http.get( Uri.parse('https://api.github.com/user/repos'), headers: {'Authorization': 'Bearer ${credentials.accessToken}'},);ProviderCredentials Properties
Section titled “ProviderCredentials Properties”| Property | Type | Description |
|---|---|---|
providerType | String | Provider name (e.g., “github”) |
accessToken | String | OAuth access token |
refreshToken | String? | Refresh token (if available) |
expiresAt | DateTime? | Token expiration time |
scopes | String? | Granted OAuth scopes |
isExpired | bool | Whether token has expired |
Provider Linking
Section titled “Provider Linking”Link multiple OAuth providers to a single account:
// Get linked providersfinal providers = await authService.getLinkedProviders();for (final provider in providers) { print('${provider.providerType}: ${provider.email}');}
// Link a new provider with tokenfinal linked = await authService.linkProviderWithToken( provider: OAuthProvider.github, token: githubAccessToken,);
// Link with authorization codefinal linked = await authService.linkProviderWithCode( provider: OAuthProvider.github, code: authorizationCode, redirectUri: 'https://yourapp.com/callback',);
// Unlink a providerawait authService.unlinkProvider(OAuthProvider.github);Auth State Management
Section titled “Auth State Management”Listen to Status Changes
Section titled “Listen to Status 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; }});Listen to User Changes
Section titled “Listen to User Changes”authService.userStream.listen((user) { if (user != null) { print('Current user: ${user.fullName}'); }});Check Authentication Status
Section titled “Check Authentication Status”if (authService.isAuthenticated) { final user = authService.currentUser; final tokens = authService.currentTokens;}Token Management
Section titled “Token Management”Custom Token Storage
Section titled “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 storagefinal authService = VooAuthstackService( config: VooAuthstackConfig(baseUrl: 'https://api.authstack.voostack.com'), tokenStorage: SecureTokenStorage(),);Auto-Refresh Interceptor
Section titled “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),));
// All requests will now automatically include auth headers// and refresh tokens when neededfinal response = await dio.get('/api/protected-resource');Error Handling
Section titled “Error Handling”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 'network-error': // Network connectivity issue break; case 'email-in-use': // Email already registered break; case 'provider-already-linked': // OAuth provider already linked break; default: print('Error: ${e.message}'); }}API Reference
Section titled “API Reference”VooAuthstackService Methods
Section titled “VooAuthstackService Methods”| 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 (social login) |
loginWithOAuthCode() | Login with OAuth authorization code (social login) |
exchangeCodeViaBackend() | Exchange code via custom backend when AuthStack is your IdP |
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
Section titled “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 |
Configuration
Section titled “Configuration”VooAuthstackConfig( // Required baseUrl: 'https://api.authstack.voostack.com',
// Optional connectTimeout: Duration(seconds: 30), receiveTimeout: Duration(seconds: 30), autoRefreshBuffer: Duration(minutes: 5),)