Skip to content

Flutter Integration

This guide covers everything you need to integrate AuthStack into your Flutter application using the official SDK.

Terminal window
flutter pub add voo_authstack_client

Or add to your pubspec.yaml:

dependencies:
voo_authstack_client: ^0.1.6
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();
// Register
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}');
}
// Login
try {
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}');
}

AuthStack handles OAuth app configuration centrally. You don’t need your own OAuth credentials:

// Get the OAuth authorization URL
final authUrl = await authService.getProviderAuthUrl(
provider: OAuthProvider.github,
redirectUri: 'https://yourapp.com/oauth/callback',
);
// Redirect user to authUrl.authorizationUrl
// After callback, link the provider
final linked = await authService.linkProviderWithCode(
provider: OAuthProvider.github,
code: codeFromCallback,
redirectUri: 'https://yourapp.com/oauth/callback',
);

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',
);
// Microsoft
await authService.loginWithOAuthCode(
provider: OAuthProvider.microsoft,
code: authorizationCode,
redirectUri: redirectUri,
);
// Apple
await authService.loginWithOAuthToken(
provider: OAuthProvider.apple,
token: identityToken,
);
// Discord
await authService.loginWithOAuthCode(
provider: OAuthProvider.discord,
code: authorizationCode,
redirectUri: redirectUri,
);

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().

ScenarioMethod
User logs in with Google/GitHub via your apploginWithOAuthCode()
Your backend exchanges code from AuthStack OAuth flowexchangeCodeViaBackend()
Frontend receives tokens from your backendexchangeCodeViaBackend()

When your backend handles the AuthStack OAuth code exchange:

// After user is redirected back from AuthStack with authorization code
final 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 automatically

This 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

After linking a provider, get their access token to make API calls:

// Get GitHub credentials
final credentials = await authService.getProviderCredentials(
OAuthProvider.github,
);
// Check if token is expired
if (credentials.isExpired) {
// Re-authenticate with GitHub
}
// Use credentials.accessToken with provider APIs
final response = await http.get(
Uri.parse('https://api.github.com/user/repos'),
headers: {'Authorization': 'Bearer ${credentials.accessToken}'},
);
PropertyTypeDescription
providerTypeStringProvider name (e.g., “github”)
accessTokenStringOAuth access token
refreshTokenString?Refresh token (if available)
expiresAtDateTime?Token expiration time
scopesString?Granted OAuth scopes
isExpiredboolWhether token has expired

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 with token
final linked = await authService.linkProviderWithToken(
provider: OAuthProvider.github,
token: githubAccessToken,
);
// Link with authorization code
final linked = await authService.linkProviderWithCode(
provider: OAuthProvider.github,
code: authorizationCode,
redirectUri: 'https://yourapp.com/callback',
);
// Unlink a provider
await authService.unlinkProvider(OAuthProvider.github);
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}');
}
});
if (authService.isAuthenticated) {
final user = authService.currentUser;
final tokens = authService.currentTokens;
}

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(),
);

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 needed
final response = await dio.get('/api/protected-resource');
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}');
}
}
MethodDescription
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
PropertyTypeDescription
statusAuthStatusCurrent auth status
currentUserUserInfo?Current user info
currentTokensAuthTokens?Current tokens
isAuthenticatedboolWhether user is authenticated
statusStreamStream<AuthStatus>Auth status changes
userStreamStream<UserInfo?>User changes
authenticatedDioDioDio instance with auth headers
VooAuthstackConfig(
// Required
baseUrl: 'https://api.authstack.voostack.com',
// Optional
connectTimeout: Duration(seconds: 30),
receiveTimeout: Duration(seconds: 30),
autoRefreshBuffer: Duration(minutes: 5),
)