Articles Archive
Articles Search
Director Wiki
 

Password entry and validation

November 21, 1999
by Pat McClellan

Dear Multimedia Handyman,

The program that I am desiging has the user put in a password, then on another screen they are asked to verify that password by typing it in again. If they get it wrong they are sent to another screen if they get it right they are sent in another direction. Please help. One more thing, when the user types in the password the first and second time the password field is protected (meaning * appear on the screen instead of letters).

sheri kord

Dear sheri,

Let's take care of the basic concepts first, then we'll get around to writing the actual code. When you have an editable field or text member on the screen, the data which is entered into it is in a format called "string". That just means that it's a string of characters, including spaces and punctuation marks if there are any. Try this experiment in Director's message window:

password = "SHERI"
put password
-- "SHERI"
confirmWord = "sheri"
put confirmWord
-- "sheri"
put password = confirmWord
-- 1

This bit of code indicates that Director is not case sensitive when comparing strings. That could be particularly important in this situation because passwords are sometimes case sensitive. Lingo does provide a way to distinguish the case of the letters: charToNum. Take a look:

put charToNum("A")
-- 65
put charToNum("a")
-- 97

As you can see, the number (the ASCII value) is differs for upper case and lower case. Although it'll be a little more tedious, we can create a test for case sensitive matches as well.

Either way, we'll need to create a way of storing the entered password for later comparison. We have several choices about how we do this. Perhaps the easiest method would be to use a global variable -- which for this example might logically be named gPassword. When the user enters an initial password, that string would be saved in the global variable. Then, later in the program when they need to confirm the password, gPassword would be easily accessible for comparison.

Object-oriented Lingo purists would probably prefer to not have the value stored in a global variable. Why? Because it's a value which doesn't need to be widely accessed, so why not encapsulate the data. The object-oriented approach would be to create a password object with methods to store the password in a property of that object and then later compare another string to it. The password would not be "exposed", but rather would be encapsulated within the object. While there's merit to this way of thinking, if you're a beginner, there's little harm in using the global variable approach instead.

Yet another alternative occurs to me. Although you don't say so in your question, in many applications, you'll want to be able to save the user's password so that they can quit the program and then use the same password later. This is very easy to do, using setPref and getPref. I think this might be the most versatile approach to the objective. So here's the overview:

  1. As the user types a password into an editable field member, a behavior on that sprite changes the entered characters to asterisks while the actual characters are stored as a property of that behavior.
  2. When a button is pressed, a message is sent to the field sprite to save the password.
  3. The behavior on the field sprite receives the message and saves the password on the user's computer using segPref.
  4. Later, when the user enters characters into a field member to confirm the password, the same behavior converts the letters to asterisks.
  5. When a button is pressed, a message is sent to the field sprite's behavior to confirm the password.
  6. The behavior on the field sprite receives the message, retrieves the original password from the user's computer using getPref, then compares the two values (either case sensitive or not.)
  7. If the two values match, the behavior will go to a particular frame, and if they don't match, it will go to another frame.

A sample movie is available for download in Mac or PC format. This is a D7 movie.

Let's start with the behavior on the field sprite. It will need handlers to convert characters to asterisks while storing the original characters, save the entered text as a password, retreive the saved password, and compare the two strings. We'll need the author to specify the frames to go to for a match or no match. We'll also add the options to make it a case sensitive validation, give the author the choice of replacement characters (asterisk by default), and allow the author to choose the prefs filename. Here's what the dialog box will look like when it's dropped on a field or text sprite:

You'll see that I've also included a field where you can specify the valid characters. In this demo, the available characters are the letters A-Z. If you wanted, you could also include numerals, but you'd probably not want to include punctuation or special characters. Here's the code for this behavior:

-- passwordEntry behavior
-- copyright © 1999, ZZP Online, LLC
-- free use to Director Online readers
property pText
property pValidChars
property pPrefs
property pSlug
property pCaseSensitive
property pMatchFrame, pNoMatchFrame
property pMem
on getPropertyDescriptionList me
  set pdlist to [:]
  
  addprop pdlist, #pValidChars, [#comment:"Valid chars", ¬
    #format:#string, #default:"abcdefghijklmnopqrstuvwxyz"]
  addprop pdlist, #pPrefs, [#comment:"Prefs filename - 8 char ¬
    max:", #format:#string, #default:"password"]
  addprop pdlist, #pSlug, [#comment:"Substitute character:", ¬
    #format:#string, #default:"*"]
  addprop pdlist, #pCaseSensitive, [#comment:"Case sensitive?", ¬
    #format:#boolean, #default:0]
  addprop pdlist, #pMatchFrame, [#comment:"Match frame:", ¬
    #format:#marker, #default:0]
  addprop pdlist, #pNoMatchFrame, [#comment:"No match frame:", ¬
    #format:#marker, #default:0]
  
  return pdlist
  
end getPropertyDescriptionList
on beginSprite me
  pMem = sprite(me.spriteNum).member
  pText = ""
  pMem.text = ""
end
on keyDown me
  if the key = BACKSPACE then
    delete the last char of pText
  else if the key = TAB then
    pass 
  else if pValidChars contains the key then
    put the key after pText
  end if
  
  displayAsterisks me
  
end
on displayAsterisks me
  charCount = pText.char.count
  displayString = "" 
  
  repeat with i = 1 to charCount
    put pSlug after displayString
  end repeat
  
  pMem.text = displayString
  the selStart = charCount
  the selEnd = charCount
  
end displayAsterisks
on savePassword me
  setPref(pPrefs, pText)
end
on validatePassword me
  password = getPref(pPrefs)
  
  if pText = password then
    status = #match
  else
    status = #noMatch
  end if
  if pCaseSensitive and status = #match then
    charCount = pText.char.count
    repeat with whichChar = 1 to charCount
      x = charToNum(pText.char[whichChar])
      y = charToNum(password.char[whichChar])
      if x <> y then
        status = #noMatch
        exit repeat
      end if
    end repeat
  end if
  
  if status = #match then
    go to frame pMatchFrame
  else
    go to frame pNoMatchFrame
  end if
  
end

You'll see that I have a keyDown handler. That may be unfamiliar to some of you. That's the handler which intercepts the user's key stroke and processes it before the letter appears in the editable field or text sprite. This handler allows BACKSPACE and TAB to pass without processing, but all other keys are checked to see if they're included in the string of valid characters. If so, then the key is appended to our pText property, and an asterisk (or other selected character) is displayed in its place.

The savePassword handler is fairly simple so I won't explain it..

The validatePassword handler starts by retrieving the password from the prefs file. Then it proceeds to do a simple string comparison (not case sensitive). If the strings do match and the author has specified case sensitive validation, then the ASCII value of each character is compared to the stored password. If any letter is not a match, then we skip the rest of the comparison. After that, it we still have a match, then we go to the pMatchFrame, else we go to the pNoMatchFrame.

All that's left is commanding this behavior to do its stuff. For that, I made this little password commands behavior which is attached to the buttons:

-- password commands behavior
-- copyright © 1999, ZZP Online, LLC
-- free use to Director Online readers
property pWhichSprite, pCommand
on getPropertyDescriptionList me
  commandList = [#savePassword, #validatePassword]
  pdlist = [:]
  
  addprop pdlist, #pWhichSprite, [#comment:"Which sprite?", ¬
    #format:#integer, #default:1]
  addprop pdlist, #pCommand, [#comment:"Which command?", ¬
    #format:#symbol, #default:#savePassword, #range:commandList]
  
  return pdlist
  
end getPropertyDescriptionList
on mouseUp me
  sendSprite pWhichSprite, pCommand
end

That's all there is to passwords. If you don't need the ability for users to choose their own password, I recommend the password behavior that comes with D7. Darrel Plant wrote this great behavior which allows the author to specify the password when you're creating the movie.

Good luck with your program.

Patrick McClellan is Director Online's co-founder. Pat is Vice President, Managing Director for Jack Morton Worldwide, a global experiential marketing company. He is responsible for the San Francisco office, which helps major technology clients to develop marketing communications programs to reach enterprise and consumer audiences.

Copyright 1997-2024, Director Online. Article content copyright by respective authors.