2FA
This commit is contained in:
parent
878aea966f
commit
6c7f8902e1
|
|
@ -18,12 +18,14 @@ class AuthRepository {
|
|||
String password,
|
||||
String url,
|
||||
String secret,
|
||||
String platform}) async {
|
||||
String platform,
|
||||
String oneTimePassword}) async {
|
||||
final credentials = {
|
||||
'token_name': 'invoice-ninja-$platform-app',
|
||||
'api_secret': secret,
|
||||
'email': email,
|
||||
'password': password,
|
||||
'one_time_password': oneTimePassword,
|
||||
};
|
||||
|
||||
url = formatApiUrlMachine(url) + '/login';
|
||||
|
|
|
|||
|
|
@ -5,15 +5,19 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
|||
|
||||
class LoadStateRequest {
|
||||
final BuildContext context;
|
||||
|
||||
LoadStateRequest(this.context);
|
||||
}
|
||||
|
||||
class LoadStateSuccess {
|
||||
final AppState state;
|
||||
|
||||
LoadStateSuccess(this.state);
|
||||
}
|
||||
|
||||
class LoadUserLogin {
|
||||
final BuildContext context;
|
||||
|
||||
LoadUserLogin(this.context);
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +35,9 @@ class OAuthLoginRequest implements StartLoading {
|
|||
final String url;
|
||||
final String secret;
|
||||
final String platform;
|
||||
OAuthLoginRequest({this.completer, this.token, this.url, this.secret, this.platform});
|
||||
|
||||
OAuthLoginRequest(
|
||||
{this.completer, this.token, this.url, this.secret, this.platform});
|
||||
}
|
||||
|
||||
class UserLoginRequest implements StartLoading {
|
||||
|
|
@ -41,8 +47,16 @@ class UserLoginRequest implements StartLoading {
|
|||
final String url;
|
||||
final String secret;
|
||||
final String platform;
|
||||
final String oneTimePassword;
|
||||
|
||||
UserLoginRequest({this.completer, this.email, this.password, this.url, this.secret, this.platform});
|
||||
UserLoginRequest(
|
||||
{this.completer,
|
||||
this.email,
|
||||
this.password,
|
||||
this.url,
|
||||
this.secret,
|
||||
this.platform,
|
||||
this.oneTimePassword});
|
||||
}
|
||||
|
||||
class UserLoginSuccess implements StopLoading {}
|
||||
|
|
@ -54,4 +68,3 @@ class UserLoginFailure implements StopLoading {
|
|||
}
|
||||
|
||||
class UserLogout implements PersistData {}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ Middleware<AppState> _createLoginRequest(AuthRepository repository) {
|
|||
password: action.password,
|
||||
url: action.url,
|
||||
secret: action.secret,
|
||||
platform: action.platform)
|
||||
platform: action.platform,
|
||||
oneTimePassword: action.oneTimePassword)
|
||||
.then((data) {
|
||||
_saveAuthLocal(action);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,15 +26,16 @@ class _LoginState extends State<LoginView> {
|
|||
final _passwordController = TextEditingController();
|
||||
final _urlController = TextEditingController();
|
||||
final _secretController = TextEditingController();
|
||||
final _oneTimePasswordController = TextEditingController();
|
||||
|
||||
static final ValueKey _emailKey = Key(LoginKeys.emailKeyString);
|
||||
static final ValueKey _passwordKey = Key(LoginKeys.passwordKeyString);
|
||||
static final ValueKey _urlKey = Key(LoginKeys.urlKeyString);
|
||||
static final ValueKey _secretKey = Key(LoginKeys.secretKeyString);
|
||||
static final ValueKey _oneTimePasswordKey =
|
||||
Key(LoginKeys.oneTimePasswordKeyString);
|
||||
|
||||
FocusNode focusNode1 = new FocusNode();
|
||||
FocusNode focusNode2 = new FocusNode();
|
||||
FocusNode focusNode3 = new FocusNode();
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
|
|
@ -61,6 +62,7 @@ class _LoginState extends State<LoginView> {
|
|||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
final viewModel = widget.viewModel;
|
||||
final error = viewModel.authState.error;
|
||||
|
||||
if (!viewModel.authState.isInitialized) {
|
||||
return Container();
|
||||
|
|
@ -78,53 +80,64 @@ class _LoginState extends State<LoginView> {
|
|||
key: _formKey,
|
||||
child: FormCard(
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
key: _emailKey,
|
||||
autocorrect: false,
|
||||
decoration: InputDecoration(labelText: localization.email),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
validator: (val) => val.isEmpty || val.trim().isEmpty
|
||||
? localization.pleaseEnterYourEmail
|
||||
: null,
|
||||
onFieldSubmitted: (String value) =>
|
||||
FocusScope.of(context).requestFocus(focusNode1),
|
||||
),
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
key: _passwordKey,
|
||||
autocorrect: false,
|
||||
decoration: InputDecoration(labelText: localization.password),
|
||||
validator: (val) => val.isEmpty || val.trim().isEmpty
|
||||
? localization.pleaseEnterYourPassword
|
||||
: null,
|
||||
obscureText: true,
|
||||
focusNode: focusNode1,
|
||||
onFieldSubmitted: (String value) =>
|
||||
FocusScope.of(context).requestFocus(focusNode2),
|
||||
),
|
||||
TextFormField(
|
||||
controller: _urlController,
|
||||
key: _urlKey,
|
||||
autocorrect: false,
|
||||
decoration: InputDecoration(labelText: localization.url),
|
||||
validator: (val) => val.isEmpty || val.trim().isEmpty
|
||||
? localization.pleaseEnterYourUrl
|
||||
: null,
|
||||
keyboardType: TextInputType.url,
|
||||
focusNode: focusNode2,
|
||||
onFieldSubmitted: (String value) =>
|
||||
FocusScope.of(context).requestFocus(focusNode3),
|
||||
),
|
||||
TextFormField(
|
||||
controller: _secretController,
|
||||
key: _secretKey,
|
||||
autocorrect: false,
|
||||
decoration: InputDecoration(labelText: localization.secret),
|
||||
obscureText: true,
|
||||
focusNode: focusNode3,
|
||||
),
|
||||
viewModel.authState.error == null
|
||||
(error != null && error.contains('2FA')) ||
|
||||
_oneTimePasswordController.text.isNotEmpty
|
||||
? TextFormField(
|
||||
controller: _oneTimePasswordController,
|
||||
key: _oneTimePasswordKey,
|
||||
autocorrect: false,
|
||||
decoration: InputDecoration(
|
||||
labelText: localization.oneTimePassword),
|
||||
)
|
||||
: Column(
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
key: _emailKey,
|
||||
autocorrect: false,
|
||||
decoration:
|
||||
InputDecoration(labelText: localization.email),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
validator: (val) => val.isEmpty || val.trim().isEmpty
|
||||
? localization.pleaseEnterYourEmail
|
||||
: null,
|
||||
onFieldSubmitted: (String value) =>
|
||||
FocusScope.of(context).requestFocus(focusNode1),
|
||||
),
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
key: _passwordKey,
|
||||
autocorrect: false,
|
||||
decoration:
|
||||
InputDecoration(labelText: localization.password),
|
||||
validator: (val) => val.isEmpty || val.trim().isEmpty
|
||||
? localization.pleaseEnterYourPassword
|
||||
: null,
|
||||
obscureText: true,
|
||||
focusNode: focusNode1,
|
||||
),
|
||||
TextFormField(
|
||||
controller: _urlController,
|
||||
key: _urlKey,
|
||||
autocorrect: false,
|
||||
decoration:
|
||||
InputDecoration(labelText: localization.url),
|
||||
validator: (val) => val.isEmpty || val.trim().isEmpty
|
||||
? localization.pleaseEnterYourUrl
|
||||
: null,
|
||||
keyboardType: TextInputType.url,
|
||||
),
|
||||
TextFormField(
|
||||
controller: _secretController,
|
||||
key: _secretKey,
|
||||
autocorrect: false,
|
||||
decoration:
|
||||
InputDecoration(labelText: localization.secret),
|
||||
obscureText: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
viewModel.authState.error == null || error.contains('2FA')
|
||||
? Container()
|
||||
: Container(
|
||||
padding: EdgeInsets.only(top: 26.0, bottom: 4.0),
|
||||
|
|
@ -148,12 +161,12 @@ class _LoginState extends State<LoginView> {
|
|||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
viewModel.onLoginPressed(
|
||||
context,
|
||||
_emailController.text,
|
||||
_passwordController.text,
|
||||
_urlController.text,
|
||||
_secretController.text);
|
||||
viewModel.onLoginPressed(context,
|
||||
email: _emailController.text,
|
||||
password: _passwordController.text,
|
||||
url: _urlController.text,
|
||||
secret: _secretController.text,
|
||||
oneTimePassword: _oneTimePasswordController.text);
|
||||
},
|
||||
),
|
||||
/*
|
||||
|
|
@ -9,7 +9,7 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
|
|||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/auth/login.dart';
|
||||
import 'package:invoiceninja_flutter/ui/auth/login_view.dart';
|
||||
import 'package:invoiceninja_flutter/redux/auth/auth_state.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
|
||||
|
|
@ -36,7 +36,12 @@ class LoginScreen extends StatelessWidget {
|
|||
class LoginVM {
|
||||
bool isLoading;
|
||||
AuthState authState;
|
||||
final Function(BuildContext, String, String, String, String) onLoginPressed;
|
||||
final Function(BuildContext,
|
||||
{String email,
|
||||
String password,
|
||||
String url,
|
||||
String secret,
|
||||
String oneTimePassword}) onLoginPressed;
|
||||
final Function(BuildContext, String, String) onGoogleLoginPressed;
|
||||
|
||||
LoginVM({
|
||||
|
|
@ -83,8 +88,12 @@ class LoginVM {
|
|||
print(error);
|
||||
}
|
||||
},
|
||||
onLoginPressed: (BuildContext context, String email, String password,
|
||||
String url, String secret) async {
|
||||
onLoginPressed: (BuildContext context,
|
||||
{String email,
|
||||
String password,
|
||||
String url,
|
||||
String secret,
|
||||
String oneTimePassword}) async {
|
||||
if (store.state.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -97,6 +106,7 @@ class LoginVM {
|
|||
url: url.trim(),
|
||||
secret: secret.trim(),
|
||||
platform: getPlatform(context),
|
||||
oneTimePassword: oneTimePassword.trim(),
|
||||
));
|
||||
completer.future.then((_) => _handleLogin(context));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ class LoginKeys {
|
|||
static const String passwordKeyString = 'loginPassword';
|
||||
static const String urlKeyString = 'loginUrl';
|
||||
static const String secretKeyString = 'loginSecret';
|
||||
static const String oneTimePasswordKeyString = 'loginOneTimePassword';
|
||||
}
|
||||
|
||||
// Keys for Product Screen
|
||||
|
|
|
|||
|
|
@ -262,6 +262,7 @@ class AppLocalization {
|
|||
'activity_45': ':user deleted task :task',
|
||||
'activity_46': ':user restored task :task',
|
||||
'activity_47': ':user updated expense :expense',
|
||||
'one_time_password': 'One Time Password',
|
||||
},
|
||||
'sq': {
|
||||
'billing_address': 'Adresa e faturimit',
|
||||
|
|
@ -7658,6 +7659,9 @@ class AppLocalization {
|
|||
String get activity_47 =>
|
||||
_localizedValues[locale.languageCode]['activity_47'];
|
||||
|
||||
String get oneTimePassword =>
|
||||
_localizedValues[locale.languageCode]['one_time_password'];
|
||||
|
||||
String lookup(String key) {
|
||||
return _localizedValues[locale.languageCode][toSnakeCase(key)] ?? key;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
import 'package:test/test.dart';
|
||||
//import 'package:invoiceninja_flutter/ui/auth/login.dart';
|
||||
//import 'package:invoiceninja_flutter/ui/auth/login_view.dart';
|
||||
|
||||
void main() {
|
||||
group('scrolling performance test', () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue