Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor authentication #552

Merged
merged 2 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,61 @@
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: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<AuthenticatedUserModel?> getAuthenticatedUser() async {
fremartini marked this conversation as resolved.
Show resolved Hide resolved
final jsonString = await storage.read(key: _authenticatedUserKey);

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

return AuthenticatedUserModel.fromJson(
json.decode(jsonString) as Map<String, dynamic>,
);
}

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

if (user == null) {
return;
}

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,26 @@
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();
final token = user?.token;

if (token != null) {
final updatedHeaders = Map.of(request.headers);
updatedHeaders['Authorization'] = 'Bearer $token';

return request.copyWith(headers: updatedHeaders);
if (token == null) {
return request;
}

return request;
final updatedHeaders = Map.of(request.headers);
updatedHeaders['Authorization'] = 'Bearer $token';

return request.copyWith(headers: updatedHeaders);
}
}
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) {
TTA777 marked this conversation as resolved.
Show resolved Hide resolved
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});

Check warning on line 6 in lib/features/authentication/domain/usecases/clear_authenticated_user.dart

View check run for this annotation

Codecov / codecov/patch

lib/features/authentication/domain/usecases/clear_authenticated_user.dart#L6

Added line #L6 was not covered by tests

Future<void> call() async {
await dataSource.clearAuthenticatedUser();

Check warning on line 9 in lib/features/authentication/domain/usecases/clear_authenticated_user.dart

View check run for this annotation

Codecov / codecov/patch

lib/features/authentication/domain/usecases/clear_authenticated_user.dart#L8-L9

Added lines #L8 - L9 were not covered by tests
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:coffeecard/features/authentication/data/datasources/authentication_local_data_source.dart';
import 'package:coffeecard/features/authentication/domain/entities/authenticated_user.dart';

class GetAuthenticatedUser {
final AuthenticationLocalDataSource dataSource;

GetAuthenticatedUser({required this.dataSource});

Check warning on line 7 in lib/features/authentication/domain/usecases/get_authenticated_user.dart

View check run for this annotation

Codecov / codecov/patch

lib/features/authentication/domain/usecases/get_authenticated_user.dart#L7

Added line #L7 was not covered by tests

Future<AuthenticatedUser?> call() async {
return dataSource.getAuthenticatedUser();

Check warning on line 10 in lib/features/authentication/domain/usecases/get_authenticated_user.dart

View check run for this annotation

Codecov / codecov/patch

lib/features/authentication/domain/usecases/get_authenticated_user.dart#L9-L10

Added lines #L9 - L10 were not covered by tests
}
}
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});

Check warning on line 7 in lib/features/authentication/domain/usecases/save_authenticated_user.dart

View check run for this annotation

Codecov / codecov/patch

lib/features/authentication/domain/usecases/save_authenticated_user.dart#L7

Added line #L7 was not covered by tests

Future<void> call({

Check warning on line 9 in lib/features/authentication/domain/usecases/save_authenticated_user.dart

View check run for this annotation

Codecov / codecov/patch

lib/features/authentication/domain/usecases/save_authenticated_user.dart#L9

Added line #L9 was not covered by tests
required String email,
required String token,
required String encodedPasscode,
}) async {
return dataSource.saveAuthenticatedUser(
AuthenticatedUserModel(

Check warning on line 15 in lib/features/authentication/domain/usecases/save_authenticated_user.dart

View check run for this annotation

Codecov / codecov/patch

lib/features/authentication/domain/usecases/save_authenticated_user.dart#L14-L15

Added lines #L14 - L15 were not covered by tests
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,51 @@ 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 {
final authenticatedUser = await getAuthenticatedUser();

if (authenticatedUser == null) {
emit(const AuthenticationState.unauthenticated());
return;
}

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
Loading