📌

ローカルサーバーを使ってLINEログインの基礎を理解する

2025/02/26に公開

対象読者

  • LINEログインの仕組みをなんとなくしか知らない人

LINEログインを使ってユーザープロフィール情報を取得する

全体像

用語

  • 自己署名証明書:自分でウェブサイトの通信が安全であることを証明するために使われるもの。本番環境では認証機関から発行された証明書を使ってそのウェブサイトが本物で信頼できることを証明するが、開発ではそこまでする必要がないため、自己署名証明書を使う。
  • server.crt:証明書ファイル。この証明書には、サーバーの情報や公開鍵(暗号化に使われる鍵)が含まれていて、サーバーが本物であることを証明するために使う。
  • server.key:秘密鍵。絶対に誰とも共有してはならないもので、もし秘密鍵が漏れてしまうと、サーバーの通信が安全ではなくなり、悪意のある第三者がその通信を盗み見ることができるようになってしまう。
  • OpenSSL:インターネット上で安全に通信を行うための暗号化ライブラリのこと。例えば、HTTPSの通信や、データの暗号化、デジタル証明書の発行などに使われる。
  • ngrok:ローカルで動作しているサーバーをインターネットに一時的に公開するツール。HTTPやHTTPS通信など、インターネットを介してAPIにリクエストするテストをしたい時に使う。
  • LINE ログイン:ユーザーが自身のLINEアカウントを使用して、ウェブサイトやアプリにログインや会員登録を行うためのソーシャルログインサービス。ユーザーがLINEログインを通じて認証・認可を行うと、アプリケーションはアクセストークンやIDトークンを取得できる。これらのトークンを使用して、ユーザーのプロフィール情報(ユーザーID、表示名、プロフィール画像のURL、メールアドレスなど)を取得し、アプリ内で活用することが可能。

※LINE関係の用語は以下の図を参照。

手順

1. ローカルサーバーでHTTPSを有効にする

ローカル開発環境でHTTPSを利用するために、以下の手順を実行します。

自己署名証明書の作成:

  • OpenSSLを使用して自己署名証明書を生成します。
  • 以下のコマンドで証明書と秘密鍵を作成します。
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt

コマンドを実行すると、以下のような情報の入力を求められるので、以下を参考に入力します。

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:Taito
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Your Company
Organizational Unit Name (eg, section) []:Development
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:your@email.com

これらの情報を入力すると、プロジェクトのルートディレクトリに以下の2つのファイルが生成されます:

  • server.crt(証明書)
  • server.key(秘密鍵)

2. ローカルサーバーで動かすログイン画面とログイン処理の実装

2-1. ログイン画面を以下の手順で作成

ログイン画面を以下の手順で作成します。

  • ルートディレクトリにpublicディレクトリを作成します。
  • publicディレクトリに移動し、以下のindex.htmlを作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>LINE Login</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      margin-top: 50px;
    }
    .login-button {
      padding: 10px 20px;
      background-color: #00c300;
      color: white;
      font-size: 16px;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
    .login-button:hover {
      background-color: #009900;
    }
  </style>
</head>
<body>
  <h1>LINEログイン</h1>
  <p>LINEアカウントでログインしてください。</p>
  <!-- LINEログインボタン -->
  <button class="login-button" onclick="window.location.href='/login'">LINEでログイン</button>
</body>
</html>

2-2 LINEログイン時のサーバーサイドスクリプト作成

以下コマンドで新しい package.json を作成します。

npm init -y

必要な依存パッケージをインストールします。

npm install express dotenv axios

package.json の scripts セクションに起動コマンドを追加します。

{
  "scripts": {
    "start": "node index.js"
  }
}
  • ルートディレクトリにindex.jsを作成します。
  • 今回はNode.jsのexpressを使用して以下のように設定します。
require('dotenv').config();
const express = require('express');
const https = require('https');
const fs = require('fs');
const axios = require('axios');

const app = express();
const PORT = 3000;

// Content-Type が application/json のリクエストボディを自動的に解析し、req.body にJavaScriptオブジェクトとして格納するための設定
app.use(express.json());

// Content-Type が application/x-www-form-urlencoded のリクエストボディを自動的に解析し、req.body にJavaScriptオブジェクトとして格納するための設定
// application/x-www-form-urlencoded:フォームのデータを送るときのフォーマットの一つで、Webフォームでよく使われる方式。今回はサーバーからLINEの認証サーバーとトークン取得のPOSTリクエストのやり取りをする時にapplication/x-www-form-urlencodedを使う
app.use(express.urlencoded({ extended: true }));

// 静的ファイルを提供するための設定
// public ディレクトリ内にindex.htmlがある場合、http://localhost:3000でアクセス可能
app.use(express.static('public'));

// LINEログイン用のエンドポイント
app.get('/login', (req, res) => {
  // CSRF(クロスサイトリクエストフォージェリ)対策のためのstateパラメータを生成
  // CSRF(クロスサイトリクエストフォージェリ)とは?:ログイン状態を維持したユーザーが罠サイトにアクセスすることで、攻撃者の仕込んだリクエストを実行させる攻撃手法
  // これが成功すると、ユーザーになりすまし、登録情報やパスワードが変更されたり、ユーザーの口座から不正に資金が引き出されるなどの被害が発生する
  // この攻撃を防ぐために、ログインリクエスト時にstateパラメータを生成し、コールバック時に照合することで、正規のリクエストであることを確認する
  const state = Math.random().toString(36).substring(7);
  console.log('Login endpoint accessed');
  const lineLoginUrl = `https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=${process.env.LINE_CHANNEL_ID}&redirect_uri=${encodeURIComponent(process.env.LINE_CALLBACK_URL)}&state=${state}&scope=profile%20openid`;
  console.log('Redirecting to:', lineLoginUrl);
  res.redirect(lineLoginUrl);
});

// コールバック処理用のエンドポイント
app.get('/callback', async (req, res) => {
  const { code, state } = req.query;
  console.log('Callback received:', { code, state });

  try {
    console.log('Attempting to get access token...');
    // アクセストークンを取得
    const tokenResponse = await axios.post('https://api.line.me/oauth2/v2.1/token', {
      grant_type: 'authorization_code',
      code: code,
      redirect_uri: process.env.LINE_CALLBACK_URL,
      client_id: process.env.LINE_CHANNEL_ID,
      client_secret: process.env.LINE_CHANNEL_SECRET
    }, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    });

    const accessToken = tokenResponse.data.access_token;
    console.log('Access token obtained successfully');

    // ユーザープロフィールを取得
    const profileResponse = await axios.get('https://api.line.me/v2/profile', {
      headers: {
        Authorization: `Bearer ${accessToken}`
      }
    });

    console.log('User profile received:', profileResponse.data);

    // 成功時のレスポンス
    res.json({
      success: true,
      profile: profileResponse.data
    });
  } catch (error) {
    console.error('Error details:', {
      response: error.response?.data,
      message: error.message,
      stack: error.stack
    });
    res.status(500).json({
      success: false,
      error: 'Authentication failed',
      details: error.response?.data || error.message
    });
  }
});

// HTTPSサーバーの設定
const options = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt')
};

// サーバーの起動(0.0.0.0を指定してngrockからのアクセスを許可)
// 0.0.0.0 を指定してサーバーを起動すると、サーバーはローカルホスト(127.0.0.1)だけでなく、ネットワーク上の他のデバイスからの接続も受け入れるようになる
// これによりサーバーはローカルネットワーク内の他のデバイスや、ngrockによるインターネット経由での接続を受け入れることができる
https.createServer(options, app).listen(PORT, '0.0.0.0', () => {
  console.log(`HTTPS Server is running on port ${PORT}`);
});

2-3. ngrokを使用してローカルサーバーを公開する

ngrokを使用すると、ローカルサーバーを一時的にインターネット上に公開し、HTTPSのコールバックURLを取得できます。

ngrokのインストールと設定:

  • 公式サイトからngrokをダウンロードし、インストールします。
  • 以下のコマンドでローカルサーバーを公開します。
ngrok http https://localhost:3000

※他のアプリケーションがポート3000を使用していると、「ERR_NGROK_3004 ngrok gateway error」が発生します。その場合は他のポートを設定するか、該当のアプリケーションが起動しているサーバーを「control + c」等で停止してください。

2-4 .envファイルの設定

  • ルートディレクトリで.envファイルを作成します。
  • 以下の通り、LINEログインの際に必要な設定を.envファイルに記載します。
LINE_CHANNEL_ID={LINEログインチャンネルID}
LINE_CHANNEL_SECRET={LINEログインチャンネルシークレット}
LINE_CALLBACK_URL={ngrokでインターネットに公開したローカルサーバーののURL。以下画像の赤線参照}/callback

3. LINE Developersコンソールの設定

LINE Developersコンソールで、コールバックURLとして{ngrokでインターネットに公開したローカルサーバーののURL。以上画像の赤線参照}/callbackを設定します。

4. ngrokで公開したローカルサーバー(LINEログインAPIサーバー)にアクセス

  • {ngrokでインターネットに公開したローカルサーバーののURL。2つ上の画像の赤線参照}をブラウザで開きます。
  • 「Visit Site」ボタンをクリックします。

「LINEでログイン」ボタンをクリックします。

  • 次のアカウントでログインします。
    {自分のアカウント名}
    「ログイン」ボタン

  • 上記が表示されるので、「ログイン」ボタンをクリックします。
    以下の形式のJSONデータが返ってくればOKです。

{
    "success": true,
    "profile": {
    "userId": "ユーザーID",
    "displayName": "表示名",
    "pictureUrl": "プロファイル写真"
    }
}

まとめ

LINEログインが成功すると、アクセストークンが発行され、これを使用してユーザーのプロフィール情報を取得できます。具体的には、https://api.line.me/v2/profile エンドポイントにリクエストを送ることで、ユーザーID、表示名、プロフィール画像URLなどの情報が得られます。

これらの機能を活用することで、ユーザーのLINEアカウント情報をアプリケーション内で利用し、よりそのユーザーにカスタマイズされたサービスを提供することが可能になります。

株式会社ソニックムーブ

Discussion