本記事は、めんどい太郎の Advent Calendar 2023 10日目の記事です。
はじめに
この記事は初心者が書いています。
皆さんはLINE使ってますか?
LINEは日本では主要な連絡ツールで多くの人が使っています。
そこで、WebでLINEログインを実装する方法を紹介します。
この記事で紹介するコードは不完全であり、テストのためにセキュリティ面を考慮してない実装があります。
LINEのドキュメントは非常に見やすいので、この記事を見るよりもいいかもしれません。
チャネルの作成
まずはLINE Developper Consoleにアクセスします。
アクセスするとプロバイダーという項目があるので隣りにある作成ボタンを押します。
するとプロバイダー名の入力を求められるので入力します。
プロバイダー名にはLINEなどの単語は使えません。
プロバイダーを作成し、プロバイダーの編集画面に行くと「チャネル設定」という項目があるので開きます。
新規チャネル作成を押します。
LINEログインを選択します。
国やチャネル名などを入力します。
アプリタイプでウェブアプリを選択します。
2要素認証の必須化は有効にすることを推奨します。(デフォルトで有効です。)
規約に同意しチャネル作成は完了です。
ここで、「チャネルID」をメモしてください。
また、「チャネルシークレット」を再発行してからメモしてください。
これらはあとで使用します。
「LINEログイン設定」の項目に移動し、「コールバックURL」を設定できる人は事前にしておいてください。
今回はhttps://example.com/login/callback/
を使用します。(example.comはあくまで例です。)
ページの用意
ログインページを用意します。
以下は例です。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>ログイン</title>
</head>
<body>
<h1>ログイン</h1><br>
<a href="login_page_url"><img src="../image/btn_login_hover.png"></img></a><br>
</body>
</html>
LINEのログインボタンはドキュメントに従ってください。
今回はリンク付きの画像を載せています。
ログインURLの用意
ログインURLを用意します。
以下は例です。
<?php
function rand_str_gene($length) {
return substr(str_shuffle("ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz0123456789"), 0, $length);
}
$client_id = '';//メモしたチャネルID
$redirect_uri = 'https://example.com/login/callback/';
$redirect_uri = urlencode($redirect_uri);
$state = rand_str_gene(32);
$scope = 'profile%20openid';
$login_url = 'https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=' . $client_id . '&redirect_uri=' . $redirect_uri . '&state=' . $state . '&scope=' . $scope;
file_put_contents("csrf_token.txt", $state);
?>
https://access.line.me/oauth2/v2.1/authorize?response_type=code
までは同じです。ここにclient_id
、redirect_uri
、state
、scope
を指定します。
client_id
は先程メモしたチャネルIDのことです。
redirect_uri
は先程指定したものです。
state
は「クロスサイトリクエストフォージェリ防止用の固有な英数字の文字列」とのことです。
いわゆるCSRFトークンというやつですね。
もちろんページにアクセスされるたびにランダムに生成します。
今回はテストのためにテキストファイルにそのまま保存していますが、こんなことしないでください。
scope
ではユーザーから取得する情報を指定します。
スコープにメールアドレスを指定する際は申請をする必要があります。
今回はテストなのでプロフィールとIDのみ取得しています。
スコープに関してはドキュメントを見たほうがいいです。
このURLをボタンを押したときに開くようにします。
こんな感じでログイン画面が表示されます。
LINEでログインされるとredirect_uri
で指定したページにリダイレクトされます。
リダイレクト先
リダイレクト先にはGETリクエストが送信されます。
リクエストが送られてきたときにまず確認することはエラーの確認です。
GETリクエストにerror
が含まれていないかをチェックします。
if(!empty($_GET['error'])) {
$error_code = $_GET['error'];
$error_contents = $_GET['error_description'];
echo $error_code . ' : ' . $error_contents . '<br>';
}
含まれていない場合はトークンの取得を行います。
<?php
$client_id = '';//メモしたチャネルID
$redirect_uri = 'https://example.com/login/callback/';
$scope = 'profile%20openid';
$channel_secret = '';//メモしたチャネルシークレット
if(!empty($_GET['error'])) {
$error_code = $_GET['error'];
$error_contents = $_GET['error_description'];
} else {
$get_code = $_GET['code'];
$get_state = $_GET['state'];
// CSRFトークンがあっているかどうか確認
$contents = file("../csrf_token.txt");
$before_login_gene_token = $contents[0];
if($before_login_gene_token != $get_state){
echo 'Error: CSRF_Token';
exit();
}
// トークン入手
$CURLERR = NULL;
$data = array(
'grant_type' => 'authorization_code',
'code' => $get_code,
'redirect_uri' => $redirect_uri,
'client_id' => $client_id,
'client_secret' => $channel_secret
);
$headers = array(
'Content-Type' => 'application/x-www-form-urlencoded',
);
$url = 'https://api.line.me/oauth2/v2.1/token';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, TRUE); //POSTで送信
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); //データをセット
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //受け取ったデータを変数に
$token_response = curl_exec($ch);
if(curl_errno($ch)){ //curlでエラー発生
$CURLERR .= 'curl_errno:' . curl_errno($ch) . "\n";
$CURLERR .= 'curl_error:' . curl_error($ch) . "\n";
$CURLERR .= '▼curl_getinfo' . "\n";
foreach(curl_getinfo($ch) as $key => $val){
$CURLERR .= '■' . $key . ':' . $val . "\n";
}
echo nl2br($CURLERR);
}
curl_close($ch);
$json_token_response = json_decode($token_response, true);
//Error Check
if(!empty($json_token_response['error'])){
echo 'Error: <br>';
echo $json_token_response['error'] . '<br>';
echo $json_token_response['error_description'] . '<br>';
exit();
}
}
?>
トークンの取得はPOSTリクエストで行います。
URLはhttps://api.line.me/oauth2/v2.1/token
です。
ヘッダーにContent-Type
をapplication/x-www-form-urlencoded
に指定します。
POSTで送信するデータはgrant_type
、code
、redirect_uri
、client_id
、client_secret
です。
grant_type
はauthorization_code
で固定です。
code
はGETリクエストで受け取った値です。
rediret_uri
、client_id
はログインURLと同じものを使用します。
client_secret
はメモしたチャネルシークレットです。
これでPOSTリクエストを送信するとJSONが返ってきます。
このJSONにはトークンなどが記載されています。
軽く紹介しますが詳しくはドキュメントを読むことをおすすめします。
今回はテストなのでid_token
だけ使用します。
プロフィール取得
IDトークンを使用してプロフィールを取得します。
// 省略
// レスポンスからIDトークンを変数に保存
$id_token = $json_token_response['id_token'];
// GET_Profile(POST)
$CURLERR = NULL;
$data = array(
'id_token' => $id_token,
'client_id' => $client_id
);
$url = 'https://api.line.me/oauth2/v2.1/verify';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, TRUE); //POSTで送信
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); //データをセット
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //受け取ったデータを変数に
$profile_response = curl_exec($ch);
if(curl_errno($ch)){ //curlでエラー発生
$CURLERR .= 'curl_errno:' . curl_errno($ch) . "\n";
$CURLERR .= 'curl_error:' . curl_error($ch) . "\n";
$CURLERR .= '▼curl_getinfo' . "\n";
foreach(curl_getinfo($ch) as $key => $val){
$CURLERR .= '■' . $key . ':' . $val . "\n";
}
echo nl2br($CURLERR);
}
curl_close($ch);
$json_profile_response = json_decode($profile_response, true);
//Error Check
if(!empty($json_profile_response['error'])){
echo 'Error: <br>';
echo $json_profile_response['error'] . '<br>';
echo $json_profile_response['error_description'] . '<br>';
exit();
}
}
https://api.line.me/oauth2/v2.1/verify
にPOSTを送ります。
id_token
に先程入手したIDトークンをclient_id
にメモしたチャネルIDをセットして送信します。
そうすることでプロフィールが取得できます。
以下はドキュメントに記載されていたものを今回実際に返ってきたデータのみに削ったものです。
{
"iss": "https://access.line.me",
"sub": "U1234567890abcdef1234567890abcdef",
"aud": "1234567890",
"exp": 1504169092,
"iat": 1504263657,
"amr": ["pwd"],
"name": "Taro Line",
"picture": "https://sample_line.me/aBcdefg123456"
}
このように返ってきます。
使用しそうな項目のみ紹介しておきます。
要素名 | 概要 |
---|---|
sub | ユーザーID |
name | ユーザー表示名 |
picture | ユーザーアイコンURL |
あとはDBに登録するなり参照するなりするかと思います。
終わりに
画像やプログラムに関する説明が少なく、少々わかりにくい記事になってしまったような気がします...
LINEログインはGoogleでログインとは違いボタンが画像なのが扱いづらいかもしれません。
また、LINEログインはトークンを取得してそのトークンからプロフィールを取得して...というように少々手間がかかります。
しかもPOSTです。
Googleでログインはライブラリがあるので一行ぐらいで済みます。ライブラリの偉大さを感じますね...
ですがこれでまた一つSNSログインを増やすことができました!
自身のサービスにSNSログインを実装してユーザーが簡単にユーザー登録できるサイトへ!