Passwords, widgets, and Pyto

When I switched from 1Password to Apple’s built-in password manager, I had concerns about the reliability of Apple’s system, but overall I’ve been pleased with how things have worked out. There’s just one problem I’ve run into. It doesn’t show up often, but the annoyance it causes when it does show up has led me to build a workaround.

When you need a new password, iOS offers to create one for you.

iOS password notice

This notice appears when you put the cursor in a password field, and the field itself gets highlighted with part of the strong password iOS made.

Password field with new strong password

If all goes well, your new login works and all the details are saved in your iCloud Keychain for later use. But sometimes the website’s logic and Apple’s don’t mesh, and your new credentials aren’t saved. Now you have an account for which you don’t know the password. Lovely.

The obvious solution would be to copy the iOS-created password to the clipboard before tapping the button that creates the new account. That way, if iCloud Keychain doesn’t save the new credentials, you can add them directly. Unfortunately, iOS doesn’t allow you to copy the password field when the strong password it created is in there. So you have to go the “I forgot my password” route with the website to get a new new password to replace the old new password you just created a few seconds ago. I always feel like the website is judging me when I have to do this.

Presumably, Apple refuses to let you copy the password it just made because there’s a security risk to having a password on the clipboard. But there’s also a security risk to having a password manager that doesn’t save your password.

To get around this problem, I wrote a script to generate passwords for me using a variant of the Diceware method. It was written in Pythonista, and used lists of words that I’d gathered for my Countdown anagram script. It did a good job of generating passwords, but because Pythonista doesn’t work well with Shortcuts, it was clumsy to use.

Enter Pyto, an iOS Python app that does work well with Shortcuts, and iOS 14 widgets, which make it easier than ever to run Shortcuts without digging into the Shortcuts app itself.

Now I have a medium-sized Shortcuts widget with four shortcuts.

Shortcuts widget

Tapping the New Password button puts a new password on my clipboard. I can then paste this into the password field on the website where I’m creating a new account. If the credentials get saved to iCloud Keychain, great. If the credentials don’t get saved to iCloud Keychain, the password is still there on my clipboard, and I can use it to add a new login manually to the Passwords section of Settings. Either way, I then copy something else to the clipboard to overwrite the password.

The New Password shortcut is just a call to run a Pyto script:

New Password shortcut

(The comment is there mainly to force Shortcuts to use the icon I chose. I’ve noticed that single-step shortcuts often get displayed with the icon of the step rather than the icon of the shortcut itself. Before I added the comment, the widget button for this shortcut was shown with the Pyto icon instead of the key icon.)

The Pyto script is pretty simple:

python:
 1:  from secrets import choice, randbelow
 2:  import pasteboard
 3:  
 4:  # Get a list of words that are 4-6 characters long.
 5:  with open('words.py') as f:
 6:    words = f.readlines()
 7:  
 8:  # Select 4 words from the list.
 9:  pwords = [ choice(words).strip() for i in range(4) ]
10:  
11:  # Add a numeral and a "special" character.
12:  # Capitalize one of the words.
13:  numeral = str(randbelow(10))
14:  special = choice('!@#$%^&*')
15:  pNum = randbelow(4)
16:  pSpec = randbelow(4)
17:  pCap = randbelow(4)
18:  pwords[pNum] = pwords[pNum] + numeral
19:  pwords[pSpec] = pwords[pSpec] + special
20:  pwords[pCap] = pwords[pCap].capitalize()
21:  
22:  # Assemble the password and put it on the clipboard.
23:  pw = '-'.join(pwords)
24:  pasteboard.set_string(pw)

Note first that this script uses the relatively new secrets module instead of random to generate random numbers. The secrets module was built specifically to use in cryptographic applications like this.

The words.py file that’s opened and read in Lines 5–6 is a text file consisting of about 28,000 words that are 4–6 letters long, one per line. It’s not a Python source code file, but I had to give it a .py extension to save it in Pyto’s Documents directory along with the scripts. Pyto really should be less strict about what file types can be saved there.

Line 9 selects four words from the list. Lines 13, 15, and 18 add a numeral to the end of one of the words. Lines 14, 16, and 19 add a “special” character to one of the words. Lines 17 and 20 capitalize one of the words. The additions and capitalization are done because some websites won’t accept passwords without them.

Finally, Line 23 puts the words together with hyphens and Line 24 copies the result to the clipboard. Here’s an example of the kind of password it generates:

rideau4-tawery-conure&-Rewin

You can tell that the word list I used is based on a Scrabble-type dictionary and not a common-words dictionary. I keep my conure’s cage behind a rideau in the tawery.

With this widget on my iPhone and iPads, I can avoid the occasional hiccups of iCloud Keychain without digging through lists of shortcuts or scripts. A good password is always just one tap away.

Update Oct 5, 2020 10:52 AM
I needed to make a couple of changes to get this working consistently across all my devices.