1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GAS (Google App Script) を使ってDifyからLINEログインへ遷移

Last updated at Posted at 2025-10-01

概要

Webサイトに埋め込んだDifyチャットアプリ上でユーザーの LINE認証(LINEログイン)や LINE通知(Messaging API)などのフローを展開したい場合、コールバック処理とPush送信を担う中継サーバーが必要になる。
Google Apps Script(GAS) は公開Webアプリ、スプレッドシート、定期実行、メール送信を無償で一体運用でき、DifyとLINEの間をつなぐ役割を最小構成で果たせる。

この記事ではチャットからLINEログインへ遷移させ、ユーザーのLINE userIdを取得する方法を記録しておく。
※尚、LINEトーク上でチャットを展開する想定ではないため、DifyのLINEプラグインは使わない。

役割分担

  • Dify(司令塔): 会話UI、意思決定、HTTP呼び出し。外部からのリダイレクト(OAuthコールバック)を受けてサーバー処理する役割は持たない。
  • LINE(認証局/配達網): LINEログインでユーザー本人性を確認し、Messaging APIで特定ユーザーにPush配達。
  • GAS(受付/中継): WebアプリURLでLINEログインのコールバックを受け、認可コード→アクセストークン→ユーザーID取得、スプレッドシート保存。定期実行で在庫を監視し、メール/LINEに通知。

ポイント:

  • コールバック先はブラウザから直接叩かれる公開エンドポイントが必要
  • 受け取ったcodeを裏で交換・保存するサーバー処理が必要

これをDify内に作るのは難しのでGASで行う。

全体アーキテクチャ

  1. チャット上でユーザーにLINEログインURLを提示
  2. ユーザーがURLをクリックし、遷移先でLINEログインに同意
  3. LINEがGASのWebアプリ(doGet)へリダイレクト
  4. GASがcodeをトークンに交換し、ユーザープロファイルからuserIdを取得

事前準備(最低限)

1) LINE Developers(プロバイダー/チャネル作成)**

  • LINEログイン チャネル
    • チャネルID / チャネルシークレットを控える
    • コールバックURLにGASのWebアプリURL(後述)を登録
    • Scope: profile openid
  • Messaging APIは本稿では不要(通知は扱わない)

2) GASプロジェクト

  • スプレッドシートを用意(例:UserMappingsシートにconversation_id(or user_id) / line_user_id / linked_at
  • Script Propertiesに以下を保存
    • LINE_LOGIN_CHANNEL_ID
    • LINE_LOGIN_CHANNEL_SECRET
  • Webアプリとしてデプロイ →URLを控えて以下に登録する
    • LINEログインチャネルのコールバックURL
    • Difyチャットフローの環境変数(後述)

3) Difyチャットフロー

  • チャットフローアプリの作成
  • 環境変数の作成(スタジオ画面右上のENVアイコンから)
    • line_login_channel_id: LINEログインのチャネルID
    • gas_webapp_url: GASのウェブアプリURL(※シークレット設定可能)

実装

ステップ1:DifyでLINEログインURLを提示

CodeノードでURLを生成して、回答ノードでリンクを出す。stateにはsys.user_idを使う。

【Codeノード】の設定

  • 入力変数:

    • client_id: line_login_channel_id(環境変数)
    • redirect_uri: gas_webapp_url(環境変数)
    • user_id: sys.user_id(システム変数)
  • Python3コード:

    import urllib.parse
    
    def main(client_id: str, redirect_uri: str, user_id: str) -> dict:
        base = "https://access.line.me/oauth2/v2.1/authorize"
        params = {
            "response_type": "code",
            "client_id": client_id,          # LINEログインチャネルID
            "redirect_uri": redirect_uri,  # GAS WebアプリURL(コールバック)
            "state": user_id,                # Difyの sys.user_id
            "scope": "profile openid"
        }
        login_url = f"{base}?{urllib.parse.urlencode(params)}"
        return {
            "line_login_url": login_url
        }
    
  • 出力変数: line_login_url

【回答ノード】
[LINEアカウントを連携する]({{[上のコードノード].line_login_url}}) でリンクを案内

Difyのコードノード(Python) は、入力変数を引数とするmain関数を呼び出す。
戻り値は出力変数名に対応するdictを返す。

ステップ2:GASのdoGetがcodeとstateを受け取る

遷移先画面でユーザーがLINEログインに同意すると、GASのdoGet()関数がステップ1でログインURLに組み込んだcodestateを受け取る。

const CONF = {
  // 事前準備でScript Propertiesに登録した変数を取得
  ID: PropertiesService.getScriptProperties().getProperty('LINE_LOGIN_CHANNEL_ID'),
  SECRET: PropertiesService.getScriptProperties().getProperty('LINE_LOGIN_CHANNEL_SECRET'),
};

function doGet(e) {
  const code = e.parameter.code;
  const state = e.parameter.state; // Difyのsys.user_id
  if (!code || !state) return HtmlService.createHtmlOutput('invalid request');

  const token = exchangeToken_(code);      // code→access_token
  const prof  = fetchProfile_(token.access_token); // userId取得(後述)

  return HtmlService.createHtmlOutput('連携が完了しました。チャットに戻ってください。');
}

function exchangeToken_(code) {
  const res = UrlFetchApp.fetch('https://api.line.me/oauth2/v2.1/token', {
    method: 'post',
    payload: {
      grant_type: 'authorization_code',
      code,
      redirect_uri: ScriptApp.getService().getUrl(), // 事前登録と一致させる
      client_id: CONF.ID,
      client_secret: CONF.SECRET,
    },
    muteHttpExceptions: true
  });
  const json = JSON.parse(res.getContentText());
  if (json.error) throw new Error(json.error_description || 'token error');
  return json;
}
  • redirect_uriはLINE DevelopersでコールバックURLに登録したURLと完全一致させる
  • SpreadsheetApp.flush()で書き込みの遅延反映を抑止
  • 検証用にログ(console.log)でstateuserIdを記録しておくと便利

ステップ3:プロフィールAPIからLINE IDを取得・保存

fetchProfile_()を定義。
でプロフィールAPIを呼び出し、userIdを取得する(以後の通知やアカウント連携に利用可能)。

function fetchProfile_(accessToken) {
  const res = UrlFetchApp.fetch('https://api.line.me/v2/profile', {
    headers: { Authorization: 'Bearer ' + accessToken },
    muteHttpExceptions: true
  });
  const json = JSON.parse(res.getContentText());
  if (json.error) throw new Error(json.error_description || 'profile error');
  return json; // { userId, displayName, ... }
}

scope=profile openid を要求しておけば、ログイン同意後のアクセスで一意なuserIdを取得できる。

動作確認

  • Dify側:コードノードの出力URLが正しく生成され、stateに想定のIDが入っているか
  • LINE側:コールバックURLの完全一致とclient_idの値が正しいか
  • GAS側:実行ログでcode/state受領→トークン交換→プロフィール取得の一連が成功しているか
    ※GASの実行ログにstateuserIdの記録が出ているかチェック
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?