Password Matcher Password Matcher
Password Matcher Password Matcher
DocFX + Singulink = ♥

Search Results for

    Match Results

    A successful check returns a PasswordMatch that describes exactly what the password was built from. This guide covers what the match contains, what each match type means, and how to turn a match into a message the user can act on.

    The Match Object

    MatchPassword returns null when the password does not match, so the match object itself only ever describes a rejection. It has two views of the same information:

    • Values is the list of matched values as strings, in the order they appear in the password. This is what you show the user.
    • Items is the list of PasswordMatchItem objects, each pairing a value with its MatchType. Use it when you want to word the explanation differently for different kinds of matches, or to log what kinds of weak passwords your users attempt.
    var match = PasswordMatchEvaluator.Default.MatchPassword("QWERTp@ssword1975", null);
    
    match.Values;                 // ["QWERT", "password", "1975"]
    match.Items[0].MatchType;     // CharSequence
    match.Items[1].MatchType;     // Common
    match.ToString();             // "\"QWERT\" (CharSequence) \"password\" (Common) \"1975\" (Common)"
    

    Values are the dictionary form

    MatchedText holds the value in the form the user will recognize, not the characters that were actually in the password. A password of p@55w0rd reports password, because that is the word the attacker's dictionary contains and the word the user needs to get rid of. Character sequences are reported in upper case (QWERTY, ABC) to stand out as sequences rather than words. The characters that were actually matched are available as MatchedChars (p@55w0rd, qwerty), taken from the normalized password, for highlighting the matched part of what the user typed or for showing both forms side by side. Separators between values (password.123 or the-bad-admin) are consumed by the match but are not reported, since they are not part of what made the password weak.

    Match Types

    PasswordMatchType says what kind of value each item is:

    Type Meaning Example item
    Common A word, phrase or value from the common password lists. password, love, 2024
    ContextualSubject A word from one of the contextual subjects passed to the check. jane, acmewidgets
    CharSequence A run of adjacent keys on a keyboard row, diagonal or zigzag, a line of a numeric keypad, or consecutive letters of the alphabet, in either direction, shifted or not. QWERTY, 1QAZ, !@#$, 1Q2W3E, 7410, ABC
    RepeatedChar A run of the same character. 111, !!
    RepeatedGroup A short group of characters repeated. 121212, 1!1!
    AnyChars A short run of arbitrary digits or symbols decorating the words. 47, #1, 1!
    RepeatValue A repeat of the value matched immediately before it. the second you in iloveyouyou

    A value that occurs several times is reported once per occurrence. The default patterns allow a few consecutive words, so the first occurrences are usually reported as separate Common or ContextualSubject matches, and RepeatValue appears once the pattern has no room for another: memememememe is reported as three Common items followed by three RepeatValue items, all with the text me. Treat the two the same way when wording a message, and deduplicate Values if you only want to tell the user which distinct words were found.

    Explaining the Rejection

    The point of returning the values is to let the user fix the problem on the first try. A message that names the words works far better than a generic one:

    static string? GetRejectionMessage(PasswordMatch? match)
    {
        if (match is null)
            return null;
    
        var words = match.Values.Distinct().Select(v => $"\"{v}\"");
    
        return $"Your password is made up entirely of words that are commonly used in passwords or that are easy to guess " +
               $"from your account details ({string.Join(", ", words)}). Add an uncommon word or character sequence to " +
               $"make it harder to guess.";
    }
    

    Wording by match type

    Items lets you be more specific. Contextual matches deserve their own explanation, since the user may not realize that their own name or your product name is an obvious guess:

    var contextual = match.Items
        .Where(i => i.MatchType is PasswordMatchType.ContextualSubject)
        .Select(i => i.MatchedText)
        .Distinct()
        .ToList();
    
    if (contextual.Count > 0)
    {
        message += $" Avoid using your name, email address or the name of this service ({string.Join(", ", contextual)}).";
    }
    
    Tip

    Keep the suggestion constructive. The matched words tell the user what to remove; suggesting they add an uncommon word tells them what to do instead, and keeps them from simply appending another digit, which the suffix patterns will catch anyway.

    Matching Without an Evaluator

    GetFirstMatch runs a password against any set of matchers directly and returns the same PasswordMatch. It is what the evaluator uses internally, and it is the easiest way to test a matcher in isolation, since a match is only returned when the matchers consume the entire password:

    var matcher = PasswordMatcher.Text("hunter");
    
    PasswordMatcher.GetFirstMatch("hunter", [matcher]);     // match with values ["hunter"]
    PasswordMatcher.GetFirstMatch("hunter2", [matcher]);    // null: the "2" is not consumed
    

    The evaluator adds the length limit and the matchers provider on top of this. See Writing Custom Matchers for testing matchers this way.

    Next Steps

    Continue with these related articles:

    • Password Policy Integration - Where the message fits in a complete password check.
    • Contextual Subjects - Where contextual items come from.
    • How Matching Works - Why the first match found is the one reported.
    © Singulink. All rights reserved.