Index
初めに
ソーシャルログインが最低限動く環境を作ってみました。
シンプルな造りなので概要は理解しやすいかと思います。
でも、セキュリティを担保できるほどの試験はしてなし、
実際に商用でのサービス提供もしていないので、これをそのまま商用で使うのはちょっと心許ないです。
また、商用でアプリを公開するためにはfacebookにアプリをレビューしてもらう必要があるみたいです。
ちなみにgoogleアカウントを使ったソーシャルログイン環境の構築手順もまとめてますので、
興味のある方はそちらもどうぞ。
必要条件
- Facebook for Developersを利用できるアカウント(事前に作成済である前提としています。)
- phpが動作するウェブサーバ環境(本記事ではXAMPPでlocalhost環境を使ってますが、LAMPでも動きます。)
※XAMPP環境の構築をする場合はこちらを参考にしてください。
構築手順
基本的にfacebook for Developersに書かれている通りに順番に構築していけばいいのですが、個人的には分かりにくかったので詳細をこちらに書いておきます。
facebook側の設定
-
facebook for Developersにアクセスして、マイアプリを選択します。
-
アプリの作成時にパスワードを聞かれるので、それを入力すればアプリが作成できます。
-
ウェブサイトのURLを設定します。ここではlocalhost環境を用いていますので、サイトURLはhttps://localhost/social-login/facebook/と、しています。
-
この後に2~5と説明が続きます。本来ならこれらの情報を元にアプリ側の実装をするのですが、今回は既に作ってありますので、適当へ「次へ」をクリックしていき、設定を終えます。
-
ちなみに、再度これらの設定を変更する場合は左側のサイドバーから「Facebookログイン>クイックスタート」を選択すればよいです。
以上でfacebook for Developersでの設定は終わりです。
連携アプリ側の設定
- Apacheの制御下にあるディレレクトリ(phpが動作する環境)へ、以下の様にファイル配置していきます。
C:\xampp\htdocs (←Apacheの制御下のフォルダ、環境の設定によって変わる)
└ social-login
└ facebook
├ index.php
├ common.php
├ verify.php
├ myPage.php
└ logout.php
- index.phpはログインのためのボタンを表示してるだけのシンプルな画面のphpソースコードです。でも内部的にはfacebookから書くように指示されたjavascriptを色々と書いてます。それと多少の独自処理を追加してます。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>HTML Sample</title>
</head>
<body>
<?php
require_once __DIR__ . "/common.php";
session_start();
if (array_key_exists("userId", $_SESSION) && $_SESSION["userId"] !== null) {
// ログイン済ならマイページへリダイレクト
header("Location: ./myPage.php", true, 307);
exit;
}
?>
<script>
window.fbAsyncInit = function() {
FB.init({
appId: '<?php echo $appId; ?>',
cookie: true,
xfbml: true,
version: '<?php echo $apiVersion; ?>'
});
FB.AppEvents.logPageView();
};
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement(s);
js.id = id;
js.src = "https://connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
function onLogin() {
console.log("OnLogin");
FB.getLoginStatus(function(response) {
if ("authResponse" in response && response.authResponse != null) {
console.log(response);
const param = {
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8"
},
body: JSON.stringify(response)
};
fetch("./verify.php", param)
.then((res) => {
return (res.json());
})
.then((json) => {
console.log(json)
if (!json.result) {
alert('Login failure');
return;
}
// ログイン成功ならマイページへリダイレクト
location.href = './myPage.php';
});
}
});
};
</script>
<!-- ログインボタンについてはこちらも参照→ https://developers.facebook.com/docs/facebook-login/web/login-button -->
<div id="fb-root"></div>
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v11.0&appId=<?php echo $appId; ?>&autoLogAppEvents=1" nonce="ITEWcCSh">
</script>
<div class="fb-login-button" data-width="" data-size="large" data-button-type="continue_with" data-layout="default" data-auto-logout-link="false" data-use-continue-as="true" onlogin="onLogin();"></div>
</body>
</html>
- common.phpはこのデモアプリ全体で使用するパラメータの設定を定義しています。facebook for Developersで表示される「アプリID」、「app secret」、「APIバージョン」のパラメータを書いています。
<?php
//ここにアプリID,アプリシークレット,APIバージョンを設定してください。
$appId = "XXXXXXXXXXXXXXXX";
$appSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
$apiVersion = "v11.0";
- verify.phpは受け取ったやIDトークン(ユーザ情報含む)をの正当性を検証しています。
<?php
require_once __DIR__ . "/common.php";
function webSafeBase64encode($JsonObj){
return rtrim(strtr(base64_encode($JsonObj), '+/', '-_'), '=');
}
function webSafeBase64decode($JsonObj){
return base64_decode(strtr($JsonObj, '-_', '+/') . str_repeat('=', 3 - (3 + strlen($JsonObj)) % 4));
}
function returnError($msg){
echo '{"result":false, "message":"' . $msg . '"}';
exit;
}
session_start();
//JSONデータを受け取る
$request_body = file_get_contents('php://input');
if ($request_body === null || $request_body === ""){
// ログイン画面へ遷移する。
header("Location: ./index.php", true, 307);
return;
};
$JsonObj = (array)json_decode($request_body);
if (!array_key_exists("status", $JsonObj) || $JsonObj["status"] !== "connected"){
returnError("Not connected");
}
if (!array_key_exists("authResponse", $JsonObj)){
returnError("No authResponse");
}
$authResponse = (array)$JsonObj["authResponse"];
//facebookから発行されていることの確認
if ($authResponse["graphDomain"] !== "facebook"){
returnError("Invalid Issure");
}
/*
//今回は試用しないパラメータ
$authResponse["accessToken"];
$authResponse["data_access_expiration_time"];
$authResponse["expiresIn"];
$authResponse["userID"];
*/
//id token(JWT [ヘッダー.ペイロード])をピリオドで分離
$jwt = explode('.', $authResponse["signedRequest"]);
if (count($jwt) !== 2){
//ヘッダーとペイロードの2つを含んでいる場合のみ対応する。
returnError("Unsupported format");
};
//ペイロード部分をデコード( https://developers.facebook.com/docs/reference/login/signed-request/ )
$payload = (array)json_decode(webSafeBase64decode($jwt[1]));
$_SESSION["userId"] = $payload["user_id"];
//OAuthコード
$_SESSION["OAuthCode"] = $payload["code"];
$alg = "";
switch ($payload["algorithm"]) {
case "HMAC-SHA256":
$alg = "sha256";
break;
case "HMAC-SHA384":
$alg = "sha384";
break;
case "HMAC-SHA512":
$alg = "sha512";
break;
default:
returnError("Unsupported hash algorithm");
}
// 発行日時をチェックする。一応、5分以上の未来に発行されている場合は受け付けないことにした。
$now = time();
if ($now + 5 * 60 < (int) $payload["issued_at"]) {
echo date('Y/m/d H:i:s', $payload["issued_at"]);
returnError("Issued at the wrong time");
}
//署名を検証
if (webSafeBase64decode($jwt[0]) !== hash_hmac($alg ,$jwt[1] , $appSecret, $raw = true) ){
returnError("Invalid signature");
}
//認証OK
echo '{"result":true}';
return;
- myPage.phpはこのデモアプリのログイン後を想定した画面です。非ログイン状態だと表示できません。画面上のボタンをクリックすると幾つかのAPIを実行できるように実装しました。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>HTML Sample</title>
</head>
<body>
<?php
require_once __DIR__ . "/common.php";
session_start();
// ログイン後の画面を想定
if (!array_key_exists("userId", $_SESSION)) {
//未ログイン状態ならログイン画面へ遷移する。
header("Location: ./index.php", true, 307);
return;
}
echo "■UserId : " . $_SESSION["userId"] . "<br />";
echo "■OAuthCode : " . $_SESSION["OAuthCode"] . "<br />";
?>
<hr>
<h2>My Page</h2>
<!-- APIを実行するボタン -->
<input type="button" value="Me" onclick="getMe();" /><br />
<input type="button" value="Permissions" onclick="getPermissions();" /><br />
<input type="button" value="Fields" onclick="getFields();" /><br />
<input type="button" value="Logout" onClick="location.href='./logout.php'" /><br />
<script>
window.fbAsyncInit = function() {
FB.init({
appId: '<?php echo $appId; ?>',
cookie: true,
xfbml: true,
version: '<?php echo $apiVersion; ?>'
});
FB.AppEvents.logPageView();
FB.getLoginStatus();
};
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement(s);
js.id = id;
js.src = "https://connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
function getMe() {
FB.api('/me', function(response) {
alert(JSON.stringify(response));
});
}
function getPermissions() {
FB.api('/me/permissions', function(response) {
alert(JSON.stringify(response));
});
}
function getFields() {
FB.api('/me', {
fields: 'id,first_name,last_name,middle_name,name,name_format,picture,short_name,email'
}, function(response) {
alert(JSON.stringify(response));
});
}
</script>
</body>
</html>
- logout.phpはセッション情報を削除してログアウトする処理を実装したものです。
<?php
session_start();
session_destroy();
$_SESSION = array();
//ログイン画面へ遷移
header("Location: ./index.php", true, 307);
以上でアプリ側の設定は終わりです。
動かしてみる
-
XAMPPを起動する(既に起動している場合は不要)
-
https://localhost/social-login/google/index.phpにウェブブラウザでアクセスする。
-
認証が成功するとmyPageが表示されます。[Me],[Permissions],[Fields]ボタンをクリックするとfacebookのAPIが実行され、ユーザ情報が取得できます。