bapp_auth 0.1.5
bapp_auth: ^0.1.5 copied to clipboard
Cross-platform OAuth package for Bapp authentication with Keycloak integration and API client
Bapp Auth #
A cross-platform Flutter package for Bapp authentication with Keycloak integration. Supports SSO OpenID Connect flow, device authentication flow, and provides a comprehensive API client for Bapp Framework.
Features #
-
Keycloak OAuth Integration
- SSO OpenID Connect flow with PKCE
- Device authentication flow
- Token refresh and management
- Token persistence with SharedPreferences
-
API Client
- Configurable base URL
- Token and Bearer authentication
- Multi-tenancy support
- Automatic retry with exponential backoff
- Comprehensive CRUD operations
- Task, Action, and Widget APIs
- Pagination support
- Error handling
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
bapp_auth:
git:
url: https://git.pidginhost.net/bapp-cloud/packages/flutter-bapp-auth.git
Or for local development:
dependencies:
bapp_auth:
path: ../path/to/bapp-auth
Platform Support #
This package supports all Flutter platforms:
- ✅ Android - Custom URL scheme deep links
- ✅ iOS - Custom URL scheme deep links
- ✅ macOS - Custom URL scheme deep links
- ✅ Web - Origin-based redirects
- ✅ Windows - Localhost redirects
- ✅ Linux - Localhost redirects
Platform-Specific Configuration #
Each platform requires specific configuration for OAuth redirects. The package includes a PlatformConfig helper that automatically detects your platform and provides the correct redirect URI.
Quick Setup:
import 'package:bapp_auth/bapp_auth.dart';
// Automatically get the correct redirect URI for current platform
final redirectUri = PlatformConfig.getRedirectUri();
final token = await auth.authenticateWithSSO(
redirectUri: redirectUri,
);
For detailed platform-specific configuration instructions, see:
- 📖 Complete Platform Configuration Guide
Quick Links:
- Android Configuration
- iOS Configuration
- macOS Configuration
- Web Configuration
- Windows Configuration
- Linux Configuration
- Keycloak Setup
Usage #
Authentication #
SSO OpenID Connect Flow (with Browser Session Reuse!)
The package uses the system browser for authentication, which means:
- ✅ If the user is already logged in to Keycloak in their browser, they won't need to log in again!
- ✅ Browser cookies and sessions are shared automatically
- ✅ Seamless single sign-on experience across apps
- ✅ Users get a familiar, trusted browser experience
import 'package:bapp_auth/bapp_auth.dart';
final auth = KeycloakAuth(
hostname: 'id.bapp.ro',
realm: 'bapp',
clientId: 'your-client-id',
);
try {
// Use automatic platform detection for redirect URI
final redirectUri = PlatformConfig.getRedirectUri();
// If user is logged in to Keycloak in browser → Instant auth, no login needed!
final token = await auth.authenticateWithSSO(
redirectUri: redirectUri,
// preferEphemeral: false (default) - Reuses browser session
// preferEphemeral: true - Forces fresh login (private mode)
);
// Save token for later use
await auth.saveToken(token);
print('Access Token: ${token.accessToken}');
} catch (e) {
print('Authentication failed: $e');
}
Want to force a fresh login?
final token = await auth.authenticateWithSSO(
redirectUri: redirectUri,
preferEphemeral: true, // Private mode - won't use existing session
);
📖 Read the complete SSO Session Management Guide to learn:
- How browser session reuse works on each platform
- When to use private mode vs. persistent sessions
- Testing SSO session behavior
- Best practices for session management
Device Authentication Flow
final token = await auth.authenticateWithDevice(
onUserAction: (userCode, verificationUri) {
print('Go to $verificationUri and enter code: $userCode');
},
onStatusUpdate: (status) {
print('Status: $status');
},
);
await auth.saveToken(token);
Token Refresh
if (token.isExpiringSoon && token.refreshToken != null) {
final newToken = await auth.refreshToken(token.refreshToken!);
await auth.saveToken(newToken);
}
API Client #
Basic Usage
final client = BappApiClient(
baseUrl: 'https://panel.bapp.ro/api/',
bearer: token.accessToken,
app: 'erp',
);
// Call the "me" endpoint
final user = await client.me();
print('User: $user');
Using Fixed Token
final client = BappApiClient(
baseUrl: 'https://panel.bapp.ro/api/',
token: 'your-fixed-token',
app: 'erp',
);
CRUD Operations
// List items
final items = await client.list('example.model', queryParams: {'page': '1'});
// Retrieve single item
final item = await client.retrieve('example.model', 'item-id');
// Create item
final newItem = await client.create('example.model', {
'name': 'New Item',
'description': 'Item description',
});
// Update item
final updated = await client.update('example.model', 'item-id', {
'name': 'Updated Name',
});
// Patch item
final patched = await client.patchResource('example.model', 'item-id', {
'description': 'New description',
});
// Delete item
await client.deleteResource('example.model', 'item-id');
Tasks API
// Get available tasks
final tasks = await client.getAvailableTasks();
// Get task options
final options = await client.getTaskOptions('task.code');
// Call task
final result = await client.callTask('task.code', data: {
'param1': 'value1',
});
Actions API
// Get available actions
final actions = await client.getAvailableActions();
// Call action
final result = await client.callAction('action.code', data: {
'param1': 'value1',
});
Widgets API
// Get available widgets
final widgets = await client.getAvailableWidgets();
// Call widget
final result = await client.callWidget('widget.code', data: {
'param1': 'value1',
});
Pagination
// Stream all pages
await for (final page in client.listPages('example.model')) {
print('Page: ${page['results']}');
}
// Stream individual items
await for (final item in client.iterResults('example.model', limit: 100)) {
print('Item: $item');
}
// Get all items at once
final (items, totalCount) = await client.listAll('example.model');
print('Total items: $totalCount');
Multi-tenancy
// Set tenant globally
client.setTenant('tenant-id');
// Use tenant context for specific calls
await client.withTenant('tenant-id', () async {
return await client.list('example.model');
});
Example #
See the example directory for a complete example app demonstrating both authentication flows and API client usage.
License #
MIT License
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.