Member integration

How members logged in to your site join the chat room with their nickname, ID, level, and permissions, and what to check when they cannot join.

Last updated October 19, 2026

On this page

With member integration, members of your site join the chat room with the same nickname, ID, level, and permissions they have on your site. Your server signs the join data with the chat room token, and uchat accepts only joins with a valid signature, so no one can impersonate another person's nickname.

How it works

  1. A member opens a page on your site.
  2. Your server collects the logged-in member's info (nick, id, level, auth, and so on), the chat room ID, and the current time, and builds a string signed with the chat room token.
  3. It puts that string in <u-chat user_data="..."> and sends the page.
  4. The chat server checks the signature and, if it is valid, lets the member join with that info.

The token is used only to create the signature and never goes out in the page. The rules for building the signed string and the full field list are defined in Member integration signature spec.

Turning it on

  1. In the Member integration menu on the admin page, turn on Member integration.
  2. In the Install code menu, choose a Site type (PHP, Node.js, Python, Java, GnuBoard, and so on) and click Create code. This code contains the chat room token.
  3. Change the member info variables in the code to match your site and add it to your server code.

As soon as you turn on member integration, joins without a signature are rejected (error 203). It is safer to update your site code first and then turn it on. Visitors who are not logged in need a signature too → Visitors who are not logged in. For CMSs such as GnuBoard and WordPress, see 5-minute setup.

Visitors who are not logged in

A chat room with member integration on needs a signature on every join. Give a user_data to visitors who are not logged in as well; without it they cannot join (error 203).

  • Sign only the chat room ID and time, with no nickname or ID, and the visitor joins as a guest. A guest nickname is added automatically.
  • The PHP, Node.js, Python and Java code below makes a guest signature as is when the member variables are empty.
  • Do not write separate code that leaves out <u-chat> or user_data depending on whether someone is a member.
// Sign the member's information when logged in, or empty information otherwise. Guests still get a user_data.
const userData = uchatUserData(user ? { nick: user.nickname, id: user.id } : {});

PHP

This is the PHP code the admin page generates. Replace only the member info in $joinData with your site's variables. It uses only functions built into PHP 5.6 and later (hash_hmac, bin2hex, rawurlencode), so there is nothing extra to install.

<?php
if(!function_exists('uchat_array2data')) {
	function uchat_array2data($arr) {
		$arr['time'] = time();
		$token = $arr['token'];
		unset($arr['token']);
		ksort($arr, SORT_STRING);
		$msg = 'uchat-v2';
		$out = array();
		foreach ($arr as $k => $v) {
			$v = (string)$v;
			if ($v === '') continue;
			$msg .= "\n".$k.'='.bin2hex($v);
			$out[] = $k.' '.rawurlencode($v);
		}
		$out[] = 'hash v2.'.hash_hmac('sha256', $msg, $token);
		return implode('|', $out);
	}
}
$joinData = array();
$joinData['room'] = 'my-room';
$joinData['token'] = 'chat room token'; // Never make this public
$joinData['nick'] = $nicknameVariable;
$joinData['id'] = $idVariable;
$joinData['level'] = $levelVariable;
$joinData['auth'] = ''; // One of admin, subadmin, member, guest. If empty: member when an ID is set, otherwise guest
$joinData['icons'] = $iconUrlVariable;
?>
<script async src="https://client.uchat.io/uchat.js"></script>
<u-chat room='<?php echo $joinData['room'];?>' user_data='<?php echo uchat_array2data($joinData); ?>' style="display:inline-block; width:100%; max-width:500px; height:500px;"></u-chat>

To give chat room admin permission only to your site admins, write something like $joinData['auth'] = $is_admin ? 'admin' : '';.

Node.js and Python

The admin page also generates Node.js, Python, and Java code. All of them use only the language's standard library. Below is the Node.js (18 or later) code.

// Run this on the server only. Never send the token to the browser.
import { createHmac } from 'node:crypto';

const UCHAT_ROOM = 'my-room';
const UCHAT_TOKEN = 'chat room token'; // Never make this public

export function uchatUserData(user, now = Math.floor(Date.now() / 1000)) {
	const fields = Object.entries({ ...user, room: UCHAT_ROOM, time: now })
		.filter(([, v]) => v !== undefined && v !== null && v !== '')
		.map(([k, v]) => [k, String(v)])
		.sort(([a], [b]) => (a < b ? -1 : 1));
	const message = ['uchat-v2', ...fields.map(([k, v]) => `${k}=${Buffer.from(v, 'utf8').toString('hex')}`)].join('\n');
	const hash = createHmac('sha256', UCHAT_TOKEN).update(message, 'utf8').digest('hex');
	return fields
		.map(([k, v]) => `${k} ${encodeURIComponent(v)}`)
		.concat(`hash v2.${hash}`)
		.join('|');
}

// Example (Express): uchatUserData({ nick: req.user.nickname, id: req.user.id, level: req.user.level, auth: req.user.isAdmin ? 'admin' : '' })

Python (3.8 or later):

# Run this on the server only. Never send the token to the browser.
import binascii
import hashlib
import hmac
import time
from urllib.parse import quote

UCHAT_ROOM = 'my-room'
UCHAT_TOKEN = 'chat room token'  # Never make this public


def uchat_user_data(user, now=None):
    data = dict(user, room=UCHAT_ROOM, time=int(time.time() if now is None else now))
    fields = sorted((k, str(v)) for k, v in data.items() if v is not None and str(v) != '')
    message = '\n'.join(['uchat-v2'] + ['%s=%s' % (k, binascii.hexlify(v.encode('utf-8')).decode('ascii')) for k, v in fields])
    digest = hmac.new(UCHAT_TOKEN.encode('utf-8'), message.encode('ascii'), hashlib.sha256).hexdigest()
    parts = ['%s %s' % (k, quote(v, safe="!~*'()")) for k, v in fields]
    return '|'.join(parts + ['hash v2.' + digest])

When you put the resulting string in an HTML attribute, pass it through your template engine's escaping and output it as <u-chat room="my-room" user_data="...">. When porting to a language not listed here, check your output against the test values in Member integration signature spec.

Next.js, Nuxt and SvelteKit

Frameworks with server code sign on the server and pass only the result to the npm package component on the page. Keep the token in a server environment variable (.env) and never send it to the browser.

  1. On the server (a Next.js server component, Nuxt server/api, SvelteKit +page.server.js), make the logged-in member's userData with the Node.js signing function above.
  2. On the page, pass it as userData to UchatRoom (in the page) or UchatWidget (the floating widget) from @u-chat/react, @u-chat/vue or @u-chat/svelte.
<!-- +page.svelte: the load in +page.server.js passes userData made on the server with uchatUserData(...) -->
<script>
	import { UchatRoom } from '@u-chat/svelte';

	let { data } = $props();
</script>

<UchatRoom room="my-room" userData={data.userData} width="100%" height={500} />

Choose Next.js, Nuxt or SvelteKit as the site type on the install code screen to get the signing function, server code and page code together. If you choose the floating widget as the placement, the code uses UchatWidget with your widget settings as props.

Loading with user_data_url

Sites that cache page HTML or export static files have a hard time putting the signature in the HTML. In that case, put your server URL in user_data_url instead of user_data.

<u-chat room="my-room" user_data_url="/uchat/user-data" style="display:inline-block; width:100%; max-width:500px; height:500px;"></u-chat>
  • When joining, the chat room appends ?room=room-id to this URL and loads it with a <script>. So the URL must not already contain a ?.
  • The response is JavaScript. When it runs, it must put the signed string into window.Uchat_cache.token[room-id].

Here is an example that responds with the uchatUserData above in Express.

app.get('/uchat/user-data', (req, res) => {
	res.set({
		'Content-Type': 'application/javascript; charset=utf-8',
		'Cache-Control': 'no-store, private',
		'X-Content-Type-Options': 'nosniff'
	});
	// Stop other sites from loading this URL with <script> and taking the member's signature
	if (req.get('Sec-Fetch-Site') === 'cross-site') return res.status(403).send('/* forbidden */');
	const user = req.user ? { nick: req.user.nickname, id: req.user.id, auth: req.user.isAdmin ? 'admin' : '' } : {};
	const room = JSON.stringify(UCHAT_ROOM);
	res.send(
		'window.Uchat_cache=window.Uchat_cache||{};window.Uchat_cache.token=window.Uchat_cache.token||{};' +
			`window.Uchat_cache.token[${room}]=${JSON.stringify(uchatUserData(user))};`
	);
});

The response identifies the member from the login session, signs fresh for every request, and uses Cache-Control: no-store so it is not cached. If the visitor is not logged in, sign empty member info so they join as a guest. Points to watch are in the Security checklist.

Time sync

Time sync in the Member integration menu (on by default) rejects old join data. It stops someone from copying the user_data from a page and using it later.

  • uchat remembers the difference between the time in the signature (Unix seconds on your server) and the uchat server's current time, and follows it automatically. Your server clock can be slightly off.
  • If a new join's difference is more than 300 seconds (5 minutes) further in the past than the remembered difference, it is rejected with error 205.
  • The same user_data is accepted only for 5 minutes after its first use. Reusing it does not change the remembered difference.
  • With Fixed standard time on, uchat does not remember a difference and compares directly with the uchat server time. Use it when your server clock is accurate.
  • If you moved servers or set your clock back by more than 5 minutes, click Time reference → Reset.
  • If you turn it off, time is not checked, so copied user_data can be reused at any time.

With time sync on, do not cache pages containing user_data for more than 5 minutes. If you need caching, use user_data_url. People who have already joined are reconnected automatically by uchat if their connection drops briefly, within 4 hours of first joining.

Nickname rules

  • nick is cut to 15 characters, and then all whitespace (spaces, tabs, line breaks) is removed. John Doe shows as JohnDoe.
  • If it is empty, a guest nickname is assigned automatically.
  • If the same nickname is already connected, _ and random characters are appended. To tell members apart, use id, not the nickname.

When joining fails

Error Cause and fix
The token is missing. (203) You joined a chat room with member integration on, without a signature. Replace the install code with the integration code, and give a signed user_data to visitors who are not logged in as well.
Your token seems to have changed. (202) The signature does not match. Check that the token and chat room ID are correct, that you signed the fields that have a value in name order, that you added no field missing from the signature spec, and that you encoded values with rawurlencode (or an equivalent encoding such as encodeURIComponent).
Please reload the page. (205) The join data is more than 5 minutes older than the time sync reference, or more than 5 minutes have passed since the same user_data was first used. Turn off page caching, and if you moved your server clock, reset the time reference.
An entry password is required. (701) This chat room has an entry password. Moderators and above join without it.

Under Join log at the bottom of the Member integration page on the admin page, you can see success and failure counts for the last 14 days and the 50 most recent failures (time, cause, IP that joined, ID and nickname sent in the request, and signature time difference). Every failure is counted, but detailed records are kept for 30 days, up to 30 per minute. Days with fewer records than failures are marked Partly logged.

To check your signature yourself, put the test values from Member integration signature spec into your code and compare whether you get the same string. You can also receive errors in a page script through the after.error event (data.code). → Events

Still not sure how it works? Ask your question on the contact board.

Business name UCHAT · CEO 김경욱 · Business registration no. 141-05-32832
Address 경기도 파주시 와석순환로 61 · Mail-order business registration no. 2013-경기파주-4485호
Phone 010-8677-8633 · Email support@uchat.io

ⓒ 2013–2026 UCHAT. All rights reserved.