Skip to content

Commit

Permalink
Merge pull request #17 from Ishad-M-I-M/main
Browse files Browse the repository at this point in the history
Added authentication on app initialization
  • Loading branch information
Ishad-M-I-M authored Nov 14, 2022
2 parents 594abd7 + ee57702 commit cae06bb
Show file tree
Hide file tree
Showing 15 changed files with 177 additions and 48 deletions.
2 changes: 2 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@
android:name="flutterEmbedding"
android:value="2" />
</application>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package net.atlassian.iblock;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.android.FlutterFragmentActivity;

public class MainActivity extends FlutterActivity {
public class MainActivity extends FlutterFragmentActivity {
}
2 changes: 2 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@
<true/>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
<key>NSFaceIDUsageDescription</key>
<string>Why is my app authenticating using face id?</string>
</dict>
</plist>
83 changes: 64 additions & 19 deletions lib/bloc/initialize/initialize_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'dart:async';
import 'dart:developer';

import 'package:bloc/bloc.dart';
import 'package:iblock/bloc/signup/signup_bloc.dart';
import 'package:local_auth/local_auth.dart';
import 'package:meta/meta.dart';
import 'package:connectivity_plus/connectivity_plus.dart';

Expand All @@ -18,37 +19,81 @@ class InitializeBloc extends Bloc<InitializeEvent, InitializeState> {
//await Future.delayed(const Duration(seconds: 3));

emit(CheckingInternetConnection());
add(CheckInternetConnection());
//await Future.delayed(const Duration(seconds: 3));

});

on<CheckInternetConnection>((event, emit) async{
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile || connectivityResult == ConnectivityResult.wifi) {
bool isPrivateKeyExist = await SecureStorageService.isKeyExist("private-key");
bool isContractAddressExist = await SecureStorageService.isKeyExist("contract-address");
if (isPrivateKeyExist && isContractAddressExist){
var privateKey = await SecureStorageService.get("private-key") as String;
var contractAddress = await SecureStorageService.get("contract-address") as String;
add(AuthenticateEvent());
}
else{
emit(NoInternetConnection());
}
});

on<AuthenticateEvent>((event, emit) async{
emit(Authenticating());
final LocalAuthentication auth = LocalAuthentication();
final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics;
final bool canAuthenticate =
canAuthenticateWithBiometrics || await auth.isDeviceSupported();

if (! canAuthenticate){
add(CheckStatus());
}
else{
final List<BiometricType> availableBiometrics =
await auth.getAvailableBiometrics();

if (availableBiometrics.isNotEmpty) {
try {
var userInfo = await UserContractService().getAll(
contractAddress, privateKey).timeout(const Duration(seconds: 10),
onTimeout: (){
throw TimeoutException("Unable to fetch data from blockchain!");
});
emit(Registered(userInfo));
}
catch(e){
emit(InitializeError(e.toString()));
}
final bool didAuthenticate = await auth.authenticate(
localizedReason: 'Please authenticate to proceed');
if(didAuthenticate){
emit(Authenticated());
add(CheckStatus());
}
else{
emit(Failed("Failed to Authenticate"));
}

} catch(error) {
log(error.toString());
}
}
else{
emit(NotRegistered());
add(CheckStatus());
}
}

});

on<CheckStatus>((event, emit) async{
bool isPrivateKeyExist = await SecureStorageService.isKeyExist("private-key");
bool isContractAddressExist = await SecureStorageService.isKeyExist("contract-address");
if (isPrivateKeyExist && isContractAddressExist){
var privateKey = await SecureStorageService.get("private-key") as String;
var contractAddress = await SecureStorageService.get("contract-address") as String;

try {
var userInfo = await UserContractService().getAll(
contractAddress, privateKey).timeout(const Duration(seconds: 10),
onTimeout: (){
throw TimeoutException("Unable to fetch data from blockchain!");
});
emit(Registered(userInfo));
}
catch(e){
emit(Failed(e.toString()));
}

}
else{
emit(NoInternetConnection());
emit(NotRegistered());
}

});
}
}
8 changes: 7 additions & 1 deletion lib/bloc/initialize/initialize_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ part of 'initialize_bloc.dart';
@immutable
abstract class InitializeEvent {}

class InitializeAppEvent extends InitializeEvent {}
class InitializeAppEvent extends InitializeEvent {}

class CheckInternetConnection extends InitializeEvent{}

class AuthenticateEvent extends InitializeEvent{}

class CheckStatus extends InitializeEvent{}
10 changes: 7 additions & 3 deletions lib/bloc/initialize/initialize_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ class Initial extends InitializeState {}

class CheckingInternetConnection extends InitializeState{}

class Authenticating extends InitializeState{}

class Authenticated extends InitializeState{}

class NoInternetConnection extends InitializeState{}

class Registered extends InitializeState {
Expand All @@ -16,7 +20,7 @@ class Registered extends InitializeState {

class NotRegistered extends InitializeState {}

class InitializeError extends InitializeState{
String message;
InitializeError(this.message);
class Failed extends InitializeState{
final String message;
Failed(this.message);
}
2 changes: 2 additions & 0 deletions lib/bloc/signup/signup_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:developer';

import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
Expand Down Expand Up @@ -93,6 +94,7 @@ class SignupBloc extends Bloc<SignupEvent, SignupState> {
emit(Success(userInfo));
}
catch(error){
log(error.toString());
emit(Failed(error.toString()));
}
});
Expand Down
5 changes: 0 additions & 5 deletions lib/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ class Config{
static const String rpcUrl = "http://192.168.8.163:8545";
static const String wsUrl = "ws://192.168.8.163:8545";

static const String fundTransferAccountPrivateKey = "2ffdcdececb76f1c6ff826cbb4c0138cac3f5f2950540a1ff1a0530cd5f5063f";


// contract details
static const String accountsContractAbiJsonFile = "contracts/build/contracts/Accounts.json";
static const String backendUrl = "http://192.168.8.163:3000";

}
23 changes: 18 additions & 5 deletions lib/pages/opening_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,34 @@ class _OpeningScreenState extends State<OpeningScreen> {
listenWhen: ((previous, current) => previous != current),
listener: (BuildContext context, state) {
if (state is NoInternetConnection) {
Navigator.popAndPushNamed(context, '/error', arguments: {"message": "No internet connection found!"});
Navigator.popAndPushNamed(context, '/error', arguments: {
"message": "No internet connection found!"
});
} else if (state is Registered) {
Navigator.popAndPushNamed(context, '/home', arguments: state.userInfo);
Navigator.popAndPushNamed(context, '/home',
arguments: state.userInfo);
} else if (state is NotRegistered) {
Navigator.popAndPushNamed(context, '/welcome1');
} else if (state is InitializeError){
Navigator.popAndPushNamed(context, '/error', arguments: {"message": state.message});
} else if (state is Failed) {
Navigator.popAndPushNamed(context, '/error',
arguments: {"message": state.message});
}
},
buildWhen: ((previous, current) => previous != current && current is Initial || current is CheckingInternetConnection),
buildWhen: ((previous, current) =>
previous != current &&
(current is Initial ||
current is CheckingInternetConnection ||
current is Authenticating ||
current is Authenticated)),
builder: (context, state) {
if (state is Initial) {
return const Text("Initializing");
} else if (state is CheckingInternetConnection) {
return const Text("Checking for connectivity");
} else if (state is Authenticating) {
return const Text("Authenticating");
} else if (state is Authenticated) {
return const Text("Authenticated");
}
return const Text("Something went wrong!");
},
Expand Down
22 changes: 9 additions & 13 deletions lib/pages/signup_page.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:iblock/widgets/covered_loading.dart';

import '../../bloc/signup/signup_bloc.dart';
import '../widgets/forms/button.dart';
Expand Down Expand Up @@ -67,23 +68,18 @@ class _SignUpPageState extends State<SignUpPage> {
child: BlocListener<SignupBloc, SignupState>(
listener: (context, state) {
if (state is Submitted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Submitting...'),
),
);
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return const CoveredLoading();
});
} else if (state is RecoverySubmitted) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return const Dialog(
backgroundColor: Colors.transparent,
child: Center(
child: SizedBox(
width: 100.0,
height: 100.0,
child: CircularProgressIndicator()),
));
return const CoveredLoading();
});
} else if (state is Success) {
print("Signup succeeded");
Expand Down
17 changes: 17 additions & 0 deletions lib/widgets/covered_loading.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';

class CoveredLoading extends StatelessWidget {
const CoveredLoading({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return const Dialog(
backgroundColor: Colors.transparent,
child: Center(
child: SizedBox(
width: 100.0,
height: 100.0,
child: CircularProgressIndicator()),
));
}
}
42 changes: 42 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
flutter_secure_storage:
dependency: "direct main"
description:
Expand Down Expand Up @@ -436,6 +443,41 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
local_auth:
dependency: "direct main"
description:
name: local_auth
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
local_auth_android:
dependency: transitive
description:
name: local_auth_android
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.15"
local_auth_ios:
dependency: transitive
description:
name: local_auth_ios
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.10"
local_auth_platform_interface:
dependency: transitive
description:
name: local_auth_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
local_auth_windows:
dependency: transitive
description:
name: local_auth_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
logging:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ dependencies:
bip39: ^1.0.6
dart_bip32_bip44: ^0.2.0
fluttertoast: ^8.1.1
local_auth: ^2.1.2

dev_dependencies:
flutter_test:
Expand Down
3 changes: 3 additions & 0 deletions windows/flutter/generated_plugin_registrant.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@

#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <local_auth_windows/local_auth_plugin.h>

void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
LocalAuthPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
}
1 change: 1 addition & 0 deletions windows/flutter/generated_plugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
flutter_secure_storage_windows
local_auth_windows
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
Expand Down

0 comments on commit cae06bb

Please sign in to comment.