feat(authentication): add mock + local storage

This commit is contained in:
Hugo Pointcheval 2023-03-10 15:07:49 +01:00
parent 4d872edc4e
commit 1d3e487d6b
Signed by: hugo
GPG Key ID: 3AAC487E131E00BC
12 changed files with 1228 additions and 3 deletions

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
abstract class AuthStorage {
static const String refreshToken = 'wyattRefreshToken';
static const String accessToken = 'wyattAccessToken';
static const String email = 'wyattEmail';
static const String id = 'wyattId';
}

View File

@ -16,7 +16,9 @@
import 'package:wyatt_architecture/wyatt_architecture.dart';
part 'exceptions_base.dart';
part 'exceptions_firebase.dart';
part 'exceptions_mock.dart';
/// Base exception used in Wyatt Authentication
abstract class AuthenticationFailureInterface extends AppException

View File

@ -0,0 +1,382 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'exceptions.dart';
/// {@macro apply_action_code_failure}
class ApplyActionCodeFailureBase extends ApplyActionCodeFailureInterface {
ApplyActionCodeFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ApplyActionCodeFailureBase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'expired-action-code':
msg = 'Action code has expired.';
break;
case 'invalid-action-code':
msg = 'Action code is invalid.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
msg = 'Email is not found, please create an account.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_up_with_email_and_password_failure}
class SignUpWithEmailAndPasswordFailureBase
extends SignUpWithEmailAndPasswordFailureInterface {
SignUpWithEmailAndPasswordFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignUpWithEmailAndPasswordFailureBase.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
msg = 'The email address is badly formatted.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
case 'email-already-in-use':
msg = 'An account already exists for that email.';
break;
case 'operation-not-allowed':
msg = 'Operation is not allowed. Please contact support.';
break;
case 'weak-password':
msg = 'Please enter a stronger password.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro fetch_sign_in_methods_failure}
class FetchSignInMethodsForEmailFailureBase
extends FetchSignInMethodsForEmailFailureInterface {
FetchSignInMethodsForEmailFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
FetchSignInMethodsForEmailFailureBase.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
msg = 'The email address is badly formatted.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_in_anonymously_failure}
class SignInAnonymouslyFailureBase extends SignInAnonymouslyFailureInterface {
SignInAnonymouslyFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignInAnonymouslyFailureBase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'operation-not-allowed':
msg = 'Operation is not allowed. Please contact support.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_in_with_credential_failure}
class SignInWithCredentialFailureBase
extends SignInWithCredentialFailureInterface {
SignInWithCredentialFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignInWithCredentialFailureBase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'account-exists-with-different-credential':
msg = 'Account exists with different credentials.';
break;
case 'invalid-credential':
msg = 'The credential received is malformed or has expired.';
break;
case 'operation-not-allowed':
msg = 'Operation is not allowed. Please contact support.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
msg = 'Email is not found, please create an account.';
break;
case 'wrong-password':
msg = 'Incorrect password, please try again.';
break;
case 'invalid-verification-code':
msg = 'The credential verification code received is invalid.';
break;
case 'invalid-verification-id':
msg = 'The credential verification ID received is invalid.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_in_with_google_failure}
class SignInWithGoogleFailureBase extends SignInWithCredentialFailureBase
implements SignInWithGoogleFailureInterface {
SignInWithGoogleFailureBase([super.code, super.msg]);
SignInWithGoogleFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_in_with_facebook_failure}
class SignInWithFacebookFailureBase extends SignInWithCredentialFailureBase
implements SignInWithFacebookFailureInterface {
SignInWithFacebookFailureBase([super.code, super.msg]);
SignInWithFacebookFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_in_with_apple_failure}
class SignInWithAppleFailureBase extends SignInWithCredentialFailureBase
implements SignInWithAppleFailureInterface {
SignInWithAppleFailureBase([super.code, super.msg]);
SignInWithAppleFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_in_with_twitter_failure}
class SignInWithTwitterFailureBase extends SignInWithCredentialFailureBase
implements SignInWithAppleFailureInterface {
SignInWithTwitterFailureBase([super.code, super.msg]);
SignInWithTwitterFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_in_with_email_link_failure}
class SignInWithEmailLinkFailureBase
extends SignInWithEmailLinkFailureInterface {
SignInWithEmailLinkFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignInWithEmailLinkFailureBase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'expired-action-code':
msg = 'Action code has expired.';
break;
case 'invalid-email':
msg = 'Email is not valid or badly formatted.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_in_with_email_and_password_failure}
class SignInWithEmailAndPasswordFailureBase
extends SignInWithEmailAndPasswordFailureInterface {
SignInWithEmailAndPasswordFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignInWithEmailAndPasswordFailureBase.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
msg = 'Email is not valid or badly formatted.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
msg = 'Email is not found, please create an account.';
break;
case 'wrong-password':
msg = 'Incorrect password, please try again.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro send_email_verification_failure}
class SendEmailVerificationFailureBase
extends SendEmailVerificationFailureInterface {
SendEmailVerificationFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SendEmailVerificationFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro send_password_reset_email_failure}
class SendPasswordResetEmailFailureBase
extends SendPasswordResetEmailFailureInterface {
SendPasswordResetEmailFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SendPasswordResetEmailFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro send_sign_in_link_email_failure}
class SendSignInLinkEmailFailureBase
extends SendSignInLinkEmailFailureInterface {
SendSignInLinkEmailFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SendSignInLinkEmailFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro confirm_password_reset_failure}
class ConfirmPasswordResetFailureBase
extends ConfirmPasswordResetFailureInterface {
ConfirmPasswordResetFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ConfirmPasswordResetFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro verify_password_reset_code_failure}
class VerifyPasswordResetCodeFailureBase
extends VerifyPasswordResetCodeFailureInterface {
VerifyPasswordResetCodeFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
VerifyPasswordResetCodeFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro refresh_failure}
class RefreshFailureBase extends RefreshFailureInterface {
RefreshFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
RefreshFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_out_failure}
class SignOutFailureBase extends SignOutFailureInterface {
SignOutFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignOutFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro reauthenticate_failure}
class ReauthenticateFailureBase extends ReauthenticateFailureInterface {
ReauthenticateFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ReauthenticateFailureBase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'user-mismatch':
msg = 'Given credential does not correspond to the user.';
break;
case 'user-not-found':
msg = 'User is not found, please create an account.';
break;
case 'invalid-credential':
msg = 'The credential received is malformed or has expired.';
break;
case 'invalid-email':
msg = 'Email is not valid or badly formatted.';
break;
case 'wrong-password':
msg = 'Incorrect password, please try again.';
break;
case 'invalid-verification-code':
msg = 'The credential verification code received is invalid.';
break;
case 'invalid-verification-id':
msg = 'The credential verification ID received is invalid.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro update_email_failure}
class UpdateEmailFailureBase extends UpdateEmailFailureInterface {
UpdateEmailFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
UpdateEmailFailureBase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'invalid-email':
msg = 'Email is not valid or badly formatted.';
break;
case 'email-already-in-use':
msg = 'An account already exists for that email.';
break;
case 'requires-recent-login':
msg = "User's last sign-in time does not meet the security threshold.";
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro update_password_failure}
class UpdatePasswordFailureBase extends UpdatePasswordFailureInterface {
UpdatePasswordFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
UpdatePasswordFailureBase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'weak-password':
msg = 'Please enter a stronger password.';
break;
case 'requires-recent-login':
msg = "User's last sign-in time does not meet the security threshold.";
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro model_parsing_failure}
class ModelParsingFailureBase extends ModelParsingFailureInterface {
ModelParsingFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ModelParsingFailureBase.fromCode(super.code) : super.fromCode();
}
/// {@macro delete_account_failure}
class DeleteAccountFailureBase extends DeleteAccountFailureInterface {
DeleteAccountFailureBase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
DeleteAccountFailureBase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'requires-recent-login':
msg = "User's last sign-in time does not meet the security threshold.";
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}

View File

@ -0,0 +1,390 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'exceptions.dart';
/// {@macro apply_action_code_failure}
class ApplyActionCodeFailureMock extends ApplyActionCodeFailureInterface {
ApplyActionCodeFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ApplyActionCodeFailureMock.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'expired-action-code':
msg = 'Action code has expired.';
break;
case 'invalid-action-code':
msg = 'Action code is invalid.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
msg = 'Email is not found, please create an account.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_up_with_email_and_password_failure}
class SignUpWithEmailAndPasswordFailureMock
extends SignUpWithEmailAndPasswordFailureInterface {
SignUpWithEmailAndPasswordFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignUpWithEmailAndPasswordFailureMock.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
msg = 'The email address is badly formatted.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
case 'email-already-in-use':
msg = 'An account already exists for that email.';
break;
case 'operation-not-allowed':
msg = 'Operation is not allowed. Please contact support.';
break;
case 'weak-password':
msg = 'Please enter a stronger password.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro fetch_sign_in_methods_failure}
class FetchSignInMethodsForEmailFailureMock
extends FetchSignInMethodsForEmailFailureInterface {
FetchSignInMethodsForEmailFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
FetchSignInMethodsForEmailFailureMock.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
msg = 'The email address is badly formatted.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_in_anonymously_failure}
class SignInAnonymouslyFailureMock
extends SignInAnonymouslyFailureInterface {
SignInAnonymouslyFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignInAnonymouslyFailureMock.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'operation-not-allowed':
msg = 'Operation is not allowed. Please contact support.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_in_with_credential_failure}
class SignInWithCredentialFailureMock
extends SignInWithCredentialFailureInterface {
SignInWithCredentialFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignInWithCredentialFailureMock.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'account-exists-with-different-credential':
msg = 'Account exists with different credentials.';
break;
case 'invalid-credential':
msg = 'The credential received is malformed or has expired.';
break;
case 'operation-not-allowed':
msg = 'Operation is not allowed. Please contact support.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
msg = 'Email is not found, please create an account.';
break;
case 'wrong-password':
msg = 'Incorrect password, please try again.';
break;
case 'invalid-verification-code':
msg = 'The credential verification code received is invalid.';
break;
case 'invalid-verification-id':
msg = 'The credential verification ID received is invalid.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_in_with_google_failure}
class SignInWithGoogleFailureMock
extends SignInWithCredentialFailureMock
implements SignInWithGoogleFailureInterface {
SignInWithGoogleFailureMock([super.code, super.msg]);
SignInWithGoogleFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_in_with_facebook_failure}
class SignInWithFacebookFailureMock
extends SignInWithCredentialFailureMock
implements SignInWithFacebookFailureInterface {
SignInWithFacebookFailureMock([super.code, super.msg]);
SignInWithFacebookFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_in_with_apple_failure}
class SignInWithAppleFailureMock extends SignInWithCredentialFailureMock
implements SignInWithAppleFailureInterface {
SignInWithAppleFailureMock([super.code, super.msg]);
SignInWithAppleFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_in_with_twitter_failure}
class SignInWithTwitterFailureMock
extends SignInWithCredentialFailureMock
implements SignInWithAppleFailureInterface {
SignInWithTwitterFailureMock([super.code, super.msg]);
SignInWithTwitterFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_in_with_email_link_failure}
class SignInWithEmailLinkFailureMock
extends SignInWithEmailLinkFailureInterface {
SignInWithEmailLinkFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignInWithEmailLinkFailureMock.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'expired-action-code':
msg = 'Action code has expired.';
break;
case 'invalid-email':
msg = 'Email is not valid or badly formatted.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro sign_in_with_email_and_password_failure}
class SignInWithEmailAndPasswordFailureMock
extends SignInWithEmailAndPasswordFailureInterface {
SignInWithEmailAndPasswordFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignInWithEmailAndPasswordFailureMock.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
msg = 'Email is not valid or badly formatted.';
break;
case 'user-disabled':
msg = 'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
msg = 'Email is not found, please create an account.';
break;
case 'wrong-password':
msg = 'Incorrect password, please try again.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro send_email_verification_failure}
class SendEmailVerificationFailureMock
extends SendEmailVerificationFailureInterface {
SendEmailVerificationFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SendEmailVerificationFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro send_password_reset_email_failure}
class SendPasswordResetEmailFailureMock
extends SendPasswordResetEmailFailureInterface {
SendPasswordResetEmailFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SendPasswordResetEmailFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro send_sign_in_link_email_failure}
class SendSignInLinkEmailFailureMock
extends SendSignInLinkEmailFailureInterface {
SendSignInLinkEmailFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SendSignInLinkEmailFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro confirm_password_reset_failure}
class ConfirmPasswordResetFailureMock
extends ConfirmPasswordResetFailureInterface {
ConfirmPasswordResetFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ConfirmPasswordResetFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro verify_password_reset_code_failure}
class VerifyPasswordResetCodeFailureMock
extends VerifyPasswordResetCodeFailureInterface {
VerifyPasswordResetCodeFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
VerifyPasswordResetCodeFailureMock.fromCode(super.code)
: super.fromCode();
}
/// {@macro refresh_failure}
class RefreshFailureMock extends RefreshFailureInterface {
RefreshFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
RefreshFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro sign_out_failure}
class SignOutFailureMock extends SignOutFailureInterface {
SignOutFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
SignOutFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro reauthenticate_failure}
class ReauthenticateFailureMock extends ReauthenticateFailureInterface {
ReauthenticateFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ReauthenticateFailureMock.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'user-mismatch':
msg = 'Given credential does not correspond to the user.';
break;
case 'user-not-found':
msg = 'User is not found, please create an account.';
break;
case 'invalid-credential':
msg = 'The credential received is malformed or has expired.';
break;
case 'invalid-email':
msg = 'Email is not valid or badly formatted.';
break;
case 'wrong-password':
msg = 'Incorrect password, please try again.';
break;
case 'invalid-verification-code':
msg = 'The credential verification code received is invalid.';
break;
case 'invalid-verification-id':
msg = 'The credential verification ID received is invalid.';
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro update_email_failure}
class UpdateEmailFailureMock extends UpdateEmailFailureInterface {
UpdateEmailFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
UpdateEmailFailureMock.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'invalid-email':
msg = 'Email is not valid or badly formatted.';
break;
case 'email-already-in-use':
msg = 'An account already exists for that email.';
break;
case 'requires-recent-login':
msg = "User's last sign-in time does not meet the security threshold.";
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro update_password_failure}
class UpdatePasswordFailureMock extends UpdatePasswordFailureInterface {
UpdatePasswordFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
UpdatePasswordFailureMock.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'weak-password':
msg = 'Please enter a stronger password.';
break;
case 'requires-recent-login':
msg = "User's last sign-in time does not meet the security threshold.";
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}
/// {@macro model_parsing_failure}
class ModelParsingFailureMock extends ModelParsingFailureInterface {
ModelParsingFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ModelParsingFailureMock.fromCode(super.code) : super.fromCode();
}
/// {@macro delete_account_failure}
class DeleteAccountFailureMock extends DeleteAccountFailureInterface {
DeleteAccountFailureMock([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
DeleteAccountFailureMock.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'requires-recent-login':
msg = "User's last sign-in time does not meet the security threshold.";
break;
default:
this.code = 'unknown';
msg = 'An unknown error occurred.';
}
}
}

View File

@ -0,0 +1,75 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:wyatt_authentication_bloc/src/core/constants/storage.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
/// {@template authentication_secure_storage_cache_data_source_impl}
/// A data source that manages the cache strategy.
/// This implementation uses Secure Storage.
/// {@endtemplate}
class AuthenticationSecureStorageCacheDataSourceImpl<Data>
extends AuthenticationCacheDataSource<Data> {
/// {@macro authentication_secure_storage_cache_data_source_impl}
const AuthenticationSecureStorageCacheDataSourceImpl({
FlutterSecureStorage? secureStorage,
}) : _secureStorage = secureStorage ?? const FlutterSecureStorage();
final FlutterSecureStorage _secureStorage;
@override
Future<void> cacheAccount(Account account) async {
await _secureStorage.write(
key: AuthStorage.id,
value: account.id,
);
await _secureStorage.write(
key: AuthStorage.email,
value: account.email,
);
await _secureStorage.write(
key: AuthStorage.accessToken,
value: account.accessToken,
);
}
@override
Future<Account?> getCachedAccount() async {
final id = await _secureStorage.read(key: AuthStorage.id);
final email = await _secureStorage.read(key: AuthStorage.email);
final accessToken = await _secureStorage.read(key: AuthStorage.accessToken);
if (id == null || email == null || accessToken == null) {
return null;
}
final currentAccount = AccountModel.fromSecureStorage(
id: id,
email: email,
accessToken: accessToken,
);
return currentAccount;
}
@override
Future<void> removeCachedAccount() async {
await _secureStorage.delete(key: AuthStorage.id);
await _secureStorage.delete(key: AuthStorage.email);
await _secureStorage.delete(key: AuthStorage.accessToken);
}
}

View File

@ -15,4 +15,5 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'authentication_firebase_cache_data_source_impl.dart';
export 'authentication_secure_storage_cache_data_source_impl.dart';
export 'authentication_session_data_source_impl.dart';

View File

@ -0,0 +1,106 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
/// {@template authentication_base_data_source}
/// Base class for custom [AuthenticationRemoteDataSource] implementations.
/// It implements all methods as throwing [UnimplementedError]s.
/// {@endtemplate}
class AuthenticationBaseDataSource<Data>
extends AuthenticationRemoteDataSource<Data> {
/// {@macro authentication_base_data_source}
const AuthenticationBaseDataSource();
@override
Future<void> confirmPasswordReset({
required String code,
required String newPassword,
}) {
throw UnimplementedError();
}
@override
Future<void> delete() {
throw UnimplementedError();
}
@override
Future<Account> reauthenticate() {
throw UnimplementedError();
}
@override
Future<Account> refresh() {
throw UnimplementedError();
}
@override
Future<void> sendEmailVerification() {
throw UnimplementedError();
}
@override
Future<void> sendPasswordResetEmail({required String email}) {
throw UnimplementedError();
}
@override
Future<Account> signInAnonymously() {
throw UnimplementedError();
}
@override
Future<Account> signInWithEmailAndPassword({
required String email,
required String password,
}) {
throw UnimplementedError();
}
@override
Future<Account> signInWithGoogle() {
throw UnimplementedError();
}
@override
Future<void> signOut() {
throw UnimplementedError();
}
@override
Future<Account> signUpWithEmailAndPassword({
required String email,
required String password,
}) {
throw UnimplementedError();
}
@override
Future<Account> updateEmail({required String email}) {
throw UnimplementedError();
}
@override
Future<Account> updatePassword({required String password}) {
throw UnimplementedError();
}
@override
Future<bool> verifyPasswordResetCode({required String code}) {
throw UnimplementedError();
}
}

View File

@ -0,0 +1,201 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:math';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
/// {@template authentication_mock_data_source_impl}
/// Implementation of [AuthenticationRemoteDataSource] using Mocks.
/// {@endtemplate}
class AuthenticationMockDataSourceImpl<Data>
extends AuthenticationRemoteDataSource<Data> {
/// {@macro authentication_mock_data_source_impl}
AuthenticationMockDataSourceImpl({
this.mockData = const {
'test@test.fr': Pair('test12', '1'),
'alice@test.fr': Pair('alice12', '2'),
'bob@test.fr': Pair('bob1234', '3'),
},
this.accessToken = 'mock_access_token',
});
/// Mock data.
/// Format: <email, <password, id>>
final Map<String, Pair<String, String>> mockData;
/// Current user.
Account? _currentUser;
/// Access token.
final String accessToken;
/// A random delay to simulate network latency.
Future<void> _randomDelay() async {
await Future<void>.delayed(
Duration(milliseconds: Random().nextInt(400) + 200),
);
return;
}
// SignUp/SignIn methods ====================================================
/// {@macro signup_pwd}
@override
Future<Account> signUpWithEmailAndPassword({
required String email,
required String password,
}) async {
await _randomDelay();
mockData[email] = Pair(password, '4');
return _currentUser = Account(
id: '4',
isAnonymous: false,
emailVerified: false,
providerId: 'mock',
email: email,
accessToken: accessToken,
);
}
/// {@macro signin_pwd}
@override
Future<Account> signInWithEmailAndPassword({
required String email,
required String password,
}) async {
await _randomDelay();
final pair = mockData[email];
if (pair == null || pair.left != password) {
throw SignInWithEmailAndPasswordFailureMock();
}
return _currentUser = Account(
id: pair.right!,
isAnonymous: false,
emailVerified: true,
providerId: 'mock',
email: email,
accessToken: accessToken,
);
}
/// {@macro signin_anom}
@override
Future<Account> signInAnonymously() async {
await _randomDelay();
return _currentUser = Account(
id: '5',
isAnonymous: true,
emailVerified: false,
providerId: 'mock',
accessToken: accessToken,
);
}
/// {@macro signin_google}
@override
Future<Account> signInWithGoogle() async {
await _randomDelay();
return _currentUser = Account(
id: '6',
isAnonymous: false,
emailVerified: true,
providerId: 'google.com',
email: 'mock@gmail.com',
accessToken: accessToken,
);
}
/// {@macro signout}
@override
Future<void> signOut() async {
await _randomDelay();
_currentUser = null;
}
// Account management methods ===============================================
/// {@macro refresh}
@override
Future<Account> refresh() async {
await _randomDelay();
if (_currentUser == null) {
throw RefreshFailureMock();
}
return _currentUser!;
}
/// {@macro reauthenticate}
@override
Future<Account> reauthenticate() async {
await _randomDelay();
if (_currentUser == null) {
throw ReauthenticateFailureMock();
}
return _currentUser!;
}
/// {@macro update_email}
@override
Future<Account> updateEmail({required String email}) async {
throw UnimplementedError();
}
/// {@macro update_password}
@override
Future<Account> updatePassword({required String password}) async {
throw UnimplementedError();
}
/// {@macro delete}
@override
Future<void> delete() async {
throw UnimplementedError();
}
// Email related stuff ======================================================
/// {@macro send_email_verification}
@override
Future<void> sendEmailVerification() async {
throw UnimplementedError();
}
/// {@macro send_password_reset_email}
@override
Future<void> sendPasswordResetEmail({required String email}) async {
throw UnimplementedError();
}
/// {@macro confirm_password_reset}
@override
Future<void> confirmPasswordReset({
required String code,
required String newPassword,
}) async {
throw UnimplementedError();
}
/// {@macro verify_password_reset_code}
@override
Future<bool> verifyPasswordResetCode({required String code}) async {
throw UnimplementedError();
}
}

View File

@ -14,4 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'authentication_base_data_source.dart';
export 'authentication_firebase_data_source_impl.dart';
export 'authentication_mock_data_source_impl.dart';

View File

@ -77,6 +77,30 @@ class AccountModel extends Account {
}
}
/// {@macro account_model}
factory AccountModel.fromSecureStorage({
required String? id,
required String? email,
required String? accessToken,
}) {
if (id != null && email != null && accessToken != null) {
return AccountModel._(
user: null,
id: id,
emailVerified: false,
isAnonymous: false,
providerId: '',
email: email,
accessToken: accessToken,
);
} else {
throw Exception(
'null-id-email-access-token'
'id, email, and accessToken cannot be null',
);
}
}
const AccountModel._({
required this.user,
required super.id,

View File

@ -16,8 +16,6 @@
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_authentication_bloc/src/core/utils/forms.dart';
import 'package:wyatt_authentication_bloc/src/domain/data_sources/local/authentication_cache_data_source.dart';
import 'package:wyatt_authentication_bloc/src/domain/data_sources/local/authentication_session_data_source.dart';
import 'package:wyatt_authentication_bloc/src/domain/domain.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
@ -152,6 +150,8 @@ class AuthenticationRepositoryImpl<Data extends Object>
email: email,
password: password,
);
// Cache the account
await authenticationCacheDataSource.cacheAccount(account);
return account;
},
(error) => error,
@ -170,6 +170,8 @@ class AuthenticationRepositoryImpl<Data extends Object>
email: email,
password: password,
);
// Cache the account
await authenticationCacheDataSource.cacheAccount(account);
return account;
},
(error) => error,
@ -194,6 +196,8 @@ class AuthenticationRepositoryImpl<Data extends Object>
() async {
final account =
await authenticationRemoteDataSource.signInWithGoogle();
// Cache the account
await authenticationCacheDataSource.cacheAccount(account);
return account;
},
(error) => error,
@ -205,6 +209,8 @@ class AuthenticationRepositoryImpl<Data extends Object>
Result.tryCatchAsync<void, AppException, AppException>(
() async {
await authenticationRemoteDataSource.signOut();
// Remove the cached account
await authenticationCacheDataSource.removeCachedAccount();
},
(error) => error,
);
@ -217,6 +223,8 @@ class AuthenticationRepositoryImpl<Data extends Object>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account = await authenticationRemoteDataSource.refresh();
// Cache the account
await authenticationCacheDataSource.cacheAccount(account);
return account;
},
(error) => error,
@ -228,6 +236,8 @@ class AuthenticationRepositoryImpl<Data extends Object>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account = await authenticationRemoteDataSource.reauthenticate();
// Cache the account
await authenticationCacheDataSource.cacheAccount(account);
return account;
},
(error) => error,
@ -240,6 +250,8 @@ class AuthenticationRepositoryImpl<Data extends Object>
() async {
final account =
await authenticationRemoteDataSource.updateEmail(email: email);
// Cache the account
await authenticationCacheDataSource.cacheAccount(account);
return account;
},
(error) => error,
@ -253,6 +265,8 @@ class AuthenticationRepositoryImpl<Data extends Object>
final account = await authenticationRemoteDataSource.updatePassword(
password: password,
);
// Cache the account
await authenticationCacheDataSource.cacheAccount(account);
return account;
},
(error) => error,
@ -262,7 +276,11 @@ class AuthenticationRepositoryImpl<Data extends Object>
@override
FutureOrResult<void> delete() =>
Result.tryCatchAsync<void, AppException, AppException>(
() async => authenticationRemoteDataSource.delete(),
() async {
await authenticationRemoteDataSource.delete();
// Remove the cached account
await authenticationCacheDataSource.removeCachedAccount();
},
(error) => error,
);

View File

@ -29,6 +29,8 @@ dependencies:
wyatt_type_utils:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: ^0.0.4
flutter_secure_storage: ^8.0.0
http: ^0.13.5
dev_dependencies:
flutter_test: { sdk: flutter }