Skip to content

Commit

Permalink
refactor authentication (#552)
Browse files Browse the repository at this point in the history
  • Loading branch information
fremartini authored Nov 20, 2023
1 parent db0df6d commit d9f1ddd
Show file tree
Hide file tree
Showing 24 changed files with 619 additions and 666 deletions.
65 changes: 0 additions & 65 deletions lib/core/storage/secure_storage.dart

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'dart:convert';

import 'package:coffeecard/features/authentication/data/models/authenticated_user_model.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:fpdart/fpdart.dart';
import 'package:logger/logger.dart';

class AuthenticationLocalDataSource {
static const _authenticatedUserKey = 'authenticated_user';

final FlutterSecureStorage storage;
final Logger logger;

const AuthenticationLocalDataSource({
required this.storage,
required this.logger,
});

Future<void> saveAuthenticatedUser(
AuthenticatedUserModel authenticatedUser,
) async {
await storage.write(
key: _authenticatedUserKey,
value: json.encode(authenticatedUser),
);

logger.d('$authenticatedUser added to storage');
}

Future<Option<AuthenticatedUserModel>> getAuthenticatedUser() async {
final jsonString = await storage.read(key: _authenticatedUserKey);

if (jsonString == null) {
return const None();
}

final user = AuthenticatedUserModel.fromJson(
json.decode(jsonString) as Map<String, dynamic>,
);

return Some(user);
}

Future<void> clearAuthenticatedUser() async {
await storage.delete(key: _authenticatedUserKey);
logger.d('deleted data for $_authenticatedUserKey');
}

Future<void> updateToken(String token) async {
final user = await getAuthenticatedUser();

user.map(
(user) async {
final model = AuthenticatedUserModel(
email: user.email,
token: token,
encodedPasscode: user.encodedPasscode,
);

await saveAuthenticatedUser(model);
},
);
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
import 'dart:async';

import 'package:chopper/chopper.dart';
import 'package:coffeecard/core/storage/secure_storage.dart';
import 'package:coffeecard/features/authentication/data/datasources/authentication_local_data_source.dart';

class AuthenticationInterceptor implements RequestInterceptor {
final SecureStorage _storage;
final AuthenticationLocalDataSource localDataSource;

AuthenticationInterceptor(this._storage);
AuthenticationInterceptor(this.localDataSource);

/// Try retrieve authentication token from storage and add authentication header if exists
@override
FutureOr<Request> onRequest(Request request) async {
final token = await _storage.readToken();
final user = await localDataSource.getAuthenticatedUser();

if (token != null) {
return user.match(() => request, (user) {
final updatedHeaders = Map.of(request.headers);
updatedHeaders['Authorization'] = 'Bearer $token';
updatedHeaders['Authorization'] = 'Bearer ${user.token}';

return request.copyWith(headers: updatedHeaders);
}

return request;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:coffeecard/features/authentication/domain/entities/authenticated_user.dart';

class AuthenticatedUserModel extends AuthenticatedUser {
const AuthenticatedUserModel({
required super.email,
required super.token,
required super.encodedPasscode,
});

factory AuthenticatedUserModel.fromJson(Map<String, dynamic> json) {
return AuthenticatedUserModel(
email: json['email'] as String,
token: json['token'] as String,
encodedPasscode: json['passcode'] as String,
);
}

Map<String, dynamic> toJson() {
return {
'email': email,
'token': token,
'passcode': encodedPasscode,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import 'package:equatable/equatable.dart';
class AuthenticatedUser extends Equatable {
final String email;
final String token;
final String encodedPasscode;

const AuthenticatedUser({
required this.email,
required this.token,
required this.encodedPasscode,
});

@override
List<Object?> get props => [email, token];
List<Object?> get props => [
email,
token,
encodedPasscode,
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:coffeecard/features/authentication/data/datasources/authentication_local_data_source.dart';

class ClearAuthenticatedUser {
final AuthenticationLocalDataSource dataSource;

ClearAuthenticatedUser({required this.dataSource});

Future<void> call() async {
await dataSource.clearAuthenticatedUser();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:coffeecard/features/authentication/data/datasources/authentication_local_data_source.dart';
import 'package:coffeecard/features/authentication/domain/entities/authenticated_user.dart';
import 'package:fpdart/fpdart.dart';

class GetAuthenticatedUser {
final AuthenticationLocalDataSource dataSource;

GetAuthenticatedUser({required this.dataSource});

Future<Option<AuthenticatedUser>> call() async {
return dataSource.getAuthenticatedUser();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:coffeecard/features/authentication/data/datasources/authentication_local_data_source.dart';
import 'package:coffeecard/features/authentication/data/models/authenticated_user_model.dart';

class SaveAuthenticatedUser {
final AuthenticationLocalDataSource dataSource;

SaveAuthenticatedUser({required this.dataSource});

Future<void> call({
required String email,
required String token,
required String encodedPasscode,
}) async {
return dataSource.saveAuthenticatedUser(
AuthenticatedUserModel(
email: email,
token: token,
encodedPasscode: encodedPasscode,
),
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:coffeecard/core/storage/secure_storage.dart';
import 'package:coffeecard/features/authentication/domain/entities/authenticated_user.dart';
import 'package:coffeecard/features/authentication/domain/usecases/clear_authenticated_user.dart';
import 'package:coffeecard/features/authentication/domain/usecases/get_authenticated_user.dart';
import 'package:coffeecard/features/authentication/domain/usecases/save_authenticated_user.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

Expand All @@ -9,38 +11,50 @@ part 'authentication_state.dart';
// trigger a logout (for instance, when the user requests logs out themselves
// vs the user's token expires and fails to renew).
class AuthenticationCubit extends Cubit<AuthenticationState> {
AuthenticationCubit(this._storage) : super(const AuthenticationState._());
final ClearAuthenticatedUser clearAuthenticatedUser;
final SaveAuthenticatedUser saveAuthenticatedUser;
final GetAuthenticatedUser getAuthenticatedUser;

final SecureStorage _storage;
AuthenticationCubit({
required this.clearAuthenticatedUser,
required this.saveAuthenticatedUser,
required this.getAuthenticatedUser,
}) : super(const AuthenticationState._());

Future<void> appStarted() async {
final authenticatedUser = await _storage.getAuthenticatedUser();
if (authenticatedUser != null) {
emit(AuthenticationState.authenticated(authenticatedUser));
} else {
emit(const AuthenticationState.unauthenticated());
}
final authenticatedUser = await getAuthenticatedUser();

authenticatedUser.match(
() => emit(const AuthenticationState.unauthenticated()),
(authenticatedUser) =>
emit(AuthenticationState.authenticated(authenticatedUser)),
);
}

Future<void> authenticated(
String email,
String encodedPasscode,
String token,
) async {
await _storage.saveAuthenticatedUser(
email,
encodedPasscode,
token,
await saveAuthenticatedUser(
email: email,
token: token,
encodedPasscode: encodedPasscode,
);

emit(
AuthenticationState.authenticated(
AuthenticatedUser(token: token, email: email),
AuthenticatedUser(
token: token,
email: email,
encodedPasscode: encodedPasscode,
),
),
);
}

Future<void> unauthenticated() async {
await _storage.clearAuthenticatedUser();
await clearAuthenticatedUser();
emit(const AuthenticationState.unauthenticated());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ class AccountRemoteDataSource {

return Left(err);
},
(result) => Right(AuthenticatedUser(email: email, token: result.token!)),
(result) => Right(
AuthenticatedUser(
email: email,
encodedPasscode: encodedPasscode,
token: result.token!,
),
),
);
}

Expand Down
Loading

0 comments on commit d9f1ddd

Please sign in to comment.