Skip to content

Commit

Permalink
Chore/authenticator password validation (#5078)
Browse files Browse the repository at this point in the history
* chore(test): Removed linter ignore

* chore(authenticator): cleaned up extra import

* chore(authenticator): Updated password policy validator to use a type not affiliated with config

* chore(authenticator): updated AmplifyConfig references to AmplifyOutputs
  • Loading branch information
tyllark authored Jun 26, 2024
1 parent e8c0f95 commit d342087
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 50 deletions.
4 changes: 2 additions & 2 deletions packages/authenticator/amplify_authenticator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter/material.dart';
import 'amplifyconfiguration.dart';
import 'amplify_outputs.dart';
void main() {
runApp(const MyApp());
Expand All @@ -50,7 +50,7 @@ class _MyAppState extends State<MyApp> {
Future<void> _configureAmplify() async {
try {
await Amplify.addPlugin(AmplifyAuthCognito());
await Amplify.configure(amplifyconfig);
await Amplify.configure(amplifyConfig);
} on Exception catch (e) {
print('Could not configure Amplify: $e');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import 'package:amplify_authenticator/src/utils/dial_code.dart';
import 'package:amplify_authenticator/src/utils/dial_code_options.dart';
import 'package:amplify_authenticator/src/widgets/authenticator_banner.dart';
import 'package:amplify_authenticator/src/widgets/form.dart';
import 'package:amplify_core/amplify_core.dart' as core;
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
Expand All @@ -42,8 +41,6 @@ export 'package:amplify_auth_cognito/amplify_auth_cognito.dart'
export 'package:amplify_authenticator/src/utils/dial_code.dart' show DialCode;
export 'package:amplify_authenticator/src/utils/dial_code_options.dart'
show DialCodeOptions;
export 'package:amplify_flutter/amplify_flutter.dart'
show PasswordProtectionSettings, PasswordPolicyCharacters;

export 'src/enums/enums.dart' show AuthenticatorStep, Gender;
export 'src/l10n/auth_strings_resolver.dart' hide ButtonResolverKeyType;
Expand Down Expand Up @@ -108,7 +105,7 @@ export 'src/widgets/form_field.dart'
/// import 'package:amplify_flutter/amplify_flutter.dart';
/// import 'package:flutter/material.dart';
///
/// import 'amplifyconfiguration.dart';
/// import 'amplify_outputs.dart';
///
/// void main() {
/// runApp(const MyApp());
Expand All @@ -131,7 +128,7 @@ export 'src/widgets/form_field.dart'
/// Future<void> _configureAmplify() async {
/// try {
/// await Amplify.addPlugin(AmplifyAuthCognito());
/// await Amplify.configure(amplifyconfig);
/// await Amplify.configure(amplifyConfig);
/// } on Exception catch (e) {
/// print('Could not configure Amplify: $e');
/// }
Expand Down Expand Up @@ -178,7 +175,7 @@ export 'src/widgets/form_field.dart'
/// ### Forms
///
/// The Authenticator uses your app's Cognito configuration (as defined in your
/// `amplifyconfiguration.dart` file) to determine which fields are required.
/// `amplify_outputs.dart` file) to determine which fields are required.
/// However, you may optionally add on additional fields using a custom form
/// component. For example, to collect your user's address information on the s
/// ign up form, use the [SignUpForm.custom] constructor:
Expand Down Expand Up @@ -644,20 +641,20 @@ class _AuthenticatorState extends State<Authenticator> {
});
}

List<String> missingConfigValues(core.AmplifyOutputs? config) {
List<String> missingConfigValues(AmplifyOutputs? config) {
final missingValues = <String>[];
final cognitoPlugin = config?.auth;
if (cognitoPlugin == null) {
return const ['auth.plugins.Auth.Default'];
return const ['auth'];
}
if (cognitoPlugin.usernameAttributes == null) {
missingValues.add('usernameAttributes');
missingValues.add('username_attributes');
}
if (cognitoPlugin.standardRequiredAttributes == null) {
missingValues.add('signupAttributes');
missingValues.add('standard_required_attributes');
}
if (cognitoPlugin.passwordPolicy == null) {
missingValues.add('passwordProtectionSettings');
missingValues.add('password_policy');
}
return missingValues;
}
Expand All @@ -670,7 +667,7 @@ class _AuthenticatorState extends State<Authenticator> {
'Encountered problem(s) building the Authenticator due to an invalid config. See below for more info.',
),
ErrorDescription(
'\nYour amplifyconfiguration.dart file is missing the following required attributes:'
'\nYour amplifyconfiguration.dart or amplify_outputs.dart file is missing the following required attributes:'
'\n - ${_missingConfigValues.join('\n - ')}',
),
ErrorDescription(
Expand All @@ -681,7 +678,7 @@ class _AuthenticatorState extends State<Authenticator> {
'\nPlease refer to the amplify flutter documentation for more info on how to resolve this and the full list of required attributes.',
),
ErrorDescription(
'\nOnce you have added the missing values to your amplifyconfiguration.dart file, you will need to restart your app.',
'\nOnce you have added the missing values to your amplifyconfiguration.dart or amplify_outputs.dart file, you will need to restart your app.',
),
]);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:amplify_authenticator/src/l10n/authenticator_localizations.dart';
import 'package:amplify_authenticator/src/l10n/resolver.dart';
import 'package:amplify_authenticator/src/utils/unmet_password_requirements.dart';
import 'package:flutter/material.dart';

enum InputField {
Expand Down Expand Up @@ -50,7 +50,7 @@ enum InputResolverKeyType {

class InputResolverKey {
const InputResolverKey.passwordRequirementsUnmet(
PasswordProtectionSettings requirements,
UnmetPasswordRequirements requirements,
) : this._(
InputResolverKeyType.passwordRequirements,
field: InputField.password,
Expand All @@ -64,7 +64,7 @@ class InputResolverKey {

final InputResolverKeyType type;
final InputField field;
final PasswordProtectionSettings? unmetPasswordRequirements;
final UnmetPasswordRequirements? unmetPasswordRequirements;

static const usernameTitle = InputResolverKey._(
InputResolverKeyType.title,
Expand Down Expand Up @@ -502,10 +502,10 @@ class InputResolver extends Resolver<InputResolverKey> {
/// defined in the amplify configuration.
String passwordRequires(
BuildContext context,
PasswordProtectionSettings requirements,
UnmetPasswordRequirements requirements,
) {
final minLength = requirements.passwordPolicyMinLength;
final characterReqs = requirements.passwordPolicyCharacters;
final minLength = requirements.minLength;
final characterReqs = requirements.characterRequirements;
if (minLength == null && (characterReqs.isEmpty)) {
return '';
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class UnmetPasswordRequirements {
UnmetPasswordRequirements({
this.minLength,
this.characterRequirements = const [],
});

int? minLength;
List<CharacterRequirements> characterRequirements;
}

enum CharacterRequirements {
requiresLowercase,
requiresUppercase,
requiresNumbers,
requiresSymbols,
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:amplify_authenticator/src/utils/unmet_password_requirements.dart';
import 'package:amplify_core/amplify_core.dart';
// ignore: implementation_imports
import 'package:amplify_core/src/config/amplify_outputs/auth/password_policy.dart';
Expand Down Expand Up @@ -59,22 +60,6 @@ FormFieldValidator<String> usernameValidator({
};
}

extension PasswordPolicyCharactersX on PasswordPolicyCharacters {
@visibleForTesting
bool meetsRequirement(String value) {
switch (this) {
case PasswordPolicyCharacters.requiresLowercase:
return value.contains(_lowercase);
case PasswordPolicyCharacters.requiresUppercase:
return value.contains(_uppercase);
case PasswordPolicyCharacters.requiresNumbers:
return value.contains(_numeric);
case PasswordPolicyCharacters.requiresSymbols:
return value.contains(_symbols);
}
}
}

FormFieldValidator<String> Function(BuildContext) validateNewPassword({
required AmplifyOutputs? amplifyOutputs,
required InputResolver inputResolver,
Expand All @@ -96,38 +81,38 @@ FormFieldValidator<String> Function(BuildContext) validateNewPassword({
final meetsMinLengthRequirement =
minLength == null || password.length >= minLength;

final unmetReqs = _getUnmetPasswordPolicies(password, passwordPolicies);
final unmetCharacterReqs =
_getUnmetCharacterRequirements(password, passwordPolicies);

final error = inputResolver.resolve(
context,
InputResolverKey.passwordRequirementsUnmet(
PasswordProtectionSettings(
passwordPolicyMinLength:
meetsMinLengthRequirement ? null : minLength,
passwordPolicyCharacters: unmetReqs,
UnmetPasswordRequirements(
minLength: meetsMinLengthRequirement ? null : minLength,
characterRequirements: unmetCharacterReqs,
),
),
);
return error.isEmpty ? null : error;
};
}

List<PasswordPolicyCharacters> _getUnmetPasswordPolicies(
List<CharacterRequirements> _getUnmetCharacterRequirements(
String password,
PasswordPolicy? policy,
) {
final unmetReqs = <PasswordPolicyCharacters>[];
final unmetReqs = <CharacterRequirements>[];
if ((policy?.requireLowercase ?? false) && !password.contains(_lowercase)) {
unmetReqs.add(PasswordPolicyCharacters.requiresLowercase);
unmetReqs.add(CharacterRequirements.requiresLowercase);
}
if ((policy?.requireUppercase ?? false) && !password.contains(_uppercase)) {
unmetReqs.add(PasswordPolicyCharacters.requiresUppercase);
unmetReqs.add(CharacterRequirements.requiresUppercase);
}
if ((policy?.requireNumbers ?? false) && !password.contains(_numeric)) {
unmetReqs.add(PasswordPolicyCharacters.requiresNumbers);
unmetReqs.add(CharacterRequirements.requiresNumbers);
}
if ((policy?.requireSymbols ?? false) && !password.contains(_symbols)) {
unmetReqs.add(PasswordPolicyCharacters.requiresSymbols);
unmetReqs.add(CharacterRequirements.requiresSymbols);
}
return unmetReqs;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// ignore_for_file: invalid_use_of_internal_member

import 'dart:async';
import 'dart:convert';

Expand Down

0 comments on commit d342087

Please sign in to comment.