vestibule_auth 0.0.1-beta.1
vestibule_auth: ^0.0.1-beta.1 copied to clipboard
Multi-tenant OTP authentication client for Vestibule
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:vestibule_auth/client.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Vestibule Auth Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const AuthDemoPage(),
);
}
}
class AuthDemoPage extends StatefulWidget {
const AuthDemoPage({super.key});
@override
State<AuthDemoPage> createState() => _AuthDemoPageState();
}
class _AuthDemoPageState extends State<AuthDemoPage> {
final _client = VestibuleClient(
serverUrl: 'https://your-vestibule-server.com',
tenantId: 'your-tenant-id',
autoSaveTokens: true, // Automatically save tokens after authentication
);
final _emailController = TextEditingController();
final _phoneController = TextEditingController();
final _codeController = TextEditingController();
String? _verifyToken;
String? _jwt;
String _status = 'Ready';
bool _loading = false;
@override
void dispose() {
_emailController.dispose();
_phoneController.dispose();
_codeController.dispose();
super.dispose();
}
Future<void> _requestEmailOTP() async {
setState(() {
_loading = true;
_status = 'Requesting email OTP...';
});
try {
final result = await _client.requestOTP(
recipient: _emailController.text,
deliveryMethod: DeliveryMethod.DELIVERY_METHOD_EMAIL,
);
setState(() {
_status = 'Email OTP sent! ${result.message}';
_loading = false;
});
} catch (e) {
setState(() {
_status = 'Error: $e';
_loading = false;
});
}
}
Future<void> _requestSMSOTP() async {
setState(() {
_loading = true;
_status = 'Requesting SMS OTP...';
});
try {
final result = await _client.requestOTP(
recipient: _phoneController.text,
deliveryMethod: DeliveryMethod.DELIVERY_METHOD_SMS,
);
setState(() {
_status = 'SMS OTP sent! ${result.message}';
_loading = false;
});
} catch (e) {
setState(() {
_status = 'Error: $e';
_loading = false;
});
}
}
Future<void> _verifyEmailOTP() async {
setState(() {
_loading = true;
_status = 'Verifying email OTP...';
});
try {
final result = await _client.verifyOTP(
recipient: _emailController.text,
code: _codeController.text,
);
setState(() {
if (result.hasJwt() && result.hasRefreshToken()) {
_jwt = result.jwt;
_status = 'Authenticated! JWT received and saved.';
} else if (result.hasVerifyToken()) {
_verifyToken = result.verifyToken;
_status = 'First factor verified. Please verify phone.';
} else {
_status = 'Verification failed: ${result.message}';
}
_loading = false;
});
} catch (e) {
setState(() {
_status = 'Error: $e';
_loading = false;
});
}
}
Future<void> _verifyPhoneOTP() async {
setState(() {
_loading = true;
_status = 'Verifying phone OTP...';
});
try {
final result = await _client.verifyOTP(
recipient: _phoneController.text,
code: _codeController.text,
verifyToken: _verifyToken,
);
setState(() {
if (result.hasJwt() && result.hasRefreshToken()) {
_jwt = result.jwt;
_status = 'Authenticated! JWT received and saved.';
} else {
_status = 'Verification failed: ${result.message}';
}
_loading = false;
});
} catch (e) {
setState(() {
_status = 'Error: $e';
_loading = false;
});
}
}
Future<void> _signInWithGoogle() async {
setState(() {
_loading = true;
_status = 'Signing in with Google...';
});
try {
final result = await _client.signInWithGoogle();
setState(() {
if (result.hasJwt() && result.hasRefreshToken()) {
_jwt = result.jwt;
_status = 'Authenticated with Google! JWT received and saved.';
} else if (result.hasVerifyToken()) {
_verifyToken = result.verifyToken;
_status = 'Google sign-in successful. Please verify phone.';
} else {
_status = 'Google sign-in failed: ${result.message}';
}
_loading = false;
});
} catch (e) {
setState(() {
_status = 'Error: $e';
_loading = false;
});
}
}
Future<void> _signInWithApple() async {
setState(() {
_loading = true;
_status = 'Signing in with Apple...';
});
try {
final result = await _client.signInWithApple();
setState(() {
if (result.hasJwt() && result.hasRefreshToken()) {
_jwt = result.jwt;
_status = 'Authenticated with Apple! JWT received and saved.';
} else if (result.hasVerifyToken()) {
_verifyToken = result.verifyToken;
_status = 'Apple sign-in successful. Please verify phone.';
} else {
_status = 'Apple sign-in failed: ${result.message}';
}
_loading = false;
});
} catch (e) {
setState(() {
_status = 'Error: $e';
_loading = false;
});
}
}
Future<void> _loadStoredTokens() async {
setState(() {
_loading = true;
_status = 'Loading stored tokens...';
});
try {
final jwt = await _client.getStoredJWT();
setState(() {
if (jwt != null) {
_jwt = jwt;
_status = 'Loaded JWT from secure storage';
} else {
_status = 'No stored tokens found';
}
_loading = false;
});
} catch (e) {
setState(() {
_status = 'Error loading tokens: $e';
_loading = false;
});
}
}
Future<void> _refreshStoredToken() async {
setState(() {
_loading = true;
_status = 'Refreshing token...';
});
try {
final success = await _client.refreshStoredToken();
if (success) {
final jwt = await _client.getStoredJWT();
setState(() {
_jwt = jwt;
_status = 'Token refreshed successfully!';
_loading = false;
});
} else {
setState(() {
_status = 'Failed to refresh token. No refresh token stored?';
_loading = false;
});
}
} catch (e) {
setState(() {
_status = 'Error refreshing token: $e';
_loading = false;
});
}
}
Future<void> _clearStoredTokens() async {
setState(() {
_loading = true;
_status = 'Clearing tokens...';
});
try {
await _client.clearTokens();
setState(() {
_jwt = null;
_status = 'Tokens cleared (logged out)';
_loading = false;
});
} catch (e) {
setState(() {
_status = 'Error clearing tokens: $e';
_loading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('Vestibule Auth Demo'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Status
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Status',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(_status),
if (_verifyToken != null) ...[
const SizedBox(height: 8),
Text('Verify Token: ${_verifyToken!.substring(0, 20)}...'),
],
if (_jwt != null) ...[
const SizedBox(height: 8),
Text('JWT: ${_jwt!.substring(0, 20)}...'),
],
],
),
),
),
const SizedBox(height: 24),
// Email OTP
const Text(
'Email OTP',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _loading ? null : _requestEmailOTP,
child: const Text('Request Email OTP'),
),
const SizedBox(height: 24),
// Phone OTP
const Text(
'Phone OTP',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
TextField(
controller: _phoneController,
decoration: const InputDecoration(
labelText: 'Phone (+1234567890)',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.phone,
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _loading ? null : _requestSMSOTP,
child: const Text('Request SMS OTP'),
),
const SizedBox(height: 24),
// Verify OTP
const Text(
'Verify OTP',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
TextField(
controller: _codeController,
decoration: const InputDecoration(
labelText: 'OTP Code',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: _loading ? null : _verifyEmailOTP,
child: const Text('Verify Email'),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
onPressed: _loading ? null : _verifyPhoneOTP,
child: const Text('Verify Phone'),
),
),
],
),
const SizedBox(height: 24),
// Social Sign-In
const Text(
'Social Sign-In',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _loading ? null : _signInWithGoogle,
icon: const Icon(Icons.g_mobiledata),
label: const Text('Sign in with Google'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _loading ? null : _signInWithApple,
icon: const Icon(Icons.apple),
label: const Text('Sign in with Apple'),
),
const SizedBox(height: 24),
// Token Management
const Text(
'Token Management',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: _loading ? null : _loadStoredTokens,
child: const Text('Load Tokens'),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
onPressed: _loading ? null : _refreshStoredToken,
child: const Text('Refresh Token'),
),
),
],
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _loading ? null : _clearStoredTokens,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Clear Tokens (Logout)'),
),
],
),
),
);
}
}