Skip to content

Commit

Permalink
Create TokenProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
banghuazhao committed Oct 28, 2024
1 parent cde3ba0 commit 5f1b663
Show file tree
Hide file tree
Showing 30 changed files with 618 additions and 454 deletions.
17 changes: 17 additions & 0 deletions data/lib/core/exceptions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class UnauthenticatedException implements Exception {
final String message;

UnauthenticatedException(this.message);

@override
String toString() => 'UnauthenticatedException: $message';
}

class ServerException implements Exception {
final String message;

ServerException(this.message);

@override
String toString() => 'ServerException: $message';
}
33 changes: 33 additions & 0 deletions data/lib/data_sources/authenticated_http_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// lib/data/datasources/authenticated_http_client.dart

import 'package:domain/repositories_abstract/token_provider.dart';
import 'package:http/http.dart' as http;

import '../core/exceptions.dart';

class AuthenticatedHttpClient extends http.BaseClient {
final http.Client _inner;
final TokenProvider _tokenProvider;

AuthenticatedHttpClient(this._inner, this._tokenProvider);

@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
final token = await _tokenProvider.getToken();
if (token != null) {
request.headers['Authorization'] = 'Bearer $token';
}

var response = await _inner.send(request);

if (response.statusCode == 401) {
// Token is invalid or expired, delete it
await _tokenProvider.deleteToken();

// Notify the app or handle as needed
throw UnauthenticatedException('Token expired');
}

return response;
}
}
24 changes: 24 additions & 0 deletions data/lib/providers/token_provider_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// lib/data/providers/token_provider_impl.dart

import 'package:domain/repositories_abstract/token_provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class TokenProviderImpl implements TokenProvider {
final FlutterSecureStorage _secureStorage = FlutterSecureStorage();
static const _tokenKey = 'accessToken';

@override
Future<void> saveToken(String token) async {
await _secureStorage.write(key: _tokenKey, value: token);
}

@override
Future<String?> getToken() async {
return await _secureStorage.read(key: _tokenKey);
}

@override
Future<void> deleteToken() async {
await _secureStorage.delete(key: _tokenKey);
}
}
24 changes: 23 additions & 1 deletion data/lib/repositories/auth_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import 'package:domain/entities/user.dart';
import 'package:domain/repositories_abstract/auth_repository.dart';
import 'package:http/http.dart' as http;

import '../core/exceptions.dart';
import '../data_sources/authenticated_http_client.dart';

class AuthRepositoryImpl implements AuthRepository {
final http.Client client;
final AuthenticatedHttpClient authClient;

AuthRepositoryImpl({required this.client});
AuthRepositoryImpl({required this.client, required this.authClient});

@override
Future<User> signup(String email, String password) async {
Expand Down Expand Up @@ -43,4 +47,22 @@ class AuthRepositoryImpl implements AuthRepository {
throw Exception('Login failed');
}
}

@override
Future<void> logout() async {
final url = Uri.parse('http://localhost:3000/api/auth/logout');

final response = await authClient.post(
url,
headers: {'Content-Type': 'application/json'},
);

if (response.statusCode == 200) {
return;
} else {
// Handle error responses
throw ServerException(
'Logout failed with status code: ${response.statusCode}');
}
}
}
1 change: 1 addition & 0 deletions data/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies:
domain: # Add dependency on the domain module
path: ../domain # Local path to your domain module
uuid: ^4.5.1
flutter_secure_storage: ^9.2.2

dev_dependencies:
flutter_test:
Expand Down
3 changes: 2 additions & 1 deletion domain/lib/repositories_abstract/auth_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ import '../entities/user.dart';

abstract class AuthRepository {
Future<User> signup(String email, String password);
Future<String> login(String email, String password); // New login method
Future<String> login(String email, String password);
Future<void> logout();
}
7 changes: 7 additions & 0 deletions domain/lib/repositories_abstract/token_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// lib/domain/providers/token_provider.dart

abstract class TokenProvider {
Future<void> saveToken(String token);
Future<String?> getToken();
Future<void> deleteToken();
}
20 changes: 18 additions & 2 deletions domain/lib/usecases/auth_usecase.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import '../entities/user.dart';
import '../repositories_abstract/auth_repository.dart';
import '../repositories_abstract/token_provider.dart';

class AuthUseCase {
final AuthRepository repository;
final TokenProvider tokenProvider;

AuthUseCase({required this.repository});
AuthUseCase({required this.repository, required this.tokenProvider});

Future<User> signup(String email, String password) async {
// Add any business logic or validation here if needed
Expand All @@ -15,6 +17,20 @@ class AuthUseCase {

Future<String> login(String email, String password) async {
// Add any business logic or validation here if needed
return await repository.login(email, password);
String accessToken = await repository.login(email, password);
await tokenProvider.saveToken(accessToken);
return accessToken;
}

Future<void> logout() async {
await repository.logout();
tokenProvider.deleteToken();
return;
}

@override
Future<bool> isLoggedIn() async {
final token = await tokenProvider.getToken();
return token != null;
}
}
26 changes: 20 additions & 6 deletions lib/injection_container.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import 'package:data/data_sources/authenticated_http_client.dart';
import 'package:data/data_sources/function_tools_data_source.dart';
import 'package:data/data_sources/open_ai_data_source.dart';
import 'package:data/providers/token_provider_impl.dart';
import 'package:data/repositories/auth_repository.dart';
import 'package:data/repositories/chat_repository_impl.dart';
import 'package:data/repositories/chat_session_repository_imp.dart';
import 'package:domain/domain.dart';
import 'package:domain/repositories_abstract/auth_repository.dart';
import 'package:domain/repositories_abstract/token_provider.dart';
import 'package:domain/usecases/auth_usecase.dart';
import 'package:domain/usecases/function_tools_usecase.dart';
import 'package:get_it/get_it.dart';
import 'package:http/http.dart' as http;
import 'package:swiftcomp/presentation/more/NewLogin/viewModels/login_view_model.dart';
import 'package:swiftcomp/presentation/more/NewLogin/viewModels/signup_view_model.dart';
import 'package:swiftcomp/presentation/more/providers/feature_flag_provider.dart';
import 'package:swiftcomp/presentation/more/viewModels/feature_flag_view_model.dart';
import 'package:swiftcomp/presentation/more/viewModels/login_view_model.dart';
import 'package:swiftcomp/presentation/more/viewModels/more_view_model.dart';
import 'package:swiftcomp/presentation/more/viewModels/signup_view_model.dart';
import 'presentation/chat/viewModels/chat_view_model.dart';

final sl = GetIt.instance;
Expand All @@ -20,9 +26,15 @@ void initInjection() {
sl.registerFactory<ChatViewModel>(() => ChatViewModel(
chatUseCase: sl(), chatSessionUseCase: sl(), functionToolsUseCase: sl()));
sl.registerFactory<LoginViewModel>(() => LoginViewModel(authUseCase: sl()));
sl.registerFactory<SignupViewModel>(() => SignupViewModel(
authUseCase: sl()));
sl.registerFactory<SignupViewModel>(() => SignupViewModel(authUseCase: sl()));
sl.registerFactory<MoreViewModel>(
() => MoreViewModel(authUseCase: sl(), featureFlagProvider: sl()));
sl.registerFactory<FeatureFlagViewModel>(
() => FeatureFlagViewModel(featureFlagProvider: sl()));

// Providers
sl.registerLazySingleton<TokenProvider>(() => TokenProviderImpl());
sl.registerLazySingleton<FeatureFlagProvider>(() => FeatureFlagProvider());

// Use Cases
sl.registerLazySingleton<ChatUseCase>(
Expand All @@ -33,15 +45,15 @@ void initInjection() {

sl.registerLazySingleton<FunctionToolsUseCase>(() => FunctionToolsUseCase());

sl.registerLazySingleton<AuthUseCase>(() => AuthUseCase(repository: sl()));
sl.registerLazySingleton<AuthUseCase>(() => AuthUseCase(repository: sl(), tokenProvider: sl()));

// Repositories
sl.registerLazySingleton<ChatRepository>(() =>
ChatRepositoryImp(openAIDataSource: sl(), functionToolsDataSource: sl()));
sl.registerLazySingleton<ChatSessionRepository>(
() => ChatSessionRepositoryImpl());
sl.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(client: sl()));
() => AuthRepositoryImpl(client: sl(), authClient: sl()));

// Data Sources
sl.registerLazySingleton<OpenAIDataSource>(
Expand All @@ -51,4 +63,6 @@ void initInjection() {

// External
sl.registerLazySingleton(() => http.Client());
sl.registerLazySingleton<AuthenticatedHttpClient>(() => AuthenticatedHttpClient(sl(), sl()));;

}
4 changes: 3 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:swiftcomp/generated/l10n.dart';
import 'package:swiftcomp/presentation/more/feature_flag_provider.dart';
import 'package:swiftcomp/presentation/more/providers/feature_flag_provider.dart';
import 'package:swiftcomp/presentation/more/viewModels/more_view_model.dart';
import 'package:swiftcomp/util/NumberPrecisionHelper.dart';
import 'package:swiftcomp/util/in_app_reviewer_helper.dart';
import 'package:swiftcomp/util/others.dart';
Expand Down Expand Up @@ -67,6 +68,7 @@ class _MyAppState extends State<MyApp> {
providers: [
ChangeNotifierProvider(create: (context) => NumberPrecisionHelper()),
ChangeNotifierProvider(create: (context) => FeatureFlagProvider()),
ChangeNotifierProvider(create: (context) => sl<MoreViewModel>()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
Expand Down
4 changes: 2 additions & 2 deletions lib/presentation/bottom_navigator.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:swiftcomp/presentation/more/feature_flag_provider.dart';
import 'package:swiftcomp/presentation/more/providers/feature_flag_provider.dart';
import 'package:swiftcomp/presentation/tools/page/tool_page.dart';

import 'chat/views/chat_screen.dart';
import 'more/more_page.dart';
import 'more/views/more_page.dart';

class BottomNavigator extends StatefulWidget {
const BottomNavigator({Key? key}) : super(key: key);
Expand Down
29 changes: 0 additions & 29 deletions lib/presentation/more/feature_flag_page.dart

This file was deleted.

Loading

0 comments on commit 5f1b663

Please sign in to comment.