Authenticated query calls

So i’m trying to develop and app in which i won’t go into too many details, but i have case where i want to have query calls that returns data depending on the user subscription. The data it self is not something that is sensitive but you can imagine like that: Users without authentication will be able to retrieve only todays data, users with one month subscription will be able to retrieve same data for one month.

And since there will be thousand of users retrieving exactly same data just for different period of time without changing data it self it makes sense to have query calls for that, since that’s what they are made for. But it seems there is no way of doing proper authentication for that… I’m using Internet Identity authentication for now which works fine for update calls. But how can i check user authentication for query calls?

I understand that today authentication has to go through consensus or whatever… but in general authentication is just checking signature of the message with public key, i don’t need consensus of the network for that, why there is no normal authentication that just does that?

Yes maybe i can’t trust single node to verify message without consensus, but there has to be something that i can run my self in an easy way.

For example why not a library who does that:
final user = Principal.getFromRequest(request);
where request is some kind of body of the message and it would verify signature of the message and that would be good enough.

But at the moment i can’t even get body of the request:
{916AAA8F-BA17-4450-847E-9C1E6B19AC2A}

this is from AI on internetcomputer.org

It did provide me with some custom solution where i can do it

public query func authenticateQuery(request: Request, signature: Signature) : async Bool {
    let publicKey = getPublicKey(request.userPrincipal);
    let isValid = verifySignature(request, signature, publicKey);
    if (isValid) {
        // process the request
    } else {
        // reject the request
    }
};

But when i asked how that verifySignature would look like if i use Internet Identity as authentication it gived me this:

I apologize for any confusion, but the knowledge sources provided do not contain specific code for a verifySignature function using Internet Identity. The Internet Identity service uses the WebAuthn protocol for user authentication, which involves the creation of a public-private key pair on the users device and the use of digital signatures. However, the specific implementation details of how to verify a signature with Internet Identity are not provided in the knowledge sources.

So basically your AI have no idea how to do it, there is no documentation about it as well. How come there is no simple way to have authenticated query function?

I mean really? If i want to have any authentication i have to have only update calls? That just makes no sense for scaling app at all… instead of running light query call that can be run on one node now i have to run every call on all 13 nodes and clog the network for no reason just to retrieve data and on top of that it will work like 10 times slower…

Looks like you got the LLM to hallucinate quite a lot of bogus…

It’s exactly the same. Every message is signed by the calling identity, and you can access the principal with ic_cdk::caller() or with public shared (msg) func test { msg.caller }

Hi, thanks for your response.

Oh believe me… it’s pain, i’m using copilot and internetcomputer.org at the same time and sometimes it takes hours to get it right. Both produces wrong answers that doesn’t work :smiley:

If i understand correctly it’s rust yes?

By the AI best equivalent for this in motoko is this:

public query func getCaller() : async Principal {
    return msg.caller;
};

but this produces me error that msg is unbound, which makes sense.
i can add shared keyword for that and make it like that:

public shared(msg) query func getCaller() : async Principal {
    return msg.caller;
};

But query keyword doesn’t work as soon as i add (msg) part near shared and without it that shared function is update call which costs cycles.

Basically it works either like that:

public shared query func getCaller() : async Principal {
    return msg.caller; // throws exception
};

or like that:

public shared(msg) func getCaller() : async Principal {
    return msg.caller; // not a query function anymore
};

So that’s my question, how to get caller for query function since shared(msg) and query can’t come together?

Figured it out:

  public query (message) func whoami() : async Principal {
    return message.caller;
  };

Side note: if you mark an answer on internetcomputer.org as bad it will get reviewed eventually and a better response integrated into the training material

Oh nice! Thanks.

I already had workaround with update call to generate access key and then pass that access key to query functions, but it’s a bit ugly and less secure, gonna try this one.

Good to know! I usually just call him stupid, he says sorry and we move on :smiley:

1 Like