Last active
March 17, 2026 12:30
-
-
Save PlugFox/b6aa1d256e6900424c74aa75136c9aef to your computer and use it in GitHub Desktop.
Password validation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Password validation | |
| * https://gist.github.com/PlugFox/b6aa1d256e6900424c74aa75136c9aef | |
| * https://dartpad.dev?id=b6aa1d256e6900424c74aa75136c9aef | |
| * Mike Matiunin <plugfox@gmail.com>, 17 March 2026 | |
| */ | |
| // ignore_for_file: curly_braces_in_flow_control_structures | |
| void main() { | |
| print(validatePasswords('Aaaa1234', 'Aaaa1234').isValid); | |
| print(validatePasswords('Aaaa1234', 'baaa1234').isValid); | |
| } | |
| /// {@template password_validation_issues} | |
| /// A bitmask representing password validation issues for new password creation. | |
| /// Use [validatePasswords] to obtain this value. | |
| /// {@endtemplate} | |
| extension type const PasswordValidationIssues(int value) implements int { | |
| /// No issues — password passes all validation rules. | |
| static const PasswordValidationIssues none = PasswordValidationIssues(0); | |
| /// Password length is not between 8 and 128 characters. | |
| static const PasswordValidationIssues length = PasswordValidationIssues( | |
| 1 << 0, | |
| ); | |
| /// Password does not contain at least one digit. | |
| static const PasswordValidationIssues number = PasswordValidationIssues( | |
| 1 << 1, | |
| ); | |
| /// Password does not contain at least one uppercase letter. | |
| static const PasswordValidationIssues uppercase = PasswordValidationIssues( | |
| 1 << 2, | |
| ); | |
| /// Password and confirmation do not match. | |
| static const PasswordValidationIssues mismatch = PasswordValidationIssues( | |
| 1 << 3, | |
| ); | |
| /// Check if the issues contain a specific flag. | |
| bool contains(PasswordValidationIssues flag) => (value & flag.value) != 0; | |
| /// Add an issue flag. | |
| PasswordValidationIssues add(PasswordValidationIssues flag) => | |
| PasswordValidationIssues(value | flag.value); | |
| /// Remove an issue flag. | |
| PasswordValidationIssues remove(PasswordValidationIssues flag) => | |
| PasswordValidationIssues(value & ~flag.value); | |
| /// Whether the password length is not between 8 and 128 characters. | |
| bool get hasLengthIssue => contains(length); | |
| /// Whether the password does not contain at least one digit. | |
| bool get hasNumberIssue => contains(number); | |
| /// Whether the password does not contain at least one uppercase letter. | |
| bool get hasUppercaseIssue => contains(uppercase); | |
| /// Whether the password and confirmation do not match. | |
| bool get hasMismatchIssue => contains(mismatch); | |
| /// Check if the password is valid (no issues). | |
| bool get isValid => value == 0; | |
| /// Check if the password is not valid (has issues). | |
| bool get isNotValid => value != 0; | |
| } | |
| /// Validates a new password against creation rules and returns a bitmask of issues. | |
| /// This method is only for password creation (sign-up), not for login. | |
| /// | |
| /// Performs a single pass over the password string, starting with all issues set | |
| /// and removing flags as conditions are met. | |
| PasswordValidationIssues validatePasswords(String password, String confirm) { | |
| // Start with all character-level issues set; remove as we find matches. | |
| var issues = const PasswordValidationIssues( | |
| PasswordValidationIssues.number | PasswordValidationIssues.uppercase, | |
| ); | |
| for (final r in password.runes) { | |
| if (r >= 0x30 && r <= 0x39) { | |
| issues = issues.remove(.number); // 0-9 | |
| if (!issues.hasUppercaseIssue) break; | |
| } else if (_isUppercaseRune(r)) { | |
| issues = issues.remove(.uppercase); // A-Z | |
| if (!issues.hasNumberIssue) break; | |
| } | |
| } | |
| final length = password.length; | |
| if (length < 8 || length > 128) issues = issues.add(.length); | |
| if (password != confirm) issues = issues.add(.mismatch); | |
| return issues; | |
| } | |
| /// Checks if a Unicode code point is an uppercase letter. | |
| /// Covers Latin, Cyrillic, Greek, and extended Latin ranges without allocations. | |
| @pragma('vm:prefer-inline') | |
| bool _isUppercaseRune(int r) => | |
| (r >= 0x41 && r <= 0x5A) || // A-Z | |
| (r >= 0x410 && r <= 0x42F) || // А-Я (Cyrillic) | |
| r == 0x401 || // Ё | |
| (r >= 0xC0 && r <= 0xD6) || // À-Ö (Latin Extended-A) | |
| (r >= 0xD8 && r <= 0xDE) || // Ø-Þ (Latin Extended-A) | |
| (r >= 0x391 && r <= 0x3A9); // Α-Ω (Greek) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment