Authorization and verification to other DID's

Hello,

I have looked at the Authorizations documentation for details about this, but I can’t seem to find how exactly to do this. The documentation say exactly what I would like to do:

Authorization is the act of delegating access to a stream to an account that is different than its owner.

There just doesn’t seem to be a step by step way of getting to this. If I am not mistaken when the document states “delegating access to a stream… different than its owner,” this is both read and write access, correct?

I am currently trying to do this exact thing and have been stuck on getting the error message:

Error: Can not verify signature for commit bagcqceralxrzhlzkaeo5yv4hf2pns56eodsbpdpycfxuosbzast26qy4c66q to stream kjzl6kcym7w8y5xwz1na1wztx7vu0dw7rp3cbnhy9fu28nm1kgyxjj13hfblmfn 
which has controller DID did:pkh:eip155:1:0x240ded725d5d33987fc85e3e466139910b4518d8: invalid_jws: 
not a valid verificationMethod for issuer: did:key:zDnaeqSi9uetCYTJNnPK6nDZdg38c9JPy86hc58RBFGntecqw#zDnaeqSi9uetCYTJNnPK6nDZdg38c9JPy86hc58RBFGntecqw

I’m not entirely sure if my models are where I should start or if I should be doing something else to help with verification.

The app I am trying to create is going to have the ability to have a profile be created by user 1 and that user can give access to user 2 to change information related to user 1’s profile. So, once given access, user 2 can change user 1’s name, for example.

Hi @mpatel31.
If you are trying to delegate write access from a did:pkh that is the controller of the stream to a did:key, that should be possible. You can provide the did:key you want to delegate to as the account and get a CACAO authorizing that DID: js-did/packages/pkh-ethereum/src/authmethod.ts at 12783250f828f3a496aa6be1717ef46aba48f173 · ceramicnetwork/js-did · GitHub

Then you can attach that CACAO to the DID instance you are using: js-did/packages/dids/src/did.ts at 12783250f828f3a496aa6be1717ef46aba48f173 · ceramicnetwork/js-did · GitHub

Note however that delegation from one did:pkh to another did:pkh is not yet supported by Ceramic.

1 Like

Also, if you are looking into letting multiple users write to the same stream, please make sure you are familiar with the implications of the Late Publishing Attack on the trust model: Consensus | Ceramic documentation

1 Like

What’s the current thinking about supporting did:pkh → did:pkh delegation?

We have a use case where we’d accept the late publishing risks for collaborative publishing, at least along with being able to resolve rearranged tips under multi-prev. But I’m sure you agree that mixing DID methods to shoehorn this feature isn’t a good path to go.

I think OCAP based delegation to have multiple users write to the same document is actually a good idea. The main problem with it right now is how ComposeDB (and OrbisDB) does conflict resolution.

Ceramic-one (rust code base) will leave conflict resolution up to the implementer, which means that a CRDT type of database could be implemented on top of it. This is something we’re hoping to do more research on (primarily in the form of requirements for such a DB).

And you are right that multi-prev will help here as well, btw.

1 Like

Is that necessarily a problem in all cases, though? Last write wins is perfectly fine for certain things, in particular with a low number of participants and the risk of collision being low and/or not important.

A collision is bound to happen at some point though, and then people are going to wonder why it’s broken.

1 Like

I’m fairly new to this all of this backend and blockchain wallet stuff.

Could you help me understand what the difference exactly is with delegating from one did:pkh to another did:pkh (in theory) and a did:pkh to did:key?

Would the goal of having a user grant access to change their data from another user be viable with using the later of the two delegations?

I would just like to understand a little more as to how each of those work, if you are able to explain.

A did:pkh corresponds to a user’s blockchain wallet. A did:key is a simple wrapper around an asymmetric key pair. Most regular users won’t want to use a did:key as their primary DID, as they would have to be responsible for doing their own key management to keep the private key safe for that DID. Using did:pkh lets you offload key management to a blockchain wallet that is a relatively sophisticated key management system.

2 Likes

Am I creating a new AuthMethod with this? I have tried to use the did:key instead of the AccountId that I have, but it doesn’t seem to work.

Pasted below is the code that I am currently using to get me to the part of needing to authorize the did:key.

try {
        const accountId = await getAccountId(provider, userAddress);
        const authMethod = await EthereumWebAuth.getAuthMethod(
          provider,
          accountId
        );
        const session = await DIDSession.get(userAddress, authMethod, {
          resources: compose.resources,
        });
        compose.setDID(session.did);
        setSessionDid(session.did);
      } catch (error) {
        console.error("Failed to establish session:", error);
        setSessionDid(null);
      }
    }
    establishSession();
  }, [provider, userAddress, setSessionDid]);
}

What in this should I change to authorize the did:key?

After that, do I just run “sessionDid.withCapability(new cacao)”?

Sir, I find your approach right in general. There must be some weird little error inside that prevents you from achieving the goal. I am about to write a PoC/harness to find out where the bug is lurking. When done, going to post the results here.

2 Likes

Here is some more context as to how I am getting the userAddress and the provider as well:

import React, { useEffect } from "react";
import { ethers } from "ethers";
import { useEthereum } from "./ethereumContext.js";

const ConnectWalletButton = () => {
  const { setProvider, setUserAddress, userAddress, setAddresses } = useEthereum();

  useEffect(() => {

  const connectWalletHandler = async () => {
    if (window.ethereum) {
      try {
        await window.ethereum.request({
          method: "wallet_requestPermissions",
          params: [{ eth_accounts: {} }],
        });
        const ethProvider = window.ethereum;
        setProvider(ethProvider);
        const addresses = await ethProvider.request({
          method: "eth_requestAccounts",
        });
        setAddresses(addresses);
        setUserAddress(addresses[0]);
      } catch (error) {
        console.error("Failed to connect MetaMask:", error);
      }
    } else {
      alert(
        "MetaMask is not installed. Please consider installing it: https://metamask.io/download.html"
      );
    }
  };


export default ConnectWalletButton;

Shouldn’t that DIDSession.get be DIDSession.authorize? :eyes:

I just followed the User Sessions documentation for that. From what I can see from the did-session library it seems to return the same thing. The get function with find a userAddress that already has a session and load that. Versus the authorize function creating a different session every time, I think.