🐙

Auth0でDiscordログインの実装+特定サーバーでロールを持っている時だけ認証が通るようにする設定

に公開

Auth0でDiscordのソーシャルログインを使って、特定のサーバーで特定のロールを持っている人だけ認証が通るようにする方法。
先人の人が実装していた方法は、今のDisocrdAPIの仕様を満たしていなくて動かなかったです。

認証用のDiscordアプリケーションの用意

Developersページでアプリケーションを新規作成する。
https://discord.com/developers/applications
OAuth2設定を開いて、OAuth2 URL GeneratorからBotにチェックを入れる。
チェックを入れると出てくるBot Permissionsで、とりあえずManage RolesとView Channelsにチェックを入れて権限をつけておく。
生成されたURLにアクセスして、Botを認証に使うDiscordサーバーに招待しておく。

ソーシャルログインのセットアップ

Auth0の認証=>ソーシャルから、Discordへの接続を作成する。
Client IDとClient Secretには作っておいたDiscordアプリケーションのClient IDとClient Secretを設定。

Discord Developersページのアプリケーション設定内のOAuth2設定で、Redirectsに以下のURLを設定しておく。

https://<Auth0のドメイン>/login/callback

-設定例----
https://<テナント名>.jp.auth0.com/login/callback

トリガーの設定

トリガーはログイン後にnodejsのスクリプトを実行して、追加の認証処理を実装するやつです。
Auth0のアクション=>トリガー=>post-loginからログイン後のトリガーを設定します。
アクションを新規作成して、以下のコードを設定します。

const axios = require('axios');

exports.onExecutePostLogin = async (event, api) => {
  // ---------------------------------------------------
  // 1. ログイン方法の判定
  // ---------------------------------------------------
  // 今回のログインに使われたConnectionが "discord" でないなら
  // チェックを行わずにログインを許可して終了
  if (event.connection.name !== 'discord') {
    return;
  }

  // ---------------------------------------------------
  // 2. Identityの取得と検証
  // ---------------------------------------------------
  const identity = event.user.identities.find((v) => v.connection === "discord");
  
  // Discord経由で来ているはずなのにIdentityがない(通常ありえないが念のため)
  if (!identity) {
    api.access.deny("Discord identity not found.");
    return;
  }

  const userIdMatch = identity.user_id?.match(/discord\|(.+)/);
  const discordUserId = userIdMatch ? userIdMatch[1] : identity.user_id;

  if (!discordUserId) {
    api.access.deny("Invalid Discord User ID format");
    return;
  }

  // ---------------------------------------------------
  // 3. Secretsの取得
  // ---------------------------------------------------
  const botToken = event.secrets.DISCORD_BOT_TOKEN;
  const guildId = event.secrets.DISCORD_GUILD_ID;
  const requiredRoleId = event.secrets.DISCORD_ROLE_ID;

  if (!botToken || !guildId || !requiredRoleId) {
    console.error("Missing secrets configuration.");
    api.access.deny("Server configuration error.");
    return; 
  }

  // ---------------------------------------------------
  // 4. APIによるメンバー・ロール確認
  // ---------------------------------------------------
  try {
    const response = await axios.get(`https://discord.com/api/v10/guilds/${guildId}/members/${discordUserId}`, {
      headers: { Authorization: `Bot ${botToken}` }
    });

    const memberRoles = response.data.roles || [];

    // 指定ロールを持っていない場合
    if (!memberRoles.includes(requiredRoleId)) {
      api.access.deny("You do not have the required role to access this application.");
      return;
    }

    // 問題なければログイン成功
    return;

  } catch (error) {
    if (error.response && error.response.status === 404) {
      // サーバーに参加していない
      api.access.deny("You are not a member of the required Discord server.");
    } else {
      console.error("Discord API Error:", error.message);
      // APIエラー時は安全のため拒否
      api.access.deny("An error occurred while validating Discord membership.");
    }
  }
};

シークレットの設定

コードエディターのサイドバーで以下のシークレットを設定します。

  • DISCORD_BOT_TOKEN
    • Discord Developersのアプリケーション設定で、Bot設定から確認できるトークンを設定します。
  • DISCORD_GUILD_ID
    • DisocrdサーバーのIDを設定。このサーバーに入っているユーザーのみのログインを許可。
  • DISCORD_ROLE_ID
    • ロールのIDを設定。このロールを持っているユーザーのみのログインを許可します。

依存関係の設定

コードの中でaxiosを使用しているので、コードエディターのサイドバーから依存関係タブを開き、axiosのlatestバージョンを設定します。

動作チェック

Auth0の認証=>ソーシャルから、Discordを開いて接続を試すを実行します。
Discordで認証して、認証が通ればOK。

Discussion