Problem in Websocket set up

I am trying to implement authenticated call in websocket.
I referred the ic-websocket-cdk-mo repo.

import IcWebSocket, { generateRandomIdentity } from "ic-websocket-js";
import { canisterId, pingpong_backend } from "../../../declarations/pingpong_backend";

// Production
// const gatewayUrl = "wss://gateway.icws.io";
// const icUrl = "https://icp0.io";
 
// Local test
const gatewayUrl = "ws://127.0.0.1:8080";
const icUrl = "http://127.0.0.1:4943";
 
export const ws = new IcWebSocket(gatewayUrl, undefined, {
   canisterId: canisterId,
   canisterActor: pingpong_backend,
   identity: generateRandomIdentity(),
   networkUrl: icUrl,
});

this is how they are generating websocket in their Example repo.

now to make authenticated call i added code to get identity.

if (authClient.isAuthenticated()) {
   _identity = authClient.getIdentity();
}

Through above method i get _identity of type Identity
but in parameters of IcWebSocket() function identity must be of type SignIdentity.

so how to make authenticated call in websocket ?

WebSocket is a project of Omnia-network, maybe @ilbert can help?

1 Like

The authClient.getIdentity() method returns a DelegationIdentity (which extends a SignIdentity) if the login process has been completed successfully.
So, you just need to cast the type:

_identity = authClient.getIdentity() as DelegationIdentity; // or: as SignIdentity

We do something similar here:

Does this help?

I tried this.
but my local websocket gateway is not authenticating. it gives error.

What error(s) are you getting?

Can you please detail here the configuration of your local environment (what command did you use to run the WS Gateway, what URL are you connecting to from the frontend, etc.)?

If you can provide these details, I can better help you!

Let me know

what command did you use to run the WS Gateway

I used cargo run.

what URL are you connecting to from the frontend

// Local test
const gatewayUrl = "ws://127.0.0.1:8080";
const icUrl = "http://127.0.0.1:4943";

Error

Thanks for providing those details.

It looks like there’s something wrong with the identity passed to the IC WebSocket class. I’m investigating it, since there seems to be an issue with the ic-websockets-chat-mo example too.

In the meantime, can you also provide a snippet of the code that you’re using on the frontend to authenticate the user and open the WebSocket connection?

Update: the chat demo is working again, thanks @iamenochchirima for the quick reply!

@krunal so, if you can provide the frontend code snippet, we may be able to solve your issue.

1 Like

Sure.

Code for authentication

const handleAuthentication = async () => {
    const authClient = await AuthClient.create();

    try {
      await authClient.login({
        identityProvider: "https://identity.ic0.app/#authorize",
        onSuccess: async () => {
          const identity = authClient.getIdentity();
          // console.log("Authenticated identity:", identity);
          setAuthenticated(true);
          window.location.reload();
        },
      });
    } catch (error) {
      console.error("Authentication error:", error);
    }
  };

Code for ws.ts file

import IcWebSocket, { generateRandomIdentity } from "ic-websocket-js";
import { canisterId, pingpong_backend } from "../../../declarations/pingpong_backend";
import { AuthClient } from "@dfinity/auth-client";
import { SignIdentity } from "@dfinity/agent";
import { DelegationIdentity } from "@dfinity/identity";

// Production
// const gatewayUrl = "wss://gateway.icws.io";
// const icUrl = "https://icp0.io";

// Local test
const gatewayUrl = "ws://127.0.0.1:8080";
const icUrl = "http://127.0.0.1:4943";

let _identity;

const authClient = await AuthClient.create();
if (await authClient.isAuthenticated()) {
   _identity = authClient.getIdentity() as DelegationIdentity;
   console.log("Authenticated identity:", _identity);
   // console.log("Authenticated identity:", _identity);
}

export const ws = new IcWebSocket(gatewayUrl, undefined, {
   canisterId: canisterId,
   canisterActor: pingpong_backend,
   identity: _identity ? _identity as SignIdentity: generateRandomIdentity(),
   networkUrl: icUrl,
});

I think I found the error.

Here, you’re authenticating using the Internet Identity mainnet canister. This creates a DelegatedIdentity signed by the mainnet canister, and that’s why the local replica cannot verify the signature.

The solution is to deploy a local instance of the Internet Identity (docs) and authenticate a user from your frontend using that instance. Then, after deploying the Internet Identity canister locally, you have to change the following line:

- identityProvider: "https://identity.ic0.app/#authorize",
+ identityProvider: "http://<internet-identity-loca-canister-id>.localhost:4943/",

You may want to control this value through an environment variable like DFX_NETWORK. We did something similar here.

Let me know if it works!


Just a side note: When you deploy to mainnet, I think you don’t need the #authorize suffix in the identityProvider link. Just https://identity.ic0.app/ should work.

1 Like