How to limit access to a canister within a whitelist of canisters?

I have a book canister, and several game canisters. I want my book canister functions could be call update only by my game canisters. such as below:
book canister function:
publice shared (msg) func addBook(n:Nat): async (){
assert(msg.caller in whitelist_canister);
I wonder whether is the msg.caller the user’s principal that calls the game canister or the game canister principal id?

The caller is always the sender of the message, so if the user makes the call directly to your book canister you get the user’s principal, if the game canister makes (or forwards) the call then you get the game canister’s principal

thanks, it solves my confusion.
there is another question, when I call the book canister from the frontend canister, msg.caller seems meaning the user’s principal, not the frontend canister id:
here is the code from my book canister:

public shared query (msg) func getBalance() : async Nat {
let anonymous_principal = Principal.fromText(“2vxsx-fae”);
assert (msg.caller != anonymous_principal);
switch (book.get(msg.caller)) {
case (?balance) {
return balance;
case (null) {
return 0;
it referenced from the DEFI example.

That’s correct - your frontend canister only serves the files to the user’s browser. The served JS then makes the call, not your canister. If you wanted to proxy the call through the frontend canister then you’d have to implement a custom one