The password and passkey registration paths both call verify_did_web() and walk the user through a "host this JSON" step. The SSO path is missing both.
In my case I had a stale /.well-known/did.json left over from testing and so after signing up I wound up with the DID document mostly correct, except for the keys.
I was able to salvage the situation by grabbing the correct didDocument from the /xrpc/_account.getDidDocument endpoint on my pds.