fix(together): 招待コードをメンバー全員に返す、JWT username 過剰チェック削除

af51a75で追加した「JWT なし時は invite_code を隠す」制限が原因で
グループ設定画面の招待コードが undefined になっていた。
メンバー確認済みなら全員に返すのが正しい動作。

create/join の「JWT username が一致しなければ 403」チェックも削除。
Together はニックネームベースで JWT identity と別名が普通に使われるため過剰な制限だった。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
posimai 2026-04-20 22:47:36 +09:00
parent 96208363d9
commit e935eb6734
1 changed files with 1 additions and 21 deletions

View File

@ -2619,15 +2619,6 @@ ${excerpt}
r.post('/together/groups', async (req, res) => { r.post('/together/groups', async (req, res) => {
const { name, username } = req.body || {}; const { name, username } = req.body || {};
if (!name || !username) return res.status(400).json({ error: 'name と username は必須です' }); if (!name || !username) return res.status(400).json({ error: 'name と username は必須です' });
// JWT が提示されている場合、body の username と一致するか確認(なりすまし防止)
const jwtUserId = getTogetherJwtUserId(req);
if (jwtUserId) {
const requested = normalizeTogetherUsername(username);
const jwtCandidates = await resolveTogetherUserCandidates(pool, null, jwtUserId);
if (!requested || !jwtCandidates.includes(requested)) {
return res.status(403).json({ error: '認証情報とユーザー名が一致しません' });
}
}
if (!checkRateLimit('together_create', req.ip || 'unknown', 5, 60 * 60 * 1000)) { if (!checkRateLimit('together_create', req.ip || 'unknown', 5, 60 * 60 * 1000)) {
return res.status(429).json({ error: 'グループ作成の上限に達しました。1時間後に再試行してください' }); return res.status(429).json({ error: 'グループ作成の上限に達しました。1時間後に再試行してください' });
} }
@ -2658,15 +2649,6 @@ ${excerpt}
r.post('/together/join', async (req, res) => { r.post('/together/join', async (req, res) => {
const { invite_code, username } = req.body || {}; const { invite_code, username } = req.body || {};
if (!invite_code || !username) return res.status(400).json({ error: 'invite_code と username は必須です' }); if (!invite_code || !username) return res.status(400).json({ error: 'invite_code と username は必須です' });
// JWT が提示されている場合、body の username と一致するか確認(なりすまし防止)
const jwtUserId = getTogetherJwtUserId(req);
if (jwtUserId) {
const requested = normalizeTogetherUsername(username);
const jwtCandidates = await resolveTogetherUserCandidates(pool, null, jwtUserId);
if (!requested || !jwtCandidates.includes(requested)) {
return res.status(403).json({ error: '認証情報とユーザー名が一致しません' });
}
}
if (!checkRateLimit('together_join', req.ip || 'unknown', 10, 60 * 60 * 1000)) { if (!checkRateLimit('together_join', req.ip || 'unknown', 10, 60 * 60 * 1000)) {
return res.status(429).json({ error: '参加試行回数が上限に達しました。1時間後に再試行してください' }); return res.status(429).json({ error: '参加試行回数が上限に達しました。1時間後に再試行してください' });
} }
@ -2697,9 +2679,7 @@ ${excerpt}
if (!(await togetherEnsureMember(pool, res, req.params.groupId, username, jwtUserId))) return; if (!(await togetherEnsureMember(pool, res, req.params.groupId, username, jwtUserId))) return;
const result = await pool.query('SELECT id, name, invite_code, created_at FROM together_groups WHERE id=$1', [req.params.groupId]); const result = await pool.query('SELECT id, name, invite_code, created_at FROM together_groups WHERE id=$1', [req.params.groupId]);
if (result.rows.length === 0) return res.status(404).json({ error: 'グループが見つかりません' }); if (result.rows.length === 0) return res.status(404).json({ error: 'グループが見つかりません' });
const row = result.rows[0]; res.json(result.rows[0]);
if (!jwtUserId) delete row.invite_code;
res.json(row);
} catch (e) { } catch (e) {
res.status(500).json({ error: 'Internal server error' }); res.status(500).json({ error: 'Internal server error' });
} }