0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

めんどい太郎のAdvent Calendar 2023

Day 10

PHPでLINEログインを実装する方法

Last updated at Posted at 2023-12-09

本記事は、めんどい太郎の Advent Calendar 2023 10日目の記事です。

はじめに

この記事は初心者が書いています。

皆さんはLINE使ってますか?

LINEは日本では主要な連絡ツールで多くの人が使っています。

そこで、WebでLINEログインを実装する方法を紹介します。

この記事で紹介するコードは不完全であり、テストのためにセキュリティ面を考慮してない実装があります。

LINEのドキュメントは非常に見やすいので、この記事を見るよりもいいかもしれません。

チャネルの作成

まずはLINE Developper Consoleにアクセスします。

アクセスするとプロバイダーという項目があるので隣りにある作成ボタンを押します。

するとプロバイダー名の入力を求められるので入力します。

image.png

プロバイダー名にはLINEなどの単語は使えません。

プロバイダーを作成し、プロバイダーの編集画面に行くと「チャネル設定」という項目があるので開きます。

新規チャネル作成を押します。

image.png

LINEログインを選択します。

国やチャネル名などを入力します。

アプリタイプでウェブアプリを選択します。

2要素認証の必須化は有効にすることを推奨します。(デフォルトで有効です。)

規約に同意しチャネル作成は完了です。

ここで、「チャネルID」をメモしてください。

また、「チャネルシークレット」を再発行してからメモしてください。

これらはあとで使用します。

「LINEログイン設定」の項目に移動し、「コールバックURL」を設定できる人は事前にしておいてください。

今回はhttps://example.com/login/callback/を使用します。(example.comはあくまで例です。)

image.png

ページの用意

ログインページを用意します。

以下は例です。

<!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のログインボタンはドキュメントに従ってください。

今回はリンク付きの画像を載せています。

image.png

ログイン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_idredirect_uristatescopeを指定します。

client_idは先程メモしたチャネルIDのことです。

redirect_uriは先程指定したものです。

stateは「クロスサイトリクエストフォージェリ防止用の固有な英数字の文字列」とのことです。

いわゆるCSRFトークンというやつですね。

もちろんページにアクセスされるたびにランダムに生成します。

今回はテストのためにテキストファイルにそのまま保存していますが、こんなことしないでください。

scopeではユーザーから取得する情報を指定します。

スコープにメールアドレスを指定する際は申請をする必要があります。

今回はテストなのでプロフィールとIDのみ取得しています。

スコープに関してはドキュメントを見たほうがいいです。

このURLをボタンを押したときに開くようにします。

こんな感じでログイン画面が表示されます。

image.png

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-Typeapplication/x-www-form-urlencodedに指定します。

POSTで送信するデータはgrant_typecoderedirect_uriclient_idclient_secretです。

grant_typeauthorization_codeで固定です。

codeはGETリクエストで受け取った値です。

rediret_uriclient_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ログインを実装してユーザーが簡単にユーザー登録できるサイトへ!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?