I was wondering if somehow I could make the authorization token accessible everywhere in the code of my application while respecting the rules of Clean Architecture, knowing that it could change for example if the user disconnects, and that authentication is managed by a specific "auth" feature. Maybe do I have to enter it in the parameters of each datasource method that requires it?
@override
Future<(List<AlimentModel>, int)> getAllAliments() async {
final http.Response response = await client.get(
Uri.http(
APIBaseURL,
'$routeName/all',
),
headers: generateHeaders(
contentType: null,
authorization: token, // authorization header needed here
),
);
if (response.statusCode == 200) {
final Map<String, dynamic> json = jsonDecode(response.body);
final List<Map<String, dynamic>> alimentsJson = json['aliments'];
final List<AlimentModel> aliments =
AlimentModel.fromJsonToList(alimentsJson);
final int alimentsLastUpdate = json['alimentsLastUpdate'];
return (aliments, alimentsLastUpdate);
} else {
throw ServerException(
statusCode: response.statusCode,
error: jsonDecode(response.body)['error'],
);
}
}
I tried creating a SessionToken singleton in get_it, but it seems not very clean.
Not having access to your code but here is the strategy that I suggest you put in place and then it is up to you to adapt it to your project.
You create a TokenRepository
abstract class TokenRepository {
String? getToken();
void setToken(String? token);
}
You create TokenRepositoryImpl which implements the TokenRepository interface
class TokenRepositoryImpl implements TokenRepository {
String? _token;
@override
String? getToken() => _token;
@override
void setToken(String? token) {
_token = token;
// you can implement secure token storage here
}
}
In your datasource where you implemented the getAllAliments() method you use TokenRepository to obtain the token
class YourDataSource {
final TokenRepository tokenRepository;
YourDataSource({required this.client, required this.tokenRepository});
@override
Future<(List<AlimentModel>, int)> getAllAliments() async {
final http.Response response = await client.get(
Uri.http(
APIBaseURL,
'$routeName/all',
),
headers: generateHeaders(
contentType: null,
authorization:
tokenRepository.getToken(), // use like that the token here the tokeauthorization header needed here
),
);
if (response.statusCode == 200) {
final Map<String, dynamic> json = jsonDecode(response.body);
final List<Map<String, dynamic>> alimentsJson = json['aliments'];
final List<AlimentModel> aliments =
AlimentModel.fromJsonToList(alimentsJson);
final int alimentsLastUpdate = json['alimentsLastUpdate'];
return (aliments, alimentsLastUpdate);
} else {
throw ServerException(
statusCode: response.statusCode,
error: jsonDecode(response.body)['error'],
);
}
}
}
In the authentication service you retrieve your token
class AuthService {
final TokenRepository tokenRepository;
final AuthenticationRemoteDataSource authDataSource;
AuthService({required this.tokenRepository, required this.authDataSource});
Future<void> login(String username, String password) async {
final token = await authDataSource.authenticate(username, password);
tokenRepository.setToken(token);
}
void logout() {
tokenRepository.setToken(null);
}
}
And to finish configuring dependency injection
getIt.registerLazySingleton<TokenRepository>(() => TokenRepositoryImpl());
getIt.registerFactory<YourDataSource>(() => YourDataSource(client: http.Client(), tokenRepository: getIt<TokenRepository>()));
getIt.registerFactory<AuthService>(() => AuthService(tokenRepository: getIt<TokenRepository>(), authDataSource: AuthenticationRemoteDataSource()));
I hope that will help you.