WordPress SSO with Google Workspace SAML and OneLogin SAML SSO Plugin

I recently embarked on a journey to simplify logins to a client’s network of WordPress sites with Single Sign-On (SSO). I didn’t really care what identity provider was going to be used, but they also used Google Workspace which I knew had support for SAML. I also figured that, being Google Workspace, the support for it would be well established, with lots of nice and simple clicky interfaces.

Nope.

Until this point my only real experience with SSO had been as a consumer; I’ve had many accounts that have implemented some form of SSO (often based on Google accounts) and it has been relatively seamless. But trying to implement it myself from a cold start I found frustrating.

I first tried auth0’s setup with their WordPress plugin, but the auth0 WordPress guide was a little out-of-date. I found the auth0 interface super overwhelming at a glance, and gave up quickly.

Some more searching put me on to the OneLogin SAML SSO plugin (GitHub), which has zero documentation on the WordPress plugins site, in their GitHub, or anywhere else that I can find. The plugin, once installed in WordPress, yields a settings page with a billion different options. While they are mostly well-described, it’s not super-clear what you need to do in order to get up and running, especially with Google Workspace.

Some trial and error and help from this support thread and it was working pretty quickly – although I still don’t know what is going on under the hood, so it will be a while before I decide to use this in any production capacity – so just wanted to document the process as it stands for WordPress and Google Workspace (as of March 2021, anyway).

There are two sides you need to configure to make this work – OneLogin’s WordPress plugin, and the Google Workspace SAML setup.

Starting with the Workspace side:

  1. Broadly, we’re following Google’s own instructions: “Set up your own custom SAML application“.
  2. Log into Workspace Admin, go to Apps, and select “SAML Apps”.
  3. Open the “Add App” dropdown and select “Add custom SAML app”.
  4. Enter whatever for the app name & click “Continue”.
  5. Copy the “SSO URL”, “Entity ID” and “Certificate” fields, taking care to get it all and preserve formatting. (You can download the IdP metadata as well for backup purposes, but you can retrieve this information again easily at any time, so don’t stress.) Click “Continue”.
  6. Now it will ask for your “Service provider details” – “ACS URL” and “Entity ID”. The Learn More link here provides no useful information about what these are or where to get them from – but they come from your WordPress setup.

So now we switch to the WordPress side:

  1. In a new tab/browser, log into your WordPress admin panel and install the OneLogin SAML SSO plugin, and activate it.
  2. Go to Settings->SSO/SAML Settings, which is where this plugin keeps its settings.
  3. At the very top of the page, there is a link: “Go to the metadata of this SP”. Clicking this will open an XML document which has the information needed for the Google Workspaces form.
  4. Two two values we want are as follows (note: both of these values seem to be able to be customised elsewhere in the OneLogin plugin settings):
    1. ACS URL: this is in the tag that looks like this: <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/wp-login.php?saml_acs" index="1"/> – we want the value in the Location field.
    2. Entity ID: this is in the very first tag that looks like: <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2021-03-04T00:22:23Z" cacheDuration="PT604800S" entityID="php-saml">. Default seems to be php-saml.
  5. You can ignore the rest of the fields in the “Name ID” section & just click Continue.
  6. Now we need to configure the Attributes. Basically just replicate the below screenshot (Primary email -> email, First name -> firstname, Last name -> lastname, Primary email -> username).

  7. Click ‘Save’.

Now, back to the WordPress SSO config:

  1. Set “IdP Entity Id” to be the “Entity ID” field that we copied from the Google settings up earlier on.
  2. Set “Single Sign On Service Url” to be the “SSO URL” field.
  3. Set “X.509 Certificate” to have the certificate from the “Certificate” field.
  4. Look for the “Create user if not exists” field. Whether or not you want this checked depends on whether you already have your user accounts set up. It may be easiest, if you’re just trying to get this working at all, to check this and try with an account that doesn’t already exist in WordPress with the same email address.
  5. Look for “Match WordPress account by” and change this to “E-mail”. Google Workspace does not appear to expose any username field (maybe you can make this work with mapping but not sure).
  6. Scroll down to “ATTRIBUTE MAPPING”. As with the Google Workspace-side mapping, we do the same here:
    Username: username
    E-mail: email
    First Name: firstname
    Last Name: lastname
  7. There are tons of other things that you should look at – for example, “Prevent reset password” might be something you want to do to make sure a user can’t accidentally have their WordPress password reset to bring it out of sync with their Workspace account (I suspect in theory this should not impact things as users should not be able to login without going through the SSO, but in case of WordPress bugs or vulnerabilities in plugins or whatever it’s probably safer).
  8. Once you’re ready, scroll back up to the top and check the “Enable” checkbox.
  9. I strongly recommend opening a new private browser session and logging in as admin at this point, just in case any of this blows up access to your admin section.
  10. Then back to the bottom, hold on to your butts, and click “Save Changes”.
  11. You should now be able to log into your WordPress site with your Google Workspace credentials.

This document is a work-in-progress as I figure out more about what is going on; very interested in comments and feedback.

Resources:

Copying Google Sheets into Shared Drive Removes Ability to Create Project Script Properties

If you have a Google Sheet with some associated Apps Script code in your personal drive, then you copy it into a shared drive (e.g., your company share), it will strip the project properties, including script properties that you may have (e.g.) set to hold credentials.

This seems like a good plan, to prevent information leaking when scripts are copied around.

Unfortunately, it also seems to permanently remove the ability to add script properties ever again. If you open the Project properties and check the Script properties, the “Add row” link is simply missing.

If you create a new spreadsheet on the shared drive, it will work fine – so in some cases, it might be easy to just copy/paste the spreadsheet info and the app script into an entirely new document, created from scratch.

Unfortunately this didn’t work for my spreadsheet, which had a lot of names ranges, buttons, images, and drawings, which don’t lend themselves to copy/pasting easily.

Been battling this for a while and can’t find a simple solution, although other people seem to have the same problem. There’s also several bugs in Google’s Issue Tracker that seem to be related.

The easiest fix I’ve found is:

  1. Create a new copy of the spreadsheet in my own drive
  2. Delete the scripting from the spreadsheet
  3. Copy the spreadsheet over into the shared drive
  4. Re-add the scripting into the spreadsheet
  5. Add the project properties you need

This preserves all the spreadsheet bits that are frustrating to recreate manually and (for me at least) re-creating the script is a simple copy/paste.

WordPress Shortlinks Interfering with wget Mirroring

Testing some methods to convert a WordPress site into a static site, I ran into a weird problem when converting the links (using -k or --convert-links in wget) was breaking the mirroring process, putting in the wrong links.

My mirror command was simply:

wget -m -k -nH example.com

The link conversion ended up breaking the internal links – a link like:

<a href="about/index.html">[ About ]</a>

… was being converted into

<a href="index.html?p=2">[ About ]</a>

I was a bit stumped until I noticed that the About page contained the following HTML:

<link rel='shortlink' href='https://example.com/?p=2' />

Removing the shortlink by simply adding the following line to wp-config.php (although it should probably go in the theme) fixed the problem:

remove_action('wp_head', 'wp_shortlink_wp_head');

My guess is that wget sees the shortlink declaration and tries to helpfully rename the files to match, but this ends up just breaking the link conversion, rendering the mirror useless.

Launchy Alternatives

Launchy is an open source keystroke launcher for Windows. You press a magic key combo (e.g., Win-A, in my case) and a little dialog pops up, in which you can type commands and have them run.

It is very similar to hitting the Windows key in Windows 8.1 or 10 and just typing – it searches through a local database of your files and applications so you can quickly find things and launch or open them.

I am four months into my first Windows 10 machine. Unfortunately, Launchy hasn’t been updated for a while and doesn’t work as gracefully in Windows 10 as I would like:

  • It doesn’t deal gracefully with high-resolution, 4K displays. Fixable with some tweaks though.
  • Starting applications on high resolution displays seems to load them in a weird mode where they are blurry. Probably workaround-able.
  • Win-A is bound to the notification panel on the right hand side, for example, and it doesn’t really deal gracefully with very high resolution displays.

As a result, I have been forcing myself to use the native Windows 10 search, but I find it totally inferior to Launchy in many ways.

Most critically for me, Launchy has some basic tracking on how often you open particular things after you’ve started typing. So it learns, quickly and effectively, what you want to open after you’ve just typed a few letters. Contrast this to Windows 10, where to open my password safe, I have to type pwsafe.exe every single time. (I have to add the `.exe` otherwise it will open the directory in Explorer).

The usability advantages that this confers don’t sound like a big deal, but after something like seven+ years of running Launchy, I find it impossible to go back.

Anyway, I’ve been going through the various other open source alternatives to Launchy that have popped up in the last few years, and thought I’d just jot them down so the next time I go looking, I have a handy list of them.

  • LaunchyQt, a promising-looking fork of the base Launchy, with some more modern features.
  • Wox, probably my next pick. Looks pretty solid.
  • Hain, since discontinued. Built with Electron so probably less lean than some alternatives.

There are some non-open source ones as well that might be worth a look if one is less fussy than I am – Keypirinha, Listary, and Executor.

On the Normalisation of Augmented Reality

AirPod Pro

The “Transparency” feature of the newly-released Apple AirPod Pro grabbed my attention. The claim from Apple is that Transparency is “for hearing what’s happening around you” — it “lets outside sound in, and allows things to sound and feel natural when you’re talking to people nearby”.

Even before the launch of AirPod Pro, I had noticed that many people would engage in conversations with their AirPods in. Maybe not long conversations, and people wouldn’t sit in meetings with them on the whole time, but (purely anecdotally) I felt like I would see a lot of people having a chat while still with their headphones in.

It has felt like the usual social stigma of talking to people with your headphones still on was fading a little bit. When I’m wandering around with headphones on, I’ll usually take at least one ear out to make it clear to whoever I’m speaking to that they have my attention.

But Transparency changes the entire game. Instead of assuming that people with headphones in are blissfully unaware of your existence, lost in their own world of music or podcasts or conference calls, we can now wonder if they’re in Transparency mode and actually hyper-conscious of what you’re saying to them, because all the background noises are being stripped out.

This sounds awesome in many contexts. As someone hitting the “middle aged” milestone, I often find it frustrating being in noisy environments and trying to have a conversation. The idea of being able to pop in headphones and have filter out the background noises so I can better hear the people near me talk is appealing.

(It should be acknowledged that Apple weren’t the first ones to come up with this idea. Bose has had conversation-enhancing technology for a while; there might be other vendors with similar technology.)

One of the big challenges though for this sort of technology, however, is the fact that people would generally be self-conscious wearing augmentation hardware in many environments, both social and professional. Think about things like hearing aids and glasses — for many people the vanity issues of these devices, despite being super common and well-established in society, have prevented them from taking them up, often to their own significant detriment.

The cost of traditional hearing aids can also be a big factor. If they’re not accessible on your insurance, they might simply be unaffordable — the top models often run into the thousands. While the AirPod Pros are expensive, they might be “good enough” for many users when compared with the expense of hearing aids — cherry picking a single example from the Bose site:

One of many positive reviews of Bose assisted hearing.

Think about Google Glass and the “glasshole” phenomenon. I was super excited when I first heard about this project. I only ever met one person who was wearing them; while it was weird talking to him wondering what was going on, I still confess to being more nerdishly fascinated by the possibilities than thinking about the implications for those around me (let alone what I’d look like wearing them). The possibilities of a great AR platform have become far more interesting to me than Virtual Reality — even from a video gaming perspective, it feels like in the foreseeable future, there are a lot more fun opportunities in AR than VR.

The Glasshole Problem (source)

But Google never really nailed Glass as a platform for the average person on the street and the project was relegated to specific commercial/industrial uses. The backlash against them blew up into all sorts of weird places as society wrestled with the Glasshole Problem, which reportedly triggered physical confrontations and resulted in businesses creating policies to deny service to customers wearing them.

With the recent report that Apple is considering Augmented Reality (AR) smart glasses, it’s easy to start thinking of the AirPod Pro as a way for Apple to test the waters in terms of normalising technological augmentations — headphones and glasses — by making them Cool.

To really drive the mass adoption of AR to the level of smartphones, it will be critical to make the experience of wearing AR hardware not only technically excellent, but also Cool enough so that people are comfortable wearing them regularly.

Apple have done more to make technology Cool than any other company. The iPod set the scene by normalising interaction of music with your computer. The iPhone transformed the world with the smartphone revolution. (I remember being asked by many people, prior to its launch in the Windows Mobile/PocketPC era, “why would you want to check your email on your phone?”, an attitude which is now so far removed from reality it’s hard to even believe it once existed).

More recently, the Apple Watch has set a new standard for fashioned-based technology. They are clearly the dominant wearable; as with many other Apple devices, despite a lot of naysayers being critical of the devices, they have had a massive impact on how people see and use hardware.

The AirPod Pro has the potential to change the way people think about other hardware augmentations that are more obviously visible in your regular interactions with other people. It seems unlikely they’ll offer a variety of colours so they’re more readily thought of as fashion accessories — the white stems poking out of the ears just seems like it has brand recognition that is too good to pass up on.

But if they can get people used to conversing with people with AirPods plugged into their ears in a variety of normal circumstances — in bars, in meetings, in conversations walking down the street —it is a powerful step along the way to adjusting the expectations of the entire planet in terms of other hardware augmentations.

And if there’s any company that can make wearing high-tech nerd computer glasses cool — it’s Apple.

Their growing, evolving knowledge on how to make consumer devices that combine fashion and technology (both software and hardware) to create a unified product that resonates with people means they are uniquely positioned to effect another paradigm shift in terms of wearable computing when it comes to AR. They might be the first company to finally make a product that people feel comfortable enough to wear enough of the time to make them genuinely useful.

First published on Medium.

Can’t Remove Specific Phone Number from Android Contacts

Just had a weird issue where I had an Android contact with an old number on the phone, but it didn’t show up in the web contacts version. Took me a while to realise that you can select ‘View linked contacts’ from the menu in the contact view page in the Android Contacts application to see where external numbers are coming from.

In this case, it was from Signal – Signal maintains its own database of contacts. Turns out zombie contacts in Signal populating your Google Contacts is a common problem, but this Github issue implies there’s no easy UI fix for it in the Signal side.

A modified version of the solution posted by riyapenn worked for me, although I had no saved messages from the contact in question – I suspect if you do it might work differently, so look at the full solution.

Re-sync Signal contacts

1) Go to Android Settings or Android Contacts App > Menu
2) Choose Accounts
3) Choose Signal
4) Tap on the Menu
5) Choose Remove Account (the alert of clearing data is incorrect, your messages will not be deleted)
6) Open Signal
7) Tap on the pencil icon in the blue circle
8) Choose the Menu
9) Tap Refresh

exim4 and apache2-mpm-itk

Migrating from a very old Debian install to a newer VPS with a more recent version of Apache and the mpm-itk mod, I was having problems with sending mail using the standard PHP mail() call (first seen when the WP contact form I was using was throwing a “Sorry, email message could not be delivered” error).

exim4 log reported the following:

unable to set gid=33 or uid=0 (euid=0): forcing real = effective

This thread contained a post indicating the problem was the LimitGIDRange/LimitUIDRange options; it seems if these are not specified there are some defaults (perhaps with very low values, or perhaps it’s just that if it’s not set it will not work at all) that need to be overridden.

Defining these values in the global Apache configuration fixes it.

PHPMailer and Gmail API Mysterious ‘Could not connect’ Error on Windows

I’ve had PHPMailer happily sending email through the Gmail API (as part of a G Suite subscription) for a while now and it mysteriously stopped working yesterday (29th Sep, 2017), throwing the following output with debug enabled:

2017-09-30 11:24:52 SERVER -> CLIENT: 220 smtp.gmail.com ESMTP v2sm1805443wmf.8 - gsmtp
2017-09-30 11:24:52 CLIENT -> SERVER: EHLO trog-pc
2017-09-30 11:24:52 SERVER -> CLIENT: 250-smtp.gmail.com at your service, [86.170.8.39]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
2017-09-30 11:24:52 CLIENT -> SERVER: STARTTLS
2017-09-30 11:24:52 SERVER -> CLIENT: 220 2.0.0 Ready to start TLS
2017-09-30 11:24:52 SMTP Error: Could not connect to SMTP host.
2017-09-30 11:24:52 CLIENT -> SERVER: QUIT
2017-09-30 11:24:52 SERVER -> CLIENT: M I A ��] P *g�� 87� �*��h�!T��
[multiple line binary gibberish removed]
2017-09-30 11:24:52 SMTP ERROR: QUIT command failed: M I A ��] P *g�� 87� �*��h�!T�� [multiple line binary gibberish removed]
2017-09-30 11:24:52 SMTP connect() failed. https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting
Mailer Error: SMTP connect() failed. https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting

It looks pretty clearly like a crypto error and the step in the Troubleshooting guide (helpfully provided in the error message!) relating to the OpenSSL check made it seem pretty clear that it was a problem.

The OpenSSL test result looked like this:

C:\files\Apps\OpenSSL>openssl s_client -starttls smtp -crlf -connect smtp.gmail.com:587
CONNECTED(0000019C)
depth=1 C = US, O = Google Trust Services, CN = Google Internet Authority G3
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
i:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
1 s:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
i:/OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIEjDCCA3SgAwIBAgIIa79pDvQYxx0wDQYJKoZIhvcNAQELBQAwVDELMAkGA1UE
BhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczElMCMGA1UEAxMc
R29vZ2xlIEludGVybmV0IEF1dGhvcml0eSBHMzAeFw0xNzA5MTMxNzUyMjVaFw0x
NzEyMDYxNzExMDBaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
MRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKDApHb29nbGUgSW5jMRcw
FQYDVQQDDA5zbXRwLmdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAJBSmxMU1SnUDEN7W9D97mjMOMhbrDPqocU1iVgdUaUuLDDvabwb1PjE
SoSpWZpva+13ZcWnHTvw+oYA+y8yjKALGQopDXjSA9OKu2TdK4gLg05PgCvFHgXd
dOJUkkyctAfthO4oll1/NhFa5w+Juv0p5UI7aSLklFxjH7B4Iv8C85vGa5YPftkt
VOCj0rWHOXeZF14qFsWYcH8azRZKU4ih9S8IoeSgfnMhxJRnjexwVNA5MJ/ig3zT
d0q7pK8usd+rbABshztIKA2AAB0g6NueOVGI4GCT/r8MAKbezi6I5U2Kw5Ja0RJl
e0HTq+dCSc9z5Yb86/+yOEDLy3h2BV8CAwEAAaOCAUwwggFIMB0GA1UdJQQWMBQG
CCsGAQUFBwMBBggrBgEFBQcDAjAZBgNVHREEEjAQgg5zbXRwLmdtYWlsLmNvbTBo
BggrBgEFBQcBAQRcMFowLQYIKwYBBQUHMAKGIWh0dHA6Ly9wa2kuZ29vZy9nc3Iy
L0dUU0dJQUczLmNydDApBggrBgEFBQcwAYYdaHR0cDovL29jc3AucGtpLmdvb2cv
R1RTR0lBRzMwHQYDVR0OBBYEFD3FXTx/EAB0m7BLwZC8B7Bh/+TUMAwGA1UdEwEB
/wQCMAAwHwYDVR0jBBgwFoAUd8K4UJpndnaxLcKG0IOgfqZ+ukswIQYDVR0gBBow
GDAMBgorBgEEAdZ5AgUDMAgGBmeBDAECAjAxBgNVHR8EKjAoMCagJKAihiBodHRw
Oi8vY3JsLnBraS5nb29nL0dUU0dJQUczLmNybDANBgkqhkiG9w0BAQsFAAOCAQEA
DWhdK0rwWV7Q2lBk2oukJBgmptwWsPHtkYjnhjqRXzAwAg6iqXJrf6BUmdgK4Vvp
rj0qeE9kcTudvZwMPVxS7gcjk66v79n2NvE2QBKGZnlUC/4S93jgQVuDZMwmKF3n
ArjDQ0zE2o6wfM3I3yNkzT+/ZxXtrYzhPmRmbVKWTgMSJvWwN6H2T7An+1JXl11A
7Tf5VeiPSI/kvCByw7sezFDRHbnj2uXZz23DymT75zgF/V3Nbzmg3htdlVnyB2Xp
kMKl5swPBrBuui2+et9ZN7vYjZRdHy0jg/PB9lfpdA2CQnIHcq/vIYBzmi+TSms1
vNaIty8ekNsvigjIzn13eg==
-----END CERTIFICATE-----
subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=smtp.gmail.com
issuer=/C=US/O=Google Trust Services/CN=Google Internet Authority G3
---
No client certificate CA names sent
Peer signing digest: SHA256
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3246 bytes and written 468 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-GCM-SHA256
Session-ID: 927522CACEB8BB3D0FB305E197235C64D147A4CC26643AB60EB5F110E787FA98
Session-ID-ctx:
Master-Key: 8D698AF5A7790DC4836430F2FA6157B310CF0DDA684B5160BEC643B966E9CCC41598D34D03DA0579893A6CAFB62C2B33
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 100800 (seconds)
TLS session ticket:
0000 - 00 33 97 a3 0b be 7f 8d-47 f3 97 6c 18 bb 43 83 .3......G..l..C.
0010 - 27 a4 f7 01 2c d1 a8 0e-55 a9 3c c3 b3 6f 30 58 '...,...U.<..o0X
0020 - 89 22 e3 29 50 42 18 8e-29 ca be 27 57 f9 bc 6e .".)PB..)..'W..n
0030 - 25 f9 ed 68 6a ba 30 97-60 0b 32 fc 19 ab 83 10 %..hj.0.`.2.....
0040 - 00 d1 91 e7 1d 72 d9 2f-3f 27 ac 06 83 23 78 94 .....r./?'...#x.
0050 - 4d 59 38 7f 5d 70 2e ec-d9 d4 b3 31 c9 34 04 25 MY8.]p.....1.4.%
0060 - 79 a8 2f 49 66 ce c7 e3-67 de 46 58 43 b9 42 36 y./If...g.FXC.B6
0070 - 54 49 33 94 99 1e 7d 0b-87 4c da c5 a4 72 b1 05 TI3...}..L...r..
0080 - 5d 47 3b cf 33 13 69 41-f8 1d e4 a0 81 26 1c e5 ]G;.3.iA.....&..
0090 - a7 6b 9b 09 c8 db 1d 8f-6b 5e 54 eb d7 ed 9e 6c .k......k^T....l
00a0 - fc 1f f9 f8 3a d4 3a df-05 c7 0b a3 0b 66 c1 4e ....:.:......f.N
00b0 - 66 27 3c 64 03 60 81 1d-44 bb f0 a4 08 d0 96 dd f'<d.`..D.......
00c0 - 14 31 95 fd 23 7f 13 82-ed 15 fa fb 6a f5 ec 69 .1..#.......j..i
00d0 - c9 b1 d3 e9 fc .....

Start Time: 1506770618
Timeout : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)
---
250 SMTPUTF8

At first glance the Troubleshooting guide implies that the ‘unable to get local issuer certificate’ is safe to ignore – but it is only referring to the first instance of the error at the top. If you’re also seeing the error message at the bottom, you have the same problem as me.

The easy fix is to set verify_peer to false as it described in the Troubleshooting guide. But (also as it notes) this is dodgy and you should fix the local certificate store. So don’t do this.

After messing around a bit (including testing identical code on a Linux VM and seeing that it worked), I gritted my teeth and dove into the OpenSSL configuration (something which I’ve studiously avoided for years because everything has magically Just Worked for me).

It looks like OpenSSL didn’t have a local certificate store at all in Windows and it needs to be explicitly configured. I have no idea how it worked at all – maybe it was using some sort of embedded certificate that just expired? Or maybe I had changed some other option somewhere without realising (unlikely but I hate blaming gremlins).

Anyway, the fix is simple:

1) Download the latest cacert.pem file from the curl website

2) Plonk it somewhere on your local machine where PHP can get to it.

3) Update your php.ini’s openssl.cafile directive to point to this new file.

PHP’s OpenSSL should now have the local certificates. The OpenSSL test in the PHPMailer Troubleshooting guide should now “pass” and that final Verify return code: 20 (unable to get local issuer certificate) message should be replaced with Verify return code: 0 (ok). PHPMailer should also happily work again.

Setting File Creation Timestamp from Modified Time

Note to self: here’s how to easily reset the file creation timestamp, replacing it with the file modification date, using PowerShell on Windows:

Get-ChildItem -recurse -filter *.jpg| % { $_.CreationTime = $_.LastWriteTime }

I wanted to do this recently when a bunch of my photos had them broken by a recovery issue. Yoinked from this StackOverflow.

Fix multi-process Firefox “Disabled by accessibility tools” issue

As of Firefox v54.0, multi-process support (also known as Electrolysis) is enabled by default in Firefox.

I had it working through various config tweaks for a few releases before that, but just noticed that at some point it had been disabled.

about:support reported it was disabled, with the issue:

0/1 (Disabled by accessibility tools)

This was a little weird as I didn’t recall having any accessibility tools enabled. I checked the Options and sure enough the option “Show a touch keyboard when necessary” (under Advanced->General) was enabled.

Disabling this option and restarting did not fix it, but then I found this page which carries this note:

accessibility.lastLoadDate – time of the last load. Electrolysis will be disabled for seven days post this time.

I edited this value in about:config – simply winding it back by some random amount (I used 1491307811 but basically anything more than seven days before whatever time it is should do the trick – and restarted Firefox. accessibility.lastLoadDate had vanished from about:config but multi-process mode was enabled again.