177 lines
5.0 KiB
Dart
177 lines
5.0 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../core/api/client.dart';
|
|
import '../core/api/endpoints.dart';
|
|
import '../core/api/exceptions.dart';
|
|
import '../models/auth.dart';
|
|
|
|
enum AuthStatus { initial, authenticated, unauthenticated, loading }
|
|
|
|
class AuthState {
|
|
final AuthStatus status;
|
|
final User? user;
|
|
final String? error;
|
|
final bool needsTwoFactor;
|
|
|
|
AuthState({
|
|
this.status = AuthStatus.initial,
|
|
this.user,
|
|
this.error,
|
|
this.needsTwoFactor = false,
|
|
});
|
|
|
|
AuthState copyWith({
|
|
AuthStatus? status,
|
|
User? user,
|
|
String? error,
|
|
bool? needsTwoFactor,
|
|
}) =>
|
|
AuthState(
|
|
status: status ?? this.status,
|
|
user: user ?? this.user,
|
|
error: error,
|
|
needsTwoFactor: needsTwoFactor ?? this.needsTwoFactor,
|
|
);
|
|
}
|
|
|
|
class AuthNotifier extends StateNotifier<AuthState> {
|
|
final ApiClient _api;
|
|
|
|
AuthNotifier(this._api) : super(AuthState()) {
|
|
_checkAuth();
|
|
}
|
|
|
|
Future<void> _checkAuth() async {
|
|
final token = await _api.getAccessToken();
|
|
if (token != null) {
|
|
state = state.copyWith(status: AuthStatus.authenticated);
|
|
} else {
|
|
state = state.copyWith(status: AuthStatus.unauthenticated);
|
|
}
|
|
}
|
|
|
|
Future<void> login(String email, String password) async {
|
|
state = state.copyWith(status: AuthStatus.loading, error: null);
|
|
try {
|
|
final response = await _api.post(Endpoints.login, data: {
|
|
'email': email,
|
|
'password': password,
|
|
});
|
|
final authResponse = AuthResponse.fromJson(
|
|
response.data as Map<String, dynamic>);
|
|
if (authResponse.user.isTwoFactorEnabled) {
|
|
state = state.copyWith(
|
|
status: AuthStatus.unauthenticated,
|
|
needsTwoFactor: true,
|
|
);
|
|
return;
|
|
}
|
|
await _api.saveTokens(
|
|
authResponse.accessToken, authResponse.refreshToken);
|
|
state = state.copyWith(
|
|
status: AuthStatus.authenticated,
|
|
user: authResponse.user,
|
|
);
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(
|
|
status: AuthStatus.unauthenticated,
|
|
error: e.message,
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> register(String email, String password, String name) async {
|
|
state = state.copyWith(status: AuthStatus.loading, error: null);
|
|
try {
|
|
final response = await _api.post(Endpoints.register, data: {
|
|
'email': email,
|
|
'password': password,
|
|
'name': name,
|
|
});
|
|
final authResponse = AuthResponse.fromJson(
|
|
response.data as Map<String, dynamic>);
|
|
await _api.saveTokens(
|
|
authResponse.accessToken, authResponse.refreshToken);
|
|
state = state.copyWith(
|
|
status: AuthStatus.authenticated,
|
|
user: authResponse.user,
|
|
);
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(
|
|
status: AuthStatus.unauthenticated,
|
|
error: e.message,
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> verifyTwoFactor(String code) async {
|
|
state = state.copyWith(status: AuthStatus.loading, error: null);
|
|
try {
|
|
final response = await _api.post(Endpoints.twoFactor, data: {
|
|
'code': code,
|
|
});
|
|
final authResponse = AuthResponse.fromJson(
|
|
response.data as Map<String, dynamic>);
|
|
await _api.saveTokens(
|
|
authResponse.accessToken, authResponse.refreshToken);
|
|
state = state.copyWith(
|
|
status: AuthStatus.authenticated,
|
|
user: authResponse.user,
|
|
needsTwoFactor: false,
|
|
);
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(
|
|
status: AuthStatus.unauthenticated,
|
|
error: e.message,
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> logout() async {
|
|
await _api.clearTokens();
|
|
state = AuthState(status: AuthStatus.unauthenticated);
|
|
}
|
|
|
|
Future<void> forgotPassword(String email) async {
|
|
state = state.copyWith(status: AuthStatus.loading, error: null);
|
|
try {
|
|
await _api.post(Endpoints.forgotPassword, data: {'email': email});
|
|
state = state.copyWith(status: AuthStatus.unauthenticated);
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(
|
|
status: AuthStatus.unauthenticated, error: e.message);
|
|
}
|
|
}
|
|
|
|
Future<void> resetPassword(String token, String password) async {
|
|
state = state.copyWith(status: AuthStatus.loading, error: null);
|
|
try {
|
|
await _api.post(Endpoints.resetPassword, data: {
|
|
'token': token,
|
|
'password': password,
|
|
});
|
|
state = state.copyWith(status: AuthStatus.unauthenticated);
|
|
} on ApiException catch (e) {
|
|
state = state.copyWith(
|
|
status: AuthStatus.unauthenticated, error: e.message);
|
|
}
|
|
}
|
|
|
|
Future<String?> getOAuthUrl(String provider) async {
|
|
try {
|
|
final response = await _api.get('${Endpoints.oauth}/$provider');
|
|
return response.data['url'] as String?;
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
void clearError() {
|
|
state = state.copyWith(error: null);
|
|
}
|
|
}
|
|
|
|
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
|
|
final api = ref.read(apiClientProvider);
|
|
return AuthNotifier(api);
|
|
});
|