Files

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);
});