【Flutter】Firebaseを使用したLINEログイン認証実装方法

こんにちは、株式会社Pentagonでアプリ開発をしている山崎です!

この記事では、LINEログイン機能を使って、Firebaseのカスタムトークン認証をする方法を説明します。LINEログイン機能をアプリに実装したい人にとって役立つ内容になっています。
記事を簡潔にまとめるため、エラー処理やアーキテクチャについては考慮していませんので、ご了承ください。

【こんな人に読んで欲しい】

  • FlutterでLINEログイン機能を実装したい人
  • FlutterでFirebaseのカスタムトークン認証を実装したい人

【この記事を読むメリット】

  • LINEログインの流れが分かる
  • Firebaseのカスタムトークン認証のやり方が分かる

【結論】

アプリにLINEログイン機能を実装することにより、アプリから離脱する人が少なくなり、かつ、ユーザーにとって便利になるので、この機会にLINEログイン機能を取り入れてみてはいかがでしょうか!

動作イメージ

LINEログイン後、しばらくしてからログイン状態になり、ログアウトボタンが表示されます。

目次

LINE認証するまでにやるべきこと

1. LINE Developersに登録し、新規チャネルを作成し、LINEログイン設定をする

LINE Developersの画面を開いてください。
https://developers.line.biz/ja/
新規登録し、ログインしてください。
LINE Developersのコンソール画面で新規チャネル作成をしてください。
次に、その作成した新規チャネルでLINEログイン設定をしてください。
iOSの場合は、iOS bundle ID を設定してください。(今回はこのあとプロジェクトを作成するので作成した後に、設定してください。)
LINE Developersの設定は以上です。

2: Firebaseのプロジェクトの作成、Cloud Functionsの設定、Authenticationの設定をする

Firebaseの画面を開いてください。
https://firebase.google.com/?hl=ja

Firebaseコンソールへ移動してください。
Firebaseプロジェクトを作成してください。

アプリにFirebaseを追加してください。
下の画面の右側にあるFlutterのアイコンを選択して、流れに沿って進めていけば、アプリにFirebaseを追加することができます。

次に、コンソール画面の左側にある構築を選択し、Cloud Functionsを選択して流れに沿って設定してください。
Cloud Functionsを使用するには、無料のSparkプランから従量課金のBlazeプランに変更する必要があります。

最後に、コンソール画面の左上のある歯車を選択し、プロジェクトの設定→新しい秘密鍵の生成を選択し、Firebase Authenticationで必要なjsonファイルが作成されるので、これをFlutterプロジェクトのfunctionsフォルダに置いてください。


今回はjsonファイルの名前を「serviceAccountKey.json」に変更しました。
Firebaseコンソール画面での設定は以上です。

3. Flutter側にflutter_line_sdkを導入する

pub.devのサイトを参考に、ios/Runner/Info.plistの最後のタグの直前に追記してください。
https://pub.dev/packages/flutter_line_sdk

4. Flutter側のコードの実装をする

ここからコードを実装していきます。実装の流れとしては下記のようになります。

  1. アプリ側で、LINEにログインし、アクセストークンを取得する。
  2. アクセストークンをCloud Functionsに送る。
  3. Cloud Functionsでアクセストークンの有効性を検証する。
  4. Cloud Functionsでアクセストークンを使用して、LINEからユーザー情報を取得する。
  5. Cloud FunctionsでLINEから取得したユーザー情報を使用して、Firebase認証のカスタムトークンを作成し、アプリ側に送る。
  6. アプリ側で、カスタムトークンを使用して、Firebase認証する。

パッケージのインストール

最初に、pubspec.yamlに今回使用するパッケージを入力し、保存します。

dependencies:
  flutter:
    sdk: flutter
  flutter_line_sdk: 2.3.3
  flutter_riverpod: ^2.3.6
  firebase_core: ^2.15.0
  firebase_auth: ^4.7.1
  cloud_functions: ^4.3.4

ターミナルに「flutter pub get」を入力し、パッケージをインストールします。

flutter pub get

main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:hello/FirstPage.dart';
import 'package:flutter_line_sdk/flutter_line_sdk.dart';

import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Firebaseの初期化
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  //setupにLINE Developersで新規作成したチャネルIDを入力
  LineSDK.instance.setup("LINEの新規チャネルIDを入力").then((_) async {
    print("LineSDK 準備完了");
  });
  runApp(const MaterialApp(
    home: FirstPage(),
  ));
}

FirstPage.dart

import 'package:cloud_functions/cloud_functions.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_line_sdk/flutter_line_sdk.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class FirstPage extends ConsumerWidget {
  const FirstPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("サンプルアプリ画面"),
      ),
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          StreamBuilder(
              // Firebaseにユーザーがログインしているかを監視する
              stream: FirebaseAuth.instance.userChanges(),
              builder: (context, snapshot) {
                // データがnullならログアウト状態
                return snapshot.data == null
                    ? TextButton(
                        child: const Text(
                          "LINEログイン",
                          style: TextStyle(fontSize: 50),
                        ),
                        onPressed: () async {
                          try {
                            // LINEにログインしてアクセストークンを取得する
                            final lineData = await LineSDK.instance.login();
                            // アクセストークンをcloud functionsへ送信して、Firebase Authenticationのカスタムトークンを発行してもらう
                            final jsonData = await FirebaseFunctions.instance
                                .httpsCallable('register')
                                .call(
                              {
                                'token': lineData.accessToken.value,
                              },
                            );
                            // Firebase Authenticationのカスタムトークンを取得
                            final customToken = jsonData.data['customToken'];
                            // カスタムトークンを使用してAuthenticationに登録する
                            await FirebaseAuth.instance
                                .signInWithCustomToken(customToken);
                          } on PlatformException catch (e) {
                            print(e);
                          }
                        },
                      )
                    : TextButton(
                        child: const Text(
                          "ログアウト",
                          style: TextStyle(fontSize: 50),
                        ),
                        onPressed: () async {
                          // ログアウト処理
                          await LineSDK.instance.logout();
                          await FirebaseAuth.instance.signOut();
                        },
                      );
              }),
        ],
      )),
    );
  }
}

Flutter側は以上です。

5. Cloud Functions側のコードの実装をする

今回はjavascriptで記述しました。

functions/package.json

"dependencies": {
    "axios": "^1.4.0",
    "firebase-admin": "^11.8.0",
    "firebase-functions": "^4.3.1"
  },

必要なパッケージをインポートするために上記のパッケージを記述し、ターミナルを開き、functionsフォルダに移動し、下記のコマンドを入力してください。

npm install

index.js

const functions = require("firebase-functions");
const admin = require('firebase-admin');
const axios = require('axios');
// Firebaseコンソール画面からダウンロードしたjsonファイル
const serviceAccount = require("./serviceAccountKey.json");

// FirebaseのAdminSDKの初期化
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
});

exports.register = functions.https.onCall(async (data, context) => {
    try {
        // アクセストークンをアプリ側から取得
        const token = data.token;

        // アクセストークンの有効性を検証
        const apiUrl = 'https://api.line.me/oauth2/v2.1/verify';
        const response = await axios.get(apiUrl, {
            params: {
                access_token: token,
            },
        });

        // ユーザーのプロフィールを取得(userID,displayName,pictureUrl)
        const url = 'https://api.line.me/v2/profile';
        const profile = await axios.get(url, {
            headers: {
                Authorization: `Bearer ${token}`,
            },
        });

        const profileData = profile.data;
        // userIdを使用してカスタムトークンを作成する
        const customToken = await admin.auth().createCustomToken(profileData.userId);
        // カスタムトークンをアプリ側に返す
        return { result: 'success', customToken: customToken };
    } catch (error) {
        return { result: 'error' };
    }
});

6. 作成したCloud Fucntions側のコードをデプロイする

ターミナルを開き、functionsフォルダに移動して、「firebase deploy --only functions」を入力しデプロイします。

firebase deploy --only functions

以上です。これら全てが終わったら実機で確認してみましょう。
Firebase認証をするとAuthenticationにユーザーUIDが登録されます。

まとめ

LINEログイン機能を追加するまで、多くの設定をしなければならず大変ですが、ログイン機能にLINEログインを追加して、ユーザーの利便性を向上させてみてはいかがでしょうか。

参考:
https://developers.line.biz/ja/docs/line-login/secure-login-process/
https://pub.dev/packages/flutter_line_sdk

おまけ

Flutterに関する知識を深めたい方には、『Flutterの特徴・メリット・デメリットを徹底解説』という記事がおすすめです。

この記事では、Flutter アプリ開発の基本から、flutter とは何か、そして実際のflutter アプリ 事例を通じて、その将来性やメリット、デメリットまで詳しく解説しています。
Flutterを使ったアプリ開発に興味がある方、またはその潜在的な可能性を理解したい方にとって、必見の内容となっています。

ぜひ一度ご覧ください。

採用情報はこちら
目次