各種情報
前提条件
- 言語はPHPで、バージョンは 5.6 を想定
- フレームワークに CodeIgniter 3 を使用
- WebサイトのURLは、https://example.com と想定して説明します
参考URL
- Instagram developer https://www.instagram.com/developer/
- CodeIgniterユーザガイド http://codeigniter.jp/user_guide/3/index.html
多用している 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でのログインは完成です。