ceramicnetwork / js-ceramic

Typescript implementation of the Ceramic protocol
http://ceramic.network
Other
415 stars 127 forks source link

Cannot read properties of undefined (reading 'verifyJWS') error during the authentication on local node #2180

Closed cnsndnz closed 2 years ago

cnsndnz commented 2 years ago

Hi,

I'm running a node from localhost and I can't authenticate from http-client. It gives following errors during the multiQuery request. I can't imagine what is wrong and can't find any solutions on the internet.

[2022-05-23T18:29:01.028Z] WARNING: Error loading stream kjzl6cwe1jw14b19mppzy7bf2gwx9w7tdmrep3oxw21h85m3yozvb2qjcfgu7io at time undefined as part of a multiQuery request: TypeError: Cannot read properties of undefined (reading 'verifyJWS')
[2022-05-23T18:29:01.031Z] WARNING: Error loading stream kjzl6cwe1jw14b19mppzy7bf2gwx9w7tdmrep3oxw21h85m3yozvb2qjcfgu7io at commit bagcqcera44qzzzmsr65nwjdnz6ejjwtw3uatnapmfll2d2lu4wgmifuevwia at time undefined as part of a multiQuery request: TypeError: Cannot read properties of undefined (reading 'verifyJWS')

I am using the code below for running the node from localhost.

export async function createIPFS(): Promise<IpfsApi> {
    return IPFS.create({
        ipld: { codecs: [dagJose] },
    });
}
export const makeCeramicCore = async (
    ipfs: IpfsApi,
): Promise<Ceramic> => {
    const core = await Ceramic.create(ipfs, {
        networkName: "testnet-clay",
    });

    // core.dispatcher.messageBus.subscribe((msg) => console.log(msg));
    return core;
};
async function main() {
    const ipfs = await createIPFS();
    const core = await makeCeramicCore(ipfs);
    const port = 7007;

    const daemon = new CeramicDaemon(
        core,
        DaemonConfig.fromObject({
            "http-api": {
                port,
                "cors-allowed-origins": [".*"],
            },
            logger: { "log-level": 0, "log-to-files": false },
            network: {
                name: "testnet-clay",
            },
        }),
    );
    await daemon.listen();
}

And I am trying to authenticate using the code below from an another web app using the @ceramicnetwork/http-client. When I use https://ceramic-clay.3boxlabs.com for connecting to the ceramic there is no problem but I can't authenticate to locally running node.

// const endpoint = "https://ceramic-clay.3boxlabs.com"; // No problem with this endpoint
const endpoint = "http://localhost:7007/";

async authenticate(account: string): Promise<boolean> {
        try {
            this.ceramic = new CeramicClient(endpoint);
            const threeIdConnect = new ThreeIdConnect();
            const authProvider = new EthereumAuthProvider((window as any).ethereum, account);
            await threeIdConnect.connect(authProvider);
            const did = new DID({
                provider: threeIdConnect.getDidProvider(),
                resolver: {
                    ...get3IDResolver(this.ceramic),
                    ...getKeyResolver(),
                },
            });

            await this.ceramic?.setDID(did);
            const result = await this.ceramic?.did?.authenticate();
            (window as any).ceramic = this.ceramic;
            return true;
        } catch (err) {
            console.log(err);
            return false;
        }
    }

Package versions are listed below

    "@ceramicnetwork/cli": "^2.0.3-rc.6",
    "@ceramicnetwork/common": "^2.0.0",
    "@ceramicnetwork/core": "^2.2.0-rc.4",
    "@ceramicnetwork/stream-tile-handler": "^2.0.0",
    "ajv-formats": "^2.1.1",
    "blockcodec-to-ipld-format": "^2.0.0",
    "dag-jose": "^2.0.0",
    "get-port": "^6.1.2",
    "ipfs-core": "~0.13.0",
    "tmp-promise": "^3.0.3"

Thanks for help.

stbrody commented 2 years ago

Any reason why you're creating the CeramicDaemon manually in your own code, instead of starting the Ceramic node via the CLI?

stbrody commented 2 years ago

I think the problem is that the Ceramic node needs to have a DID instance attached with DIDResolvers for whatever DID methods your node will support. If you use the ceramic CLI to launch your Ceramic node, this is taken care of for you. You can see the code for how we do that here: https://github.com/ceramicnetwork/js-ceramic/blob/a940e135c34e58a22200f24141d8ff04aa6ffda5/packages/cli/src/ceramic-daemon.ts#L234-L235

cnsndnz commented 2 years ago

Any reason why you're creating the CeramicDaemon manually in your own code, instead of starting the Ceramic node via the CLI?

We are trying to achieve indexing the data due to updates on the streams that created via our app. So we decided to create the CeramicDaemon in our code. Currently we are planning to use messageBus for this purpose. If a better solution is possible, I would appreciate it if you could share it.

core.dispatcher.messageBus.subscribe((msg) => console.log(msg));

cnsndnz commented 2 years ago

@stbrody I added the below lines and the problem is solved thanks.

export function makeResolvers(
    ceramic: any,
): ResolverRegistry {
    const result = {
        ...ThreeIdResolver.getResolver(ceramic),
        ...KeyDidResolver.getResolver(),
    };
    return result;
}
    const did = new DID({ resolver: makeResolvers(core) });
    await core.setDID(did);

I realized that I can't subscribe the document updates on my own ceramic node via pub-sub feature. I can only see the TileDocument create messages but I don't receive any messages about the TileDocument updates. Is there a way to watch the stream updates on my own node ?

stbrody commented 2 years ago

do you want to see updates to all streams or just to a given stream?

stbrody commented 2 years ago

core.dispatcher.messageBus.subscribe((msg) => console.log(msg));

if your goal is to log all pubsub messages, we already do that with an out-of-the-box ceramic daemon. If you run the ceramic daemon via the CLI and configure it to use file logging, there will be a pubsub.log file in the log directory containing a log of all the pubsub messages sent and received.

If you want to be able to programmatically consume every received pubsub message to the ceramic network, you don't actually even need to run a Ceramic node for that. You can just connect to the /ceramic/mainnet (or /ceramic/testnet-clay) pubsub topic using IPFS or libp2p directly

cnsndnz commented 2 years ago

@stbrody I tried IPFS for subscribing the updates on the streams and it works but still don't understand why is your messageBus doesn't provide any messages on updates. It would be much better if it works in that way without using IPFS and deserializing the data again. :)

Below lines subscribes update & create events without a problem.

    core.ipfs.pubsub.subscribe("/ceramic/testnet-clay", (msg) => {
        const asString = textDecoder.decode(msg.data);
        const parsed = JSON.parse(asString);
        const typ = parsed.typ as MsgType;

        if (typ === 0) {
            console.log(parsed.stream);
        }
    });

Thank you so much.