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 available in all server events.
The server offers onbefore*
events to validate authentication logic before a user performs actions. These events let you check things like 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're 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.