Skip to main content

Read Access Control

In this tutorial, we will cover how to authenticate a user to enforce read access control. This tutorial expands on the schema created in the Write Access Control tutorial.

In Kwil, data is primarily read with view procedures. For executing view procedures, the same access control rules are available as for write procedures.

Additionally, Kwil supports two authentication methods for enforcing read access control:

The main difference between Private RPC and Kwil Gateway is that Private RPC requires the user to sign for authentication with each call request, whereas the Kwil Gateway enables a user to sign once for all call requests in a session.

Private RPC Mode

Private RPC is a server-side configuration in kwild that enforces user authentication for each call request. Learn more about private mode here.

By default, data in Kwil is public. To enable private data and read access control, Private RPC mode requires a user to identify themselves via signing a message with each call request. If the signature is valid, the call will return the query result. If the signature is invalid or not provided, the call will return an error.

tip

Private RPC mode is not enabled on the Longhorn Testnet. If you would like to test private mode, run a local node with private RPC enabled.

If your Kwil network is running in private mode, authenticate users by passing a KwilSigner to the call method.

import { WebKwil, KwilSigner } from '@kwilteam/kwil-js';
import { BrowserProvider } from 'ethers';

const kwil = new WebKwil({
kwilProvider: "http://localhost:8484",
chainId: "your-chain-id",
});

async function getSigner() {
// Prompt user to login with their wallet
const provider = new BrowserProvider(window.ethereum);
const ethSigner = await provider.getSigner();
const ethAddress = await ethSigner.getAddress();
return new KwilSigner(ethSigner, ethAddress);
}

async function authenticatedProcedure() {
const kwilSigner = await getSigner();

const payload = {
name: "get_profile",
dbid: 'x123abc...'
}

return await kwil.call(payload, kwilSigner); // user will be prompted to authenticate
}

Kwil Gateway

The Kwil Gateway requires a user to sign a message with their private key. Once the signature is verified, the gateway will return the user a cookie to be passed with all subsequent requests.

tip

The gateway is still in private beta. If you would like access, please contact our team.

KGW cookies are partitioned by account and origin. This means that multiple users can be authenticated at the same time, and users authenticated from one origin (e.g., https://website1.com) will not be authenticated on another origin (e.g., https://website2.com).

Require Authentication in Kuneiform

To signal to the Kwil Gateway that a procedure requires authentication, add an annotation before the procedure. The @kgw(authn='true') annotation will require the user to authenticate via a signature before executing the procedure.

social_media.kf
// authentication required view procedure to reveal a user profile
@kgw(authn='true')
procedure get_profile () public view returns table(id int, username text, age int) {
return SELECT id, username, age
FROM users
WHERE address = @caller;
}
danger

The @kgw(authn='true') annotation will only require the user to authenticate IF the Kwil Gateway is enabled. If the gateway is not enabled, the annotation will be ignored.

Authenticating a User

Using the JavaScript SDK and the KwilSigner, a user can authenticate with the Kwil Gateway (KGW). When calling the call method, the Kwil SDK will check if the gateway is enforcing authentication. If authentication is enforced, the gateway will prompt the user to sign a message with their account keys. Once the signature is verified, the SDK will include a cookie to be passed with all subsequent requests.

tip

When multiple users are authenticated, the current user is the signer passed to the call method.

import { WebKwil, KwilSigner } from '@kwilteam/kwil-js';
import { BrowserProvider } from 'ethers';

const kwil = new WebKwil({
kwilProvider: "your-kgw-url",
chainId: "your-chain-id",
});

async function getSigner() {
// Prompt user to login with their wallet
const provider = new BrowserProvider(window.ethereum);
const ethSigner = await provider.getSigner();
const ethAddress = await ethSigner.getAddress();
return new KwilSigner(ethSigner, ethAddress);
}

async function authenticatedProcedure() {
const kwilSigner = await getSigner();

const payload = {
name: "get_profile",
dbid: 'x123abc...'
}

return await kwil.call(payload, kwilSigner); // user will be prompted to authenticate
}

Logging Out a User

Logging out a user invalidates the cookie from the requesting origin. This means that the user will need to re-authenticate to access any procedures that require authentication.

Log out all signers

To log out all signers that a user has authenticated with, call the logout method with no arguments.

async function logout() {
await kwil.auth.logoutKGW();
}

Log out a specific signer

To log out a specific signer, pass the signer to the logout method.

async function logoutUser() {
const kwilSigner = await getSigner();
await kwil.auth.logoutKGW(kwilSigner);
}

Advanced Usage

Disable Auto Authentication

By default, the Kwil SDK will automatically prompt the user to authenticate when required (for both Private RPC and Kwil Gateway). To disable this behavior, set the autoAuthenticate option to false.

const kwil = new WebKwil({
kwilProvider: "http://localhost:8484",
chainId: "your-chain-id",
autoAuthenticate: false,
});

Manually Authenticate

To manually authenticate a user, call the authenticatePrivateMode or authenticateKGW methods.

Call the authenticatePrivateMode method to authenticate with the Private RPC. Pass the AuthBody that is returned from the authenticatePrivateMode method to the call method.

async function manualAuthenticate() {
const authBody = await kwil.auth.authenticatePrivateMode(kwilSigner); // user will be prompted to authenticate

const payload = {
name: "get_profile",
dbid: 'x123abc...',
authBody
};

return await kwil.call(payload);
}