Fixed login and error handling

This commit is contained in:
Anton Stubenbord
2022-12-30 01:28:43 +01:00
parent bf0e186646
commit 2326c9d1d6
43 changed files with 502 additions and 447 deletions

View File

@@ -3,22 +3,19 @@ import 'dart:io';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/security/authentication_aware_dio_manager.dart';
import 'package:paperless_mobile/core/store/local_vault.dart';
import 'package:paperless_mobile/features/login/bloc/authentication_state.dart';
import 'package:paperless_mobile/features/login/model/authentication_information.dart';
import 'package:paperless_mobile/features/login/model/client_certificate.dart';
import 'package:paperless_mobile/features/login/model/user_credentials.model.dart';
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
import 'package:paperless_mobile/features/settings/model/application_settings_state.dart';
class AuthenticationCubit extends HydratedCubit<AuthenticationState> {
class AuthenticationCubit extends Cubit<AuthenticationState>
with HydratedMixin<AuthenticationState> {
final LocalAuthenticationService _localAuthService;
final PaperlessAuthenticationApi _authApi;
final LocalVault _localVault;
final AuthenticationAwareDioManager _dioWrapper;
AuthenticationCubit(
this._localVault,
this._localAuthService,
this._authApi,
this._dioWrapper,
@@ -31,6 +28,7 @@ class AuthenticationCubit extends HydratedCubit<AuthenticationState> {
}) async {
assert(credentials.username != null && credentials.password != null);
try {
print(_dioWrapper.client.hashCode);
_dioWrapper.updateSettings(
baseUrl: serverUrl,
clientCertificate: clientCertificate,
@@ -41,19 +39,22 @@ class AuthenticationCubit extends HydratedCubit<AuthenticationState> {
password: credentials.password!,
);
final auth = AuthenticationInformation(
serverUrl: serverUrl,
_dioWrapper.updateSettings(
baseUrl: serverUrl,
clientCertificate: clientCertificate,
token: token,
authToken: token,
);
await _localVault.storeAuthenticationInformation(auth);
emit(AuthenticationState(
isAuthenticated: true,
wasLoginStored: false,
authentication: auth,
));
emit(
AuthenticationState(
wasLoginStored: false,
authentication: AuthenticationInformation(
serverUrl: serverUrl,
clientCertificate: clientCertificate,
token: token,
),
),
);
} on TlsException catch (_) {
const error = PaperlessServerException(
ErrorCode.invalidClientCertificateConfiguration);
@@ -67,59 +68,68 @@ class AuthenticationCubit extends HydratedCubit<AuthenticationState> {
}
}
Future<void> restoreSessionState() async {
final storedAuth = await _localVault.loadAuthenticationInformation();
late ApplicationSettingsState? appSettings;
try {
appSettings = await _localVault.loadApplicationSettings() ??
ApplicationSettingsState.defaultSettings;
} catch (err) {
appSettings = ApplicationSettingsState.defaultSettings;
///
/// Performs a conditional hydration based on the local authentication success.
///
Future<void> restoreSessionState(bool promptForLocalAuthentication) async {
final json = HydratedBloc.storage.read(storageToken);
if (json == null) {
// If there is nothing to restore, we can quit here.
return;
}
if (storedAuth == null || !storedAuth.isValid) {
return emit(
AuthenticationState(isAuthenticated: false, wasLoginStored: false),
);
} else {
if (appSettings.isLocalAuthenticationEnabled) {
final localAuthSuccess = await _localAuthService
.authenticateLocalUser("Authenticate to log back in");
if (localAuthSuccess) {
if (promptForLocalAuthentication) {
final localAuthSuccess = await _localAuthService
.authenticateLocalUser("Authenticate to log back in");
if (localAuthSuccess) {
hydrate();
if (state.isAuthenticated) {
_dioWrapper.updateSettings(
clientCertificate: storedAuth.clientCertificate,
clientCertificate: state.authentication!.clientCertificate,
authToken: state.authentication!.token,
baseUrl: state.authentication!.serverUrl,
);
return emit(
AuthenticationState(
isAuthenticated: true,
wasLoginStored: true,
authentication: storedAuth,
authentication: state.authentication,
wasLocalAuthenticationSuccessful: true,
),
);
} else {
return emit(AuthenticationState(
isAuthenticated: false,
wasLoginStored: true,
wasLocalAuthenticationSuccessful: false,
));
}
} else {
hydrate();
return emit(
AuthenticationState(
wasLoginStored: true,
wasLocalAuthenticationSuccessful: false,
authentication: state.authentication,
),
);
}
} else {
hydrate();
if (state.isAuthenticated) {
_dioWrapper.updateSettings(
clientCertificate: storedAuth.clientCertificate,
clientCertificate: state.authentication!.clientCertificate,
authToken: state.authentication!.token,
baseUrl: state.authentication!.serverUrl,
);
final authState = AuthenticationState(
isAuthenticated: true,
authentication: storedAuth,
authentication: state.authentication!,
wasLoginStored: true,
);
return emit(authState);
} else {
return emit(AuthenticationState.initial);
}
}
}
Future<void> logout() async {
await _localVault.clear();
await super.clear();
await clear();
_dioWrapper.resetSettings();
emit(AuthenticationState.initial);
}

View File

@@ -6,21 +6,20 @@ part 'authentication_state.g.dart';
@JsonSerializable()
class AuthenticationState {
final bool wasLoginStored;
@JsonKey(ignore: true)
final bool? wasLocalAuthenticationSuccessful;
final bool isAuthenticated;
final AuthenticationInformation? authentication;
static final AuthenticationState initial = AuthenticationState(
wasLoginStored: false,
isAuthenticated: false,
);
bool get isAuthenticated => authentication != null;
AuthenticationState({
required this.isAuthenticated,
required this.wasLoginStored,
this.wasLocalAuthenticationSuccessful,
this.authentication,
}) : assert(!isAuthenticated || authentication != null);
});
AuthenticationState copyWith({
bool? wasLoginStored,
@@ -29,7 +28,6 @@ class AuthenticationState {
bool? wasLocalAuthenticationSuccessful,
}) {
return AuthenticationState(
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
wasLoginStored: wasLoginStored ?? this.wasLoginStored,
authentication: authentication ?? this.authentication,
wasLocalAuthenticationSuccessful: wasLocalAuthenticationSuccessful ??

View File

@@ -8,10 +8,7 @@ part of 'authentication_state.dart';
AuthenticationState _$AuthenticationStateFromJson(Map<String, dynamic> json) =>
AuthenticationState(
isAuthenticated: json['isAuthenticated'] as bool,
wasLoginStored: json['wasLoginStored'] as bool,
wasLocalAuthenticationSuccessful:
json['wasLocalAuthenticationSuccessful'] as bool?,
authentication: json['authentication'] == null
? null
: AuthenticationInformation.fromJson(
@@ -22,8 +19,5 @@ Map<String, dynamic> _$AuthenticationStateToJson(
AuthenticationState instance) =>
<String, dynamic>{
'wasLoginStored': instance.wasLoginStored,
'wasLocalAuthenticationSuccessful':
instance.wasLocalAuthenticationSuccessful,
'isAuthenticated': instance.isAuthenticated,
'authentication': instance.authentication,
};

View File

@@ -49,7 +49,7 @@ class _LoginPageState extends State<LoginPage> {
padding: const EdgeInsets.only(top: 16.0),
child: Text(
S.of(context).loginPageAdvancedLabel,
style: Theme.of(context).textTheme.bodyText1,
style: Theme.of(context).textTheme.bodyLarge,
).padded(),
),
),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:paperless_mobile/core/service/connectivity_status.service.dart';
import 'package:paperless_mobile/generated/l10n.dart';
@@ -26,6 +27,10 @@ class _ServerAddressFormFieldState extends State<ServerAddressFormField> {
validator: FormBuilderValidators.required(
errorText: S.of(context).loginPageServerUrlValidatorMessageText,
),
inputFormatters: [
FilteringTextInputFormatter.deny(r".*/$"),
FilteringTextInputFormatter.deny(r"\s"),
],
decoration: InputDecoration(
suffixIcon: _buildIsReachableIcon(),
hintText: "http://192.168.1.50:8000",