Connection Resources

How to create connection specific resources to store user login information

In some cases you might want the client to get connection specific information, most commonly information on the user which the connection is logged in as. Since the connection’s access token is not available to the client, we need some other way to send the data to the client.

In the Client Session example, we can see how this is done using connection resources. Let’s have a closer look at how it works!

Connection ID tag

The connection ID tag1 is a specific string, "{cid}" (without the quotation marks), that may be used as part of a resource ID2. Resgate will replace the tag with the client’s actual connection ID3 before passing any request further to the services.

On the client, it could look like this:

client.get('session.user.{cid}').then(model => { /* ... */ });

Serving connection resources

When receiving the request on the service, the tag will have been replaced with the actual connection ID. In order to listen to such requests, we may use a wildcard subscription as described in Chapter 2 - Basic Concepts, like this:

nats.subscribe('get.session.user.*', (req, reply, subject) => {
	// Remove 'session.user.' from subject to get cid
	let subjectCID = subject.substr(17);
	/* ... */
});

To deny access to others than the connection owner, we can validate that the connection ID in the resource matches the one making the request:

nats.subscribe('access.session.user.*', (req, reply, subject) => {
	let { cid } = JSON.parse(req);
	nats.publish(reply, JSON.stringify({ result: {
		get: cid == subject.substr(20)
	}}));
});

User info model

Since a get request does not contain an access token, we need to rely on the connection ID to lookup what user is logged in on that connection. In the Client Session example, this is done with the following code:

// User get listener for model access.session.user.{cid}
nats.subscribe('get.session.user.*', (req, reply, subject) => {
	let cid = subject.substr(17); // Remove 'get.session.user.' from subject

	// Check if the connection is logged in to a session
	let session = sessionsByCID[cid];
	if (session) {
		// Return a model with the session's user information
		let { user } = session;
		nats.publish(reply, JSON.stringify({ result: { model:
			{ id: user.id, name: user.name, role: user.role }
		}}));
	} else {
		// Return a model with all properties set to null
		nats.publish(reply, JSON.stringify({ result:
			{ model: { id: null, name: null, role: null }}
		}));
	}
});

Updating the model

When the user logs in, we need to to update the model to reflect this change. Since the user info model is like any other model, we update the connection status using an ordinary change event. In the example, this is done like this:

nats.publish('event.session.user.' + cid + '.change', JSON.stringify({
	values: { id: user.id, name: user.name, role: user.role }
}));

When the user logs out, the model’s properties are cleared:

nats.publish('event.session.user.' + cid + '.change', JSON.stringify({
	values: { id: null, name: null, role: null }
}));

Listening on the client

Finally, on the client, we may get the model and listen to events whenever it changes:

client.get('session.user.{cid}').then(user => {
	let showUserStatus = () => {
		if (user.id) {
			console.log("Logged in as " + user.name);
		} else {
			console.log("Logged out");
		}
	};
	model.on('change', showUserStatus);
	showUserStatus();
});

Now the client can track its login status in real-time, just like with all the other resources.