Authentication

Overview

Jspreadsheet Server uses an event-based architecture that allows developers to implement custom authentication tailored to their application's needs.

Authentication Events

On the client side, authentication is provided using the auth property, which is passed to all server events.

The server offers onbefore* events to validate authentication logic before a user performs actions. These events allow you to check JWT tokens or custom access rules. They must return true or false to indicate whether the user can proceed.

Event Description
onbeforeconnect(auth) Validates whether a user is allowed to connect.
onbeforeload(auth) Checks if a user is allowed to load a document.
onbeforechange(auth) Validates spreadsheet changes or allows you to override them before they are applied.

Examples

Owner-Only Access

This example checks if the authenticated user is the owner of the spreadsheet.

Server-side

/** * Method to check if the user has permission to proceed * @param {string} method Purpose of the request (e.g., load, change) * @param {string} guid The document's unique identifier * @param {object} auth Information about the user's authentication * @returns {boolean} */ const Authentication = async function(method, guid, auth) { if (guid) { // Validate signature and get the jwt information let info = await jwt.verify(auth.token, process.env.JWT_SECRET); if (info) { // Get the document information from the database let document = await adapter.get(guid); if (document && info.sub === document.user_id) { // The user is the owner of the document return true; } } } return false; }; // Server setup server({ port: 3000, beforeLoad: async function(guid, auth) { // Return false to block access if the user is not the owner return await Authentication('load', guid, auth); }, load: async function(guid, auth, cachedConfiguration) { return await adapter.load(guid, auth, cachedConfiguration); }, change: async function(guid, changes, auth, onerror) { return await adapter.change(guid, changes, auth, onerror); }, create: async function(guid, config, auth) { return await adapter.create(guid, config, auth); }, destroy: async function(guid, auth) { return await adapter.destroy(guid, auth); } }); 

Client-side

 let token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; intrasheets({ bar: { suggestions: true }, client: { url: 'http://localhost:3009', auth: { token: token }, } }) 

Public Spreadsheet Example

This setup allows any user to access documents marked as public.

/** * Method to check if the user has permission to proceed * @param {string} method Purpose of the request (e.g., load, change) * @param {string} guid The document's unique identifier * @param {object} auth Information about the user's authentication * @returns {boolean} */ const Authentication = async function(method, guid, auth) { if (guid) { // Retrieve the document information from the database let document = await adapter.get(guid); if (document && document.state === 0) { // The document is public return true; } } // The document is either private or does not exist return false; }; // Server setup server({ port: 3000, beforeLoad: async function(guid, auth) { // Allow or block access based on the document's visibility return await Authentication('load', guid, auth); }, load: async function(guid, auth, cachedConfiguration) { return await adapter.load(guid, auth, cachedConfiguration); }, change: async function(guid, changes, auth, onerror) { return await adapter.change(guid, changes, auth, onerror); }, create: async function(guid, config, auth) { return await adapter.create(guid, config, auth); }, destroy: async function(guid, auth) { return await adapter.destroy(guid, auth); } }); 

Access Levels

Jspreadsheet recommends the use of three access levels:

  • Owner (2) — full access
  • Editor (1) — can edit
  • Viewer (0) — read-only

These levels are set and validated by your application logic.

/** * This method retrieves the user's access level for the specified document. * @param {string} guid The document's unique identifier * @param {object} auth The authentication object containing token and invitation information * @returns {number|boolean} Returns the user's access level (0 for read-only, 1 for editor, 2 for owner) or false if no access */ const getUserLevel = async function(guid, auth) { // Validate signature and get the jwt information let info = await jwt.verify(auth.token, process.env.JWT_SECRET); // Retrieve the document information from the database let document = await adapter.get(guid); if (document) { // Check if the user is the owner (user_id matches the subject in the JWT token) if (info && info.sub === document.user_id) { return 2; // Owner level } // Check if the spreadsheet is public (privacy flag is false) if (!document.spreadsheet.privacy) { return 1; // Editor privileges } // Check if the user has an invitation to access the document if (auth.invitation && document.users && document.users.length) { for (let i = 0; i < document.users.length; i++) { // Match the invitation code with the invited users if (document.users[i].hash === auth.invitation) { // Return the access level based on the invitation (0: read-only, 1: editor, 2: owner) return document.users[i].level; } } } } // No access granted return false; } /** * Method to check if the user has permission to proceed * @param {string} method The request's purpose (e.g., 'load', 'change') * @param {string|null} guid The document's unique identifier (or null if connecting to the server) * @param {object} auth The authentication object containing token and invitation information * @return {Promise<boolean>} Returns true if the user is authorized, false otherwise */ const Authentication = async function(method, guid, auth) { // If a document is being accessed if (guid) { // Get the user's access level for the specified document let level = await getUserLevel(guid, auth); if (level !== false) { // Any invited user can connect or load the document. Only editors (1) or owners (2) can make changes. if (level === 0) { // Read-only users cannot change the spreadsheet if (method !== 'change') { return true; } } else { // Editors and owners have full access return true; } } } else { // Would you like to allow everyone to connect? return true; } // If none of the conditions are met, deny access return false; } 

🔐 Reminder: Always validate JWT signatures and securely manage access. The above examples skip some checks for simplicity.