fill
This commit is contained in:
380
lib/features/auth/README.md
Normal file
380
lib/features/auth/README.md
Normal file
@@ -0,0 +1,380 @@
|
||||
# Authentication Feature
|
||||
|
||||
Complete authentication implementation following clean architecture principles for the warehouse management app.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
auth/
|
||||
├── data/ # Data layer
|
||||
│ ├── datasources/ # API and local data sources
|
||||
│ │ └── auth_remote_datasource.dart
|
||||
│ ├── models/ # Data transfer objects
|
||||
│ │ ├── login_request_model.dart
|
||||
│ │ └── user_model.dart
|
||||
│ ├── repositories/ # Repository implementations
|
||||
│ │ └── auth_repository_impl.dart
|
||||
│ └── data.dart # Barrel export
|
||||
│
|
||||
├── domain/ # Domain layer (business logic)
|
||||
│ ├── entities/ # Business entities
|
||||
│ │ └── user_entity.dart
|
||||
│ ├── repositories/ # Repository interfaces
|
||||
│ │ └── auth_repository.dart
|
||||
│ ├── usecases/ # Use cases
|
||||
│ │ └── login_usecase.dart
|
||||
│ └── domain.dart # Barrel export
|
||||
│
|
||||
├── presentation/ # Presentation layer (UI)
|
||||
│ ├── pages/ # Screen widgets
|
||||
│ │ └── login_page.dart
|
||||
│ ├── providers/ # State management
|
||||
│ │ └── auth_provider.dart
|
||||
│ ├── widgets/ # Reusable widgets
|
||||
│ │ └── login_form.dart
|
||||
│ └── presentation.dart # Barrel export
|
||||
│
|
||||
├── di/ # Dependency injection
|
||||
│ └── auth_dependency_injection.dart
|
||||
│
|
||||
├── auth.dart # Main barrel export
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Implemented
|
||||
- ✅ User login with username/password
|
||||
- ✅ Token storage in secure storage
|
||||
- ✅ Authentication state management
|
||||
- ✅ Form validation
|
||||
- ✅ Error handling with user-friendly messages
|
||||
- ✅ Loading states
|
||||
- ✅ Auto-navigation after successful login
|
||||
- ✅ Check authentication status on app start
|
||||
- ✅ Logout functionality
|
||||
- ✅ Token refresh (prepared for future use)
|
||||
|
||||
### Pending
|
||||
- ⏳ Integration with actual API endpoints
|
||||
- ⏳ Biometric authentication
|
||||
- ⏳ Remember me functionality
|
||||
- ⏳ Password recovery
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Login Flow
|
||||
```
|
||||
1. User enters credentials in LoginPage
|
||||
2. LoginForm validates input
|
||||
3. AuthNotifier.login() is called
|
||||
4. LoginUseCase validates and processes request
|
||||
5. AuthRepository calls AuthRemoteDataSource
|
||||
6. API response is converted to UserModel
|
||||
7. Tokens saved to SecureStorage
|
||||
8. AuthState updated to authenticated
|
||||
9. Navigation to warehouses page
|
||||
```
|
||||
|
||||
### Logout Flow
|
||||
```
|
||||
1. User triggers logout
|
||||
2. AuthNotifier.logout() is called
|
||||
3. LogoutUseCase calls AuthRepository
|
||||
4. API logout call (optional, can fail)
|
||||
5. SecureStorage cleared
|
||||
6. AuthState reset to initial
|
||||
7. Navigation to login page
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Import
|
||||
```dart
|
||||
import 'package:minhthu/features/auth/auth.dart';
|
||||
```
|
||||
|
||||
### Using in UI
|
||||
```dart
|
||||
// In a ConsumerWidget or ConsumerStatefulWidget
|
||||
class MyWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Watch auth state
|
||||
final authState = ref.watch(authProvider);
|
||||
|
||||
// Check authentication
|
||||
if (authState.isAuthenticated) {
|
||||
return AuthenticatedView(user: authState.user!);
|
||||
}
|
||||
|
||||
// Handle loading
|
||||
if (authState.isLoading) {
|
||||
return LoadingIndicator();
|
||||
}
|
||||
|
||||
// Show error
|
||||
if (authState.error != null) {
|
||||
return ErrorView(message: authState.error!);
|
||||
}
|
||||
|
||||
return LoginView();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Perform Login
|
||||
```dart
|
||||
// In your widget
|
||||
void handleLogin(String username, String password) {
|
||||
ref.read(authProvider.notifier).login(username, password);
|
||||
}
|
||||
```
|
||||
|
||||
### Perform Logout
|
||||
```dart
|
||||
void handleLogout() {
|
||||
ref.read(authProvider.notifier).logout();
|
||||
}
|
||||
```
|
||||
|
||||
### Check Auth Status
|
||||
```dart
|
||||
void checkIfAuthenticated() async {
|
||||
await ref.read(authProvider.notifier).checkAuthStatus();
|
||||
}
|
||||
```
|
||||
|
||||
### Listen to Auth Changes
|
||||
```dart
|
||||
ref.listen(authProvider, (previous, next) {
|
||||
if (next.isAuthenticated) {
|
||||
// Navigate to home
|
||||
context.go('/home');
|
||||
} else if (next.error != null) {
|
||||
// Show error snackbar
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(next.error!)),
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## API Integration
|
||||
|
||||
### Expected API Response Format
|
||||
```json
|
||||
{
|
||||
"Value": {
|
||||
"userId": "string",
|
||||
"username": "string",
|
||||
"accessToken": "string",
|
||||
"refreshToken": "string"
|
||||
},
|
||||
"IsSuccess": true,
|
||||
"IsFailure": false,
|
||||
"Errors": [],
|
||||
"ErrorCodes": []
|
||||
}
|
||||
```
|
||||
|
||||
### Login Request
|
||||
```json
|
||||
POST /api/v1/auth/login
|
||||
{
|
||||
"username": "string",
|
||||
"password": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Logout Request
|
||||
```json
|
||||
POST /api/v1/auth/logout
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
### Refresh Token Request
|
||||
```json
|
||||
POST /api/v1/auth/refresh
|
||||
{
|
||||
"refreshToken": "string"
|
||||
}
|
||||
```
|
||||
|
||||
## State Management
|
||||
|
||||
### AuthState
|
||||
```dart
|
||||
class AuthState {
|
||||
final UserEntity? user; // Current user or null
|
||||
final bool isAuthenticated; // Authentication status
|
||||
final bool isLoading; // Loading indicator
|
||||
final String? error; // Error message
|
||||
}
|
||||
```
|
||||
|
||||
### State Transitions
|
||||
```
|
||||
Initial State → Loading → Authenticated (success)
|
||||
→ Error (failure)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests (TODO)
|
||||
```dart
|
||||
// Test use cases
|
||||
test('login with valid credentials returns user', () async {
|
||||
// Arrange
|
||||
final useCase = LoginUseCase(mockRepository);
|
||||
final request = LoginRequestModel(
|
||||
username: 'testuser',
|
||||
password: 'password123',
|
||||
);
|
||||
|
||||
// Act
|
||||
final result = await useCase(request);
|
||||
|
||||
// Assert
|
||||
expect(result.isRight(), true);
|
||||
});
|
||||
|
||||
// Test repository
|
||||
test('repository saves tokens on successful login', () async {
|
||||
// Test implementation
|
||||
});
|
||||
```
|
||||
|
||||
### Widget Tests (TODO)
|
||||
```dart
|
||||
testWidgets('login page shows form fields', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
child: MaterialApp(home: LoginPage()),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(TextField), findsNWidgets(2));
|
||||
expect(find.text('Username'), findsOneWidget);
|
||||
expect(find.text('Password'), findsOneWidget);
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Validation Errors
|
||||
- Empty username/password
|
||||
- Username too short (< 3 characters)
|
||||
- Password too short (< 6 characters)
|
||||
|
||||
### Network Errors
|
||||
- Connection timeout
|
||||
- No internet connection
|
||||
- Server unreachable
|
||||
|
||||
### Authentication Errors
|
||||
- Invalid credentials
|
||||
- Account locked
|
||||
- Token expired
|
||||
|
||||
### Display Errors
|
||||
All errors are displayed in a user-friendly format in the UI with appropriate styling.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Implemented
|
||||
- ✅ Tokens stored in secure storage (encrypted)
|
||||
- ✅ Password field obscured
|
||||
- ✅ Auth token added to API headers automatically
|
||||
- ✅ Token cleared on logout
|
||||
- ✅ No sensitive data logged
|
||||
|
||||
### Best Practices
|
||||
- Never log passwords or tokens
|
||||
- Use HTTPS for all API calls
|
||||
- Implement token refresh before expiration
|
||||
- Clear sensitive data on logout
|
||||
- Validate all user input
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Core Dependencies
|
||||
- `flutter_riverpod` - State management
|
||||
- `dartz` - Functional programming (Either type)
|
||||
- `flutter_secure_storage` - Secure token storage
|
||||
- `dio` - HTTP client (via ApiClient)
|
||||
- `equatable` - Value equality
|
||||
- `go_router` - Navigation
|
||||
|
||||
### Internal Dependencies
|
||||
- `core/network/api_client.dart` - HTTP client wrapper
|
||||
- `core/storage/secure_storage.dart` - Secure storage wrapper
|
||||
- `core/errors/failures.dart` - Error types
|
||||
- `core/errors/exceptions.dart` - Exception types
|
||||
- `core/widgets/custom_button.dart` - Button widget
|
||||
- `core/widgets/loading_indicator.dart` - Loading widget
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Issue: Login always fails**
|
||||
- Check API endpoint configuration in `api_endpoints.dart`
|
||||
- Verify API is running and accessible
|
||||
- Check network connectivity
|
||||
- Verify request/response format matches API
|
||||
|
||||
**Issue: Tokens not persisted**
|
||||
- Verify secure storage is initialized
|
||||
- Check device storage permissions
|
||||
- Clear app data and try again
|
||||
|
||||
**Issue: Navigation doesn't work after login**
|
||||
- Verify router configuration includes `/warehouses` route
|
||||
- Check if listener in LoginPage is properly set up
|
||||
- Ensure ProviderScope wraps the app
|
||||
|
||||
**Issue: State not updating in UI**
|
||||
- Ensure using ConsumerWidget or ConsumerStatefulWidget
|
||||
- Verify provider is being watched, not just read
|
||||
- Check if state is properly copied in copyWith
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
1. **Biometric Authentication**
|
||||
- Face ID / Touch ID support
|
||||
- Fallback to password
|
||||
|
||||
2. **Token Auto-Refresh**
|
||||
- Background token refresh
|
||||
- Seamless reauthentication
|
||||
|
||||
3. **Multi-factor Authentication**
|
||||
- OTP support
|
||||
- SMS verification
|
||||
|
||||
4. **Remember Me**
|
||||
- Optional persistent login
|
||||
- Secure device storage
|
||||
|
||||
5. **Password Reset**
|
||||
- Email-based reset flow
|
||||
- Security questions
|
||||
|
||||
## Contributing
|
||||
|
||||
When modifying this feature:
|
||||
|
||||
1. Follow clean architecture principles
|
||||
2. Maintain separation of concerns (data/domain/presentation)
|
||||
3. Add tests for new functionality
|
||||
4. Update this README with changes
|
||||
5. Follow existing code style and patterns
|
||||
|
||||
## Related Files
|
||||
|
||||
- App Router: `lib/core/routing/app_router.dart`
|
||||
- API Endpoints: `lib/core/constants/api_endpoints.dart`
|
||||
- App Theme: `lib/core/theme/app_theme.dart`
|
||||
- Main App: `lib/main.dart`
|
||||
Reference in New Issue
Block a user