253 lines
5.4 KiB
Markdown
253 lines
5.4 KiB
Markdown
# Authentication Feature - Quick Reference
|
|
|
|
## Import
|
|
|
|
```dart
|
|
import 'package:minhthu/features/auth/auth.dart';
|
|
```
|
|
|
|
## Common Usage Patterns
|
|
|
|
### 1. Login
|
|
```dart
|
|
ref.read(authProvider.notifier).login(username, password);
|
|
```
|
|
|
|
### 2. Logout
|
|
```dart
|
|
ref.read(authProvider.notifier).logout();
|
|
```
|
|
|
|
### 3. Check if Authenticated
|
|
```dart
|
|
final isAuthenticated = ref.watch(isAuthenticatedProvider);
|
|
```
|
|
|
|
### 4. Get Current User
|
|
```dart
|
|
final user = ref.watch(currentUserProvider);
|
|
if (user != null) {
|
|
print('Logged in as: ${user.username}');
|
|
}
|
|
```
|
|
|
|
### 5. Watch Auth State
|
|
```dart
|
|
final authState = ref.watch(authProvider);
|
|
|
|
if (authState.isLoading) {
|
|
return LoadingIndicator();
|
|
}
|
|
|
|
if (authState.error != null) {
|
|
return ErrorView(message: authState.error!);
|
|
}
|
|
|
|
if (authState.isAuthenticated) {
|
|
return HomeView(user: authState.user!);
|
|
}
|
|
|
|
return LoginView();
|
|
```
|
|
|
|
### 6. Listen to Auth Changes
|
|
```dart
|
|
ref.listen(authProvider, (previous, next) {
|
|
if (next.isAuthenticated) {
|
|
context.go('/home');
|
|
} else if (next.error != null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(next.error!)),
|
|
);
|
|
}
|
|
});
|
|
```
|
|
|
|
## Key Classes
|
|
|
|
### AuthState
|
|
```dart
|
|
class AuthState {
|
|
final UserEntity? user;
|
|
final bool isAuthenticated;
|
|
final bool isLoading;
|
|
final String? error;
|
|
}
|
|
```
|
|
|
|
### UserEntity
|
|
```dart
|
|
class UserEntity {
|
|
final String userId;
|
|
final String username;
|
|
final String accessToken;
|
|
final String? refreshToken;
|
|
}
|
|
```
|
|
|
|
### LoginRequestModel
|
|
```dart
|
|
final request = LoginRequestModel(
|
|
username: 'john.doe',
|
|
password: 'secure123',
|
|
);
|
|
```
|
|
|
|
## Providers
|
|
|
|
| Provider | Type | Description |
|
|
|----------|------|-------------|
|
|
| `authProvider` | `StateNotifier<AuthState>` | Main auth state |
|
|
| `isAuthenticatedProvider` | `bool` | Check auth status |
|
|
| `currentUserProvider` | `UserEntity?` | Get current user |
|
|
| `isAuthLoadingProvider` | `bool` | Check loading state |
|
|
| `authErrorProvider` | `String?` | Get error message |
|
|
|
|
## API Endpoints
|
|
|
|
| Endpoint | Method | Description |
|
|
|----------|--------|-------------|
|
|
| `/api/v1/auth/login` | POST | Login |
|
|
| `/api/v1/auth/logout` | POST | Logout |
|
|
| `/api/v1/auth/refresh` | POST | Refresh token |
|
|
|
|
## Error Types
|
|
|
|
- `ValidationFailure` - Invalid input
|
|
- `AuthenticationFailure` - Login failed
|
|
- `NetworkFailure` - Network error
|
|
- `ServerFailure` - Server error
|
|
|
|
## Protected Route Example
|
|
|
|
```dart
|
|
class MyPage extends ConsumerWidget {
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final authState = ref.watch(authProvider);
|
|
|
|
if (!authState.isAuthenticated) {
|
|
return LoginPage();
|
|
}
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text('Protected Page'),
|
|
actions: [
|
|
IconButton(
|
|
icon: Icon(Icons.logout),
|
|
onPressed: () => ref.read(authProvider.notifier).logout(),
|
|
),
|
|
],
|
|
),
|
|
body: Center(
|
|
child: Text('Hello, ${authState.user!.username}!'),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Check Auth on App Start
|
|
```dart
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
Future.microtask(() {
|
|
ref.read(authProvider.notifier).checkAuthStatus();
|
|
});
|
|
}
|
|
```
|
|
|
|
### Show Loading Overlay
|
|
```dart
|
|
if (ref.watch(isAuthLoadingProvider)) {
|
|
return Stack(
|
|
children: [
|
|
yourContent,
|
|
LoadingIndicator.overlay(),
|
|
],
|
|
);
|
|
}
|
|
```
|
|
|
|
### Conditional Navigation
|
|
```dart
|
|
ref.listen(authProvider, (previous, next) {
|
|
if (!previous!.isAuthenticated && next.isAuthenticated) {
|
|
context.go('/home');
|
|
} else if (previous.isAuthenticated && !next.isAuthenticated) {
|
|
context.go('/login');
|
|
}
|
|
});
|
|
```
|
|
|
|
## Testing Helpers
|
|
|
|
```dart
|
|
// Mock auth state
|
|
final mockAuthState = AuthState.authenticated(
|
|
UserEntity(
|
|
userId: '123',
|
|
username: 'test',
|
|
accessToken: 'token',
|
|
),
|
|
);
|
|
|
|
// Create test container
|
|
final container = ProviderContainer(
|
|
overrides: [
|
|
authProvider.overrideWith((ref) => MockAuthNotifier()),
|
|
],
|
|
);
|
|
```
|
|
|
|
## Files Reference
|
|
|
|
| Layer | File | Purpose |
|
|
|-------|------|---------|
|
|
| **Data** | `login_request_model.dart` | Request DTO |
|
|
| | `user_model.dart` | User DTO |
|
|
| | `auth_remote_datasource.dart` | API calls |
|
|
| | `auth_repository_impl.dart` | Repository impl |
|
|
| **Domain** | `user_entity.dart` | Domain entity |
|
|
| | `auth_repository.dart` | Repository interface |
|
|
| | `login_usecase.dart` | Business logic |
|
|
| **Presentation** | `login_page.dart` | Login UI |
|
|
| | `login_form.dart` | Form widget |
|
|
| | `auth_provider.dart` | State management |
|
|
| **DI** | `auth_dependency_injection.dart` | Providers setup |
|
|
|
|
## Troubleshooting Quick Fixes
|
|
|
|
| Issue | Solution |
|
|
|-------|----------|
|
|
| Provider not found | Add `ProviderScope` to main.dart |
|
|
| Navigation fails | Check router configuration |
|
|
| Tokens not saved | Verify secure storage setup |
|
|
| API calls fail | Check base URL in constants |
|
|
| State not updating | Use `ConsumerWidget` |
|
|
|
|
## Performance Tips
|
|
|
|
1. Use `ref.read()` for one-time operations
|
|
2. Use `ref.watch()` for reactive updates
|
|
3. Use `ref.listen()` for side effects
|
|
4. Avoid rebuilding entire tree - scope providers
|
|
5. Use `select()` for partial state watching
|
|
|
|
## Security Checklist
|
|
|
|
- [x] Tokens in secure storage
|
|
- [x] Password fields obscured
|
|
- [x] No logging of sensitive data
|
|
- [x] Token auto-added to headers
|
|
- [x] Token cleared on logout
|
|
- [x] Input validation
|
|
- [ ] HTTPS only (configure in production)
|
|
- [ ] Token expiration handling
|
|
- [ ] Rate limiting
|
|
- [ ] Biometric auth (optional)
|