SNSを使用したログイン – Instagram 実装編

投稿者: | 2018年6月27日

各種情報

前提条件

  • 言語はPHPで、バージョンは 5.6 を想定
  • フレームワークに CodeIgniter 3 を使用
  • WebサイトのURLは、https://example.com と想定して説明します

参考URL

多用している CodeIgniter のコマンド

redirect(リダイレクト先, リダイレクトメソッド, HTTPレスポンスコード)

リダイレクト先へリダイレクトします。リダイレクト先に ‘login’ と指定していれば、Loginコントローラーのindexメソッドへリダイレクトします(http://example.com/login/index)。素のPHPであれば、header(‘Location: http://example.com/login/index’); のようになります。

$this->input->get_post('・・・')
$this->input->post('・・・')

$_GET$_POSTから値を取り出します。その値(項目)がないときは、null を返してくれます。get_post() は先に $_GET を調べて、値がなければ $_POST を調べてくれます。post()$_POSTだけを調べます。

$this->load->library('・・・')
$this->load->view('・・・')

ライブラリやビューとして作成した PHP ファイルをインクルードします。require_once '・・・' と同じ意味です。

random_string('md5')

ランダムな文字列を作成する関数です。'md5' を指定しているのはちょうど 32 文字の文字列を返してくれるからで、他意はありません。推測されにくいランダムな英数字の文字列であることが重要です。

Instagram用ライブラリの作成(Instagram_library.php)

Instagram用のライブラリを作成しておきます。CodeIgniterでは以下のようにクラスとして作成します。
ログイン処理には Client ID や Client Secret が必要となりますので、これらの値を変数(下記コード例ではprivateメンバー)に入れておきます。

Instagram_library.php
<?php
defined(‘BASEPATH’) or exit(‘No direct script access allowed’);

/**
 * Instagramログインライブラリー
 */
class Instagram_library
{
    /**
     * フィールド
     */
    private $ci;

    // アプリの情報
    private $clientId = '000000000000000000000000';
    private $clientSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

    // リダイレクトURL
    private $redirectUrl = 'https://example.com/login/instagram';

    // エンドポイント
    private $apiAuthorizeUrl = 'https://api.instagram.com/oauth/authorize/?';
    private $apiRequestAccessToken = 'https://api.instagram.com/oauth/access_token';

    /**
     * コンストラクタ
     */
    public function __construct()
    {
        $this->ci = &get_instance();

        // curlライブラリのロード(require_once 'curl_library.php' と同義)
        $this->ci->load->library('curl_library');
    }
}

エンドポイントは公式ドキュメントを参照して、正しく入力してください。

ログインコントローラーの準備

コンストラクターに、Instagram_library ライブラリをロードするコマンドを追加しておきます。
Login.php

public function __construct()
{
    parent::__construct();
    $this->load->library('line_library');
    $this->load->library('facebook_library');
    
    // 以下を追加
    $this->load->library('instagram_library');
}

ログインページの「Instagramでログイン」ボタンをクリックしたとき、ログイン処理が開始されるよう、index メソッドを修正します。
Login.php

public function index()
{
    // 押されたボタンによってログインの処理を分岐
    switch ($this->input->post('submit')) {
        case 'line':
            redirect('login/login_line', 'location', 302);
            break;

        case 'yahoo':
            break;

        case 'twitter':
            break;

        case 'instagram':
        	// 以下を追加(login_instagramメソッドへリダイレクト)
            redirect('login/login_instagram', 'location', 302);
            break;

        case 'facebook':
            redirect('login/login_facebook', 'location', 302);
            break;

        case 'google':
            break;
    }

認証コードのリクエスト(login_instagram メソッド)

このメソッドが Instagram でのログイン開始になります。
ログイン処理は、認証コードをリクエストすることから始まります。認証コードのリクエストは Instagram のログインページへ、適切なパラメーターとともにリダイレクトすることです。
Instagram のログインページでユーザーがログインを行います。ユーザーがログインすると、認証できたことを示すリクエストトークンが戻ってきます。

login_instagram メソッドではパラメーターを作成し、Instagram のログインページへリダイレクトするところまでを処理します。
Login.php

/**
 * Instagramによるログインを開始する
 */
public function login_instagram()
{
    $this->instagram_library->requestAuthorize();
}

処理を instagram_library ライブラリの requestAuthorize メソッドに丸投げしています。このメソッドを次に作成します。

認証コードエンドポイントへリダイレクトする(requestAuthorize メソッド)

instagram_library ライブラリに requestAuthorize メソッドを追加します。
Instagram_library.php

/**
 * 認証コードのリクエストを行う
 */
public function requestAuthorize()
{
    $this->ci->load->helper('string');

    // stateを作成(ランダムな文字列)
    $state = random_string('md5');

    // stateをセッションに保管
    $_SESSION['auth'] = $state;

    // エンドポイントへ送信するパラメーター
    $params = [
        'client_id' => $this->clientId,
        'redirect_uri' => $this->redirectUrl,
        'response_type' => 'code',
        'scope' => 'basic',
        'state' => $state,
    ];

    // エンドポイントのURLにパラメーターを付けて、リダイレクト
    $param = http_build_query($params, '', '&');
    $url = $this->apiAuthorizeUrl . $param;

    header('Location: ' . $url);
    exit;
}

他のSNS同様、CSRF対策として state を作成します。これは推測されにくいランダムな文字列です。適度な長さのもの(32バイト以上推奨)を作成します。作成した state はセッションに入れておきます。

次に、エンドポイントへ送信するパラメーターを作成します。パラメーターには以下のものを指定します。

パラメーター内容
client_idアプリの Client ID。
redirect_uri登録済みのリダイレクトURL。登録したとおりのものを指定します。
response_type'code'
scopeアプリが必要とする権限。
'basic'、'public_content' があります。両方のスコープを指定する場合、間を空白で区切ります。空白は '+' にエンコードする必要があります。
stateランダムな文字列。CSRF対策に作成した文字列です。

作成したパラメーターはURLエンコードする必要があります。このとき、半角スペースは ‘+'(プラス)にエンコードしなければなりません。以下のように http_build_query 関数を使用すると簡単です。

$param = http_build_query($params, '', '&');

エンコード済みのパラメーターを、認証エンドポイントの URL に付けて、その URL へリダイレクトします。

コールバック処理(instagram メソッド)

ユーザーが Instagram にログインすると、リダイレクト URL に処理が戻ってきます。このとき URL に認証コードが付加されてきます。この認証コードを使って、アクセストークンをリクエストすることになります。
このコード例では、リダイレクトURLが https://example.com/login/instagram となっています。これは Login コントローラーの instagram メソッドを表していますので、Instagram メソッドを作成します。

Login.php

/**
 * Instagramのログイン処理を行う
 */
public function instagram()
{
    // パラメーターにerrorがあるか
    // if (isset($_GET['error'])) { $error = $_GET['error']; } と同じ意味になる
    $error = $this->input->get_post('error');
    
    if (!empty($error)) {
        $_SESSION['message'] = '認証エラーです。エラー内容:' . $error;
        redirect('login', 'location', 302);
    }

    // パラメーターから認証コードとstateを取得
    $requestToken = $this->input->get_post('code');
    $state = $this->input->get_post('state');

    // セッションからstateを読み取り、セッションのデータを削除する
    $savedState = $_SESSION['auth'];
    unset($_SESSION['auth']);

    // stateの確認
    if ($savedState !== $state) {
        $_SESSION['message'] = '不正なレスポンスです。';
        redirect('login', 'location', 302);
    }

    // アクセストークンのリクエスト
    $token = $this->instagram_library->requestAccessToken($requestToken);
    if ($token === false) {
        $_SESSION['message'] = 'アクセストークンを取得できませんでした。';
        redirect('login', 'location', 302);
    }

    // ログイン完了、結果を表示する
    $viewdata = [
        'message' => 'Instagramでログインしました。',
        'data' => $token,
    ];

    $this->load->view('logined', $viewdata);
}

ユーザーがInstagramへのログインをキャンセルしたり何らかのエラーが発生したりすると、リダイレクト URL に error パラメーターが付加されます。まずは error パラメーターが存在するか確認します。

エラーではなかった場合、パラメーターから認証コード(code)と state を取得します。
state は最初に送信した state の文字列がそのまま返ってきています。セッションに保管していた state と、パラメーターで返ってきた state が一致していることを確認します。もし一致しない場合、CSRFの可能性を考慮して、ログイン処理を中止します。

認証コードを使用して、アクセストークンをリクエストします。requestAccessToken メソッドがリクエストを実行するメソッドです。これは後で作成します。
アクセストークンの取得に成功したら、ログイン完了です。

アクセストークンをリクエストする(requestAccessTokenメソッド)

アクセストークンのリクエストを実際に行うのは、instagram_library ライブラリの requestAccessToken メソッドです。requestAccessToken メソッドを作成します。
Instagram_library.php

/**
 * アクセストークンをリクエストする
 * @param   string  $requestToken   認証コード
 * @return  array   アクセストークンとユーザー属性の配列
 */
public function requestAccessToken($requestToken)
{
    // 送信するパラメーター
    $params = [
        'client_id' => $this->clientId,
        'client_secret' => $this->clientSecret,
        'grant_type' => 'authorization_code',
        'redirect_uri' => $this->redirectUrl,
        'code' => $requestToken,
    ];

    // curlでPOSTする
    $response = $this->ci->curl_library->execPostCurl($this->apiRequestAccessToken, $params);
    if (empty($response['response'])) {
        return false;
    } else {
        return $response['response'];
    }
}

アクセストークンのリクエストは、access_token エンドポイントへ行います。
access_token エンドポイントへ送信するパラメーターを作成します。パラメーターは以下の値を指定します。

パラメーター内容
client_idアプリの Client ID。
client_secretアプリの Client Secret。
grant_type'authorization_code' を指定。
redirect_uri登録済みのリダイレクトURL。認証のために必要です。リダイレクトはされません。
code認証コード。

リクエストは cURL を POST で実行します。
レスポンスにはアクセストークンと、ユーザー情報が含まれます。取得できるユーザー情報は、アプリが求めた権限によります。

以上でInstagramでのログインは完成です。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)