会員連携

自分のサイトにログインした会員を、ニックネーム・ID・レベル・権限のままチャットルームに入室させる流れと、入室できないときの確認点を説明します。

最終更新 2026年10月19日

このページ

会員連携を使うと、自分のサイトの会員がサイトでのニックネーム・ID・レベル・権限のままチャットルームに入室します。自分のサーバーが入室情報にチャットルームのトークンで署名し、uchatは署名が正しい入室だけを受け付けるため、ほかの人のニックネームをなりすますことはできません。

動作の流れ

  1. 会員が自分のサイトのページを開きます。
  2. 自分のサーバーが、ログイン中の会員情報(nick、id、level、auth など)とチャットルームID、現在時刻をまとめ、チャットルームのトークンで署名した文字列を作ります。
  3. その文字列を <u-chat user_data="..."> に入れてページを返します。
  4. チャットサーバーが署名を確認し、正しければその情報で入室させます。

トークンは署名を作るためだけに使い、ページには出しません。署名文字列を作るルールとフィールドの全体は会員連携の署名仕様が正式な資料です。

有効にする手順

  1. 管理ページの会員連携メニューで会員連携をオンにします。
  2. 設置コードメニューでサイトの種類(PHP、Node.js、Python、Java、GnuBoardなど)を選び、コードを作成を押します。このコードにはチャットルームのトークンが含まれています。
  3. コード内の会員情報の変数を自分のサイトに合わせて書き換え、サーバーのコードに入れます。

会員連携をオンにした瞬間から、署名のない入室は拒否されます(エラー203)。先にサイトのコードを書き換えてからオンにするほうが安全です。ログインしていない訪問者にも署名が必要です → ログインしていない訪問者。GnuBoardやWordPressなどのCMSは5分で設置を参照してください。

ログインしていない訪問者

会員連携がオンのチャットルームは、すべての入室に署名が必要です。ログインしていない訪問者にも user_data を渡してください。渡さないとエラー203で入室できません。

  • ニックネーム・IDなしで、チャットルームIDと時刻だけを署名するとゲストとして入室します。ゲストのニックネームは自動で付きます。
  • 下のPHP・Node.js・Python・Javaのコードは、会員情報の変数が空ならそのままゲストの署名を作ります。
  • 会員かどうかで <u-chat> や user_data を省くコードを別に書かないでください。
// ログインしていれば会員情報、していなければ空の情報で署名します。ゲストにも user_data を省きません。
const userData = uchatUserData(user ? { nick: user.nickname, id: user.id } : {});

PHP

管理画面が生成するPHPコードです。$joinData の会員情報だけを自分のサイトの変数に置き換えます。PHP 5.6以上に標準で含まれる関数(hash_hmac、bin2hex、rawurlencode)だけを使うので、別途インストールするものはありません。

<?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'] = 'チャットルームのトークン'; // 絶対に公開しないでください
$joinData['nick'] = $nicknameVariable;
$joinData['id'] = $idVariable;
$joinData['level'] = $levelVariable;
$joinData['auth'] = ''; // admin, subadmin, member, guestのいずれか。空ならIDがあれば会員、なければゲスト
$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>

サイトの管理者にだけチャットルームの管理者権限を与えるには、$joinData['auth'] = $is_admin ? 'admin' : ''; のように書きます。

Node.js・Python

管理画面はNode.js・Python・Javaのコードも生成します。どれもその言語の標準ライブラリだけを使います。以下はNode.js(18以上)のコードです。

// サーバーでのみ実行してください。トークンをブラウザに送ってはいけません。
import { createHmac } from 'node:crypto';

const UCHAT_ROOM = 'my-room';
const UCHAT_TOKEN = 'チャットルームのトークン'; // 絶対に公開しないでください

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('|');
}

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

Python(3.8以上):

# サーバーでのみ実行してください。トークンをブラウザに送ってはいけません。
import binascii
import hashlib
import hmac
import time
from urllib.parse import quote

UCHAT_ROOM = 'my-room'
UCHAT_TOKEN = 'チャットルームのトークン'  # 絶対に公開しないでください


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])

作った文字列をHTML属性に入れるときは、テンプレートエンジンのエスケープを通して <u-chat room="my-room" user_data="..."> として出力します。一覧にない言語に移植する場合は、会員連携の署名仕様の確認用サンプル値で結果を照合してください。

Next.js・Nuxt・SvelteKit

サーバーコードのあるフレームワークはサーバーで署名し、画面には署名結果だけを npm パッケージのコンポーネントに渡します。トークンはサーバーの環境変数(.env)に置き、ブラウザーに送りません。

  1. サーバー(Next.js のサーバーコンポーネント、Nuxt の server/api、SvelteKit の +page.server.js)で、上の Node.js の署名関数でログイン中の会員の userData を作ります。
  2. 画面で @u-chat/react・@u-chat/vue・@u-chat/svelte の UchatRoom(ページ内)または UchatWidget(フローティングウィジェット)に userData として渡します。
<!-- +page.svelte: +page.server.js の load がサーバーで uchatUserData(...) で作った userData を渡します -->
<script>
	import { UchatRoom } from '@u-chat/svelte';

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

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

設置コード画面でサイトの種類を Next.js・Nuxt・SvelteKit にすると、署名関数・サーバーコード・画面コードをまとめて作れます。設置の形をフローティングウィジェットにすると、UchatWidget とウィジェット設定が props として入ります。

user_data_urlで読み込む

ページのHTMLをキャッシュしたり静的ファイルとして書き出したりするサイトでは、署名をHTMLに入れるのが難しくなります。その場合は user_data の代わりに user_data_url に自分のサーバーのアドレスを入れます。

<u-chat room="my-room" user_data_url="/uchat/user-data" style="display:inline-block; width:100%; max-width:500px; height:500px;"></u-chat>
  • チャットルームは入室時にこのアドレスの後ろに ?room=room-id を付け、<script> として読み込みます。そのため、アドレスにすでに ? が含まれていてはいけません。
  • レスポンスはJavaScriptで、実行されたときに window.Uchat_cache.token[room-id] に署名文字列を入れる必要があります。

Expressで上の uchatUserData を使って応答する例です。

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'
	});
	// ほかのサイトがこのアドレスを<script>で読み込んで会員の署名を持ち出せないようにする
	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))};`
	);
});

レスポンスはログインセッションで会員を確認してリクエストごとに新しく署名し、Cache-Control: no-store でキャッシュされないようにします。ログインしていなければ空の会員情報で署名し、ゲストとして入室させます。注意点はセキュリティチェックリストにあります。

時刻連携

会員連携メニューの時刻連携(最初はオン)は、古い入室情報を拒否します。誰かがページの user_data をコピーしておき、あとで使うことを防ぎます。

  • 署名に含まれる time(自分のサーバーのUnix秒)とuchatサーバーの現在時刻の差を記憶し、自動で追従します。自分のサーバーの時計が少しずれていても問題ありません。
  • 新しい入室の差が記憶している差より300秒(5分)を超えて過去であれば、エラー205で拒否します。
  • 同じ user_data は最初に使ってから5分間だけ受け付けます。再利用しても記憶している差は変わりません。
  • 標準時刻に固定をオンにすると、差を記憶せずにuchatサーバーの時刻と直接比較します。自分のサーバーの時計が正確な場合に使ってください。
  • サーバーを移転した場合や、時計を5分を超えて戻した場合は、時刻基準 → 初期化を押します。
  • オフにすると time を検査しないため、コピーした user_data をいつでも再利用できてしまいます。

時刻連携をオンにしている場合、user_data を含むページを5分を超えてキャッシュしてはいけません。キャッシュが必要なら user_data_url を使ってください。すでに入室している人は、接続が一時的に切れても、最初に入室してから4時間以内であればuchatが自動で再接続させます。

ニックネームのルール

  • nick は15文字で切ったあと、空白(スペース・タブ・改行)をすべて取り除きます。山田 太郎 は 山田太郎 と表示されます。
  • 空の場合はゲストのニックネームが自動で付きます。
  • 同じニックネームがすでに接続している場合は、後ろに _ とランダムな文字を付けて変更します。会員を区別するときはニックネームではなく id を使ってください。

入室できないとき

エラー 原因と解決方法
トークンがありません。(203) 会員連携がオンのチャットルームに署名なしで入室しました。設置コードを連携コードに置き換え、ログインしていない訪問者にも署名した user_data を渡してください。
トークンが変更されたようです。(202) 署名が一致しません。トークンとチャットルームIDが正しいか、値のある項目を名前順で署名したか、署名仕様にない項目を入れていないか、値を rawurlencode(または encodeURIComponent と同じエンコード)で入れたかを確認します。
ページを再読み込みしてください。(205) 時刻連携の基準より5分を超えて古い入室情報か、同じ user_data を最初に使ってから5分が過ぎています。ページのキャッシュをオフにし、サーバーの時計を動かした場合は時刻基準を初期化してください。
入室パスワードが必要です。(701) 入室パスワードを設定したチャットルームです。モデレーター以上はパスワードなしで入室できます。

管理ページの会員連携画面の下にある入室記録で、直近14日間の成功・失敗の回数と、直近の失敗50件(時刻、原因、入室したIP、リクエストに含まれていたID・ニックネーム、署名の時刻差)を確認できます。失敗はすべて回数に含まれますが、詳しい記録は1分あたり30件までを30日間だけ残します。失敗回数より記録が少ない日には一部のみ記録が付きます。

署名が正しいか自分で確認するには、会員連携の署名仕様の確認用サンプル値を自分のコードに入れ、同じ文字列になるか比較してください。エラーはページスクリプトの after.error イベント(data.code)でも受け取れます。→ イベント

読んでもよくわからない場合は お問い合わせ掲示板に質問を書き込んでください。

商号 UCHAT · 代表者 김경욱 · 事業者登録番号 141-05-32832
所在地 경기도 파주시 와석순환로 61 · 通信販売業届出番号 2013-경기파주-4485호
代表電話 010-8677-8633 · メール support@uchat.io

ⓒ 2013–2026 UCHAT. All rights reserved.