Maintain PLC DidDocument completeness & integrity during PDS migrations #3

The com.atproto.identity.getRecommendedDidCredentials lexicon does not return a comprehensive DidDocument, only changes that need to be made for a migration. Its returned parameters need to be merged with unchanged services, verification methods, and aliases in the user's existing DidDocument: "If using a PDS-managed did:plc, you can edit the parameters to match any additional services or recovery keys".

This change retrieves the user's existing PLC DidDocument and replaces the required services, verification methods, and aliases needed for PDS migration only (via the above lexicon). It doesn't change the user migration experience, aside from assuring no silent (potentially unrecoverable) breakage of non-atproto PDS services or verification methods

Changed files
+37 -6
src
+37 -6
src/pdsmoover.js
···
async signPlcOperation(token) {
const getDidCredentials =
await this.newAgent.com.atproto.identity.getRecommendedDidCredentials();
-
const rotationKeys = getDidCredentials.data.rotationKeys ?? [];
+
const rotationKeys = getDidCredentials.data.rotationKeys;
if (!rotationKeys) {
throw new Error('No rotation key provided from the new PDS');
}
-
const credentials = {
-
...getDidCredentials.data,
-
rotationKeys: rotationKeys,
+
const oldDidDoc = await docResolver.resolve(this.oldAgent.did);
+
+
// Format the service(s) in the user's DidDocument into the operation syntax
+
let oldServices = {};
+
for(const service of oldDidDoc.service) {
+
const idComponents = service.id.split('#');
+
oldServices[idComponents[idComponents.length - 1]] = {
+
type: service.type,
+
endpoint: service.serviceEndpoint
+
};
+
};
+
// Combine old & new suggested services
+
const services = {
+
...oldServices, // Existing services in the user's DidDocument
+
...getDidCredentials.data.services // Service(s) suggested by the new PDS
+
};
+
+
// Format the verification method(s) in the user's DidDocument into the operation syntax
+
let oldVerificationMethods = {};
+
for(const verificationMethod of oldDidDoc.verificationMethod) {
+
const idComponents = verificationMethod.id.split('#');
+
oldVerificationMethods[idComponents[idComponents.length - 1]] = `did:key:${verificationMethod.publicKeyMultibase}` // PLC Operation verification methods are did:keys
+
};
+
// Combine old & new suggested verification methods
+
const verificationMethods = {
+
...oldVerificationMethods, // Existing verification methods in the user's DidDocument
+
...getDidCredentials.data.verificationMethods // Verification method(s) suggested by the new PDS
};
-
+
const alsoKnownAs = [
+
...getDidCredentials.data.alsoKnownAs, // Suggested alsoKnownAs goes first (the first entry is the user's primary handle)
+
...oldDidDoc.alsoKnownAs.slice(1) // Pre-existing alsoKnownAs, minus the old primary handle
+
];
+
const plcOp = await this.oldAgent.com.atproto.identity.signPlcOperation({
token: token,
-
...credentials,
+
rotationKeys: rotationKeys, // Replace rotation keys
+
alsoKnownAs: alsoKnownAs, // Merged alsoKnownAs
+
services: services, // Merged services
+
verificationMethods: verificationMethods, // Merged verification methods
});
await this.newAgent.com.atproto.identity.submitPlcOperation({