Skip to main content

User authentication

In order to use the collab service, users must be signed in and authenticated with the server.

The server supports three methods of authentication: token based authentication, email and password authentication, and anonymous authentication.

In most cases, you will want to use token based authentication, as it is the most flexible.

Read both authentication methods below and choose what one works best for you.

Note

Email and password authentication requires the COLLAB_KEY environment variable to be set. Read more here

Token based authentication#

Token based authentication uses an authentication token that is passed between the server and client. The token is used to validate the users session.

In this flow, you must provide us with a function that accepts an authentication token, and returns a user id. This leaves the token generation and validation totally up to you, making this the most flexible option.

How it works#

You must pass a getUserFromToken function to the server constructor. This function will accept a token, context, and the cookies from the request (as an object), and must return either an object with the users id if the token is valid, or null otherwise.

import CollabServer from '@pdftron/collab-server';
const server = new CollabServer({
getUserFromToken: async (token, ctx, cookies) => {
const user = await decodeUserFromToken(token);
if (user) {
return { id: user.id };
} else {
return null;
}
}
});

On the client, call the loginWithToken function, and pass in the authentication token that your application is using. This token will be passed into the getUserFromToken token for decoding.

collabClient.loginWithToken(token);

Example#

This example shows one possible way you could handle this flow using JWT. Keep in mind you do not have to use JWT, this is just one possible case.

Server

import CollabServer from '@pdftron/collab-server';
import express from 'express';
import jwt from 'jsonwebtoken';
// this should be an env variable in a real word scenario
const SECRET_KEY = 'ABCEF';
const app = express();
app.use(express.json());
// Create a login endpoint using an express server
app.post('/login', async (req, res) => {
const { email, password } = req.body;
// validate the email and password
// using your own flow
const user = await validateUser(email, password);
if (!user) {
// send 401 unauthorized response if email or pass is invalid
return res.status(401).send();
}
// generate a JWT using the 'jsonwebtoken' package
// here we only encode the users id because thats all that is required
const token = jwt.sign(
{
id: user.id
},
SECRET_KEY
);
return res.status(200).send({ token });
});
app.listen(3001, () => {
console.log('Listening on port 3001');
});
// create an instance of the collab server,
// and pass in the 'getUserFromToken' option.
const server = new CollabServer({
...otherOptions,
getUserFromToken: async (accessToken) => {
try {
// decode the JWT using the 'verify' function.
const payload = jwt.verify(accessToken, SECRET_KEY);
// if the token is valid, the payload will equal
// {id: 'usersId'}, because this is what
// we encoded in the /login endpoint above
if (payload) {
return payload;
} else {
return null;
}
} catch (e) {
return null;
}
}
});
// start the collab server
server.start(3000);

Client

import { CollabClient } from '@pdftron/collab-client';
const client = new CollabClient({
...options
});
const submitButton = document.getElementById('loginButton');
// set up an onclick handler for your login form
submitButton.onClick = async () => {
// send the email and password to the /login endpoint (setup above)
const resp = await fetch('http://localhost:3001/login', {
body: {
email: document.getElementById('emailField').value,
password: document.getElementById('passwordField').value
},
method: 'post'
});
if (resp.status === 200) {
const { token } = await resp.json();
// login the collab client with your token
// this will pass the token to the `getUserFromToken` function
// passed to the server, and validate the session
await client.loginWithToken(token);
}
};

Email and password authentication#

Email an password authentication is similar to token authentication, except the collab server handles the token generation, and instead you just verify a email and password.

Once a persons email and password is verified, we generate a JWT and set it as a cookie. This cookie is used to authenticate the user in future requests.

To use this method, an environment variable called COLLAB_KEY must be set to a string. This will be the secret key we use to encode the JWT. Make sure to not share this string with anyone. Treat it like a password!

How it works#

To use email and password authentication, you must pass a verifyPassword function to the constructor. The function accepts an email, password, and context and must return a promise that resolves to a boolean indicating if the email and password are valid.

import CollabServer from '@pdftron/collab-server';
const server = new CollabServer({
verifyPassword: async (email, password, context) => {
const user = await myDB.query(`SELECT * FROM users WHERE email = ${email} LIMIT 1`);
if (!user) return false;
const hashedPassword = user.password;
const passwordValid = comparePassword(password, hashedPassword);
return passwordValid;
}
});
server.start(3000);

Now on the client, you call the loginWithPassword function with the users email and password. This gets passed in to your verifyPassword function on the server, and if returns true, the user is logged in!

Example#

Server

import CollabServer from '@pdftron/collab-server';
const server = new CollabServer({
...otherOptions,
verifyPassword: async (email, password) => {
const user = await myDB.query(`SELECT * FROM users WHERE email = ${email} LIMIT 1`);
if (!user) return false;
const hashedPassword = user.password;
const passwordValid = comparePassword(password, hashedPassword);
return passwordValid;
}
});
server.start(3000);

Client

import { CollabClient } from '@pdftron/collab-client';
const client = new CollabClient({
...options
});
const submitButton = document.getElementById('loginButton');
// set up an onclick handler for your login form
submitButton.onClick = async () => {
const email = document.getElementById('emailField').value;
const password = document.getElementById('passwordField').value;
try {
const { user } = await client.loginWithPassword(email, password);
// user logged in!
} catch (e) {
// error occurred, probably invalid credentials
}
};

Anonymous authentication#

The Collaboration system allows you to sign in users anonymously (no username or password). There is no additional server config required for this feature.

To log in a user anonymously on the client, see this guide.

Context#

Both types of authentication functions accept context as a parameter.

See this guide for more information on context.

Troubleshooting#

If you are experiencing issues setting up user authentication, we recommend enabling authentication specific debug logs on both the server and client:

// Server side
const server = new CollabServer({
logLevel: CollabServer.LogLevels.DEBUG,
filterLogsByTag: CollabServer.LogTags.AUTH,
})
// Client side
const client = new CollabClient({
logLevel: CollabClient.LogLevels.DEBUG,
filterLogsByTag: CollabClient.LogTags.AUTH
})

Once enabled, logs related to authentication can be seen in both your server and client consoles. These logs can provide insight into what might be going wrong.