[この記事は Khanh LeViet、デベロッパー アドボケートによる The Firebase Blog の記事 "Authenticate your Firebase users with LINE Login" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。]



Khanh LeViet
Developer Advocate


ソーシャル ログインを好んで利用するユーザーは最近特に増えてきています。Google ログインのほか、Facebook、Twitter などとのログイン連携を使うと、使用するサービスごとに新しくアカウントを作成する必要がなくなります。ユーザーにはそれぞれお気に入りのソーシャル ログイン プロバイダがありますが、アプリに各ログイン連携プロバイダを組み込んで管理するのは、多大な労力が必要です。この作業を楽にするのが Firebase Authentication です。

Firebase Authentication は、多くの有名なソーシャル ログイン プロバイダに対応しています。アプリに Firebase Authentication を組み込みさえすれば、Firebase は裏で自動的に複数のログイン連携プロバイダを管理してくれます。

しかし、ユーザーの居住国によっては、まだ Firebase がデフォルトで対応していないログイン連携プロバイダが好まれているかもしれません。たとえば、日本やアジアの国々では、多くのユーザーを持つ LINE ログインが幅広く普及しているので、そのサポートを期待する方もいらっしゃるでしょう。うれしいことに、Firebase Authentication のカスタム認証を使うと、LINE ログインをはじめとする多くのログイン連携プロバイダを簡単にサポートすることができます。

以前のブログ投稿では、ウェブと JavaScript SDK を使って Firebase Authentication がデフォルトで対応していないログイン連携に対応する方法を紹介しました。今回のブログ投稿では、iOS と Android に LINE ログインを組み込む方法を紹介しましょう。

設計の概要



ログインのフローは、次のような流れで実行されます。

ステップ 1: LINE Login SDK を使ってログインし、ユーザーの LINE アクセス トークンを取得します。

ステップ 2: LINE アクセス トークンを自分のサーバーに送信し、LINE 認証サーバーを使ってそれを検証します。トークンが有効である場合、ユーザーに対応する Firebase カスタム認証トークンを作成し、ユーザーの端末に返します。

ステップ 3: Firebase カスタム認証トークンを使って、端末から Firebase にログインします。

構築に着手する

準備

LINE ビジネス アカウントを取得して Firebase プロジェクトを準備するには、いくつかの設定作業が必要になります。
  • こちらの手順にしたがって LINE ビジネス アカウントを設定し、アプリに LINE SDK を組み込みます。
  • 次に、アプリに Firebase Authentication ライブラリが追加されていることを確認します(iOS / Android)。
  • カスタム認証トークンの生成に Firebase Server SDK を使うこともできますが、任意の JSON ウェブトークン ライブラリを使って生成することもできるため、これは必須ではありません。

LINE ログインフローの開始

LINE ログインのドキュメント(iOS / Android)を参照し、アプリに LINE SDK を組み込んで LINE ログイン フローを実装します。ユーザーのログインが成功すると、次のようにして LINE アクセス トークンを取得できます。

iOS(Objective-C)
NSString *lineAccessToken = self.lineAdapter.getLineApiClient.accessToken;


Android
LineAuthManager authManager = LineSdkContextManager.getSdkContext().getAuthManager();
final String accessToken = authManager.getAccessToken().accessToken;

次に、お好みのネットワーク通信ライブラリを使って、自分のサーバーにアクセス トークンを送信して検証します。今回のサンプルコードでは、iOS は GTM HTTP Fetcher を使い、Android は Volley を使っています。

iOS (Objective-C)
NSURL *url = [NSURL URLWithString:@"https:///verifyToken"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/json" forHTTPHeaderField:@"content-type"];

NSDictionary *token = @{@"token" : lineAccessToken};
NSError *error;
NSData *requestBody = [NSJSONSerialization dataWithJSONObject:token
                                    options:kNilOptions error:&error];
[request setHTTPBody:requestBody];
GTMHTTPFetcher *fetcher = [GTMHTTPFetcher fetcherWithRequest:request];
[fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) {
    if (!error) {
       // レスポンスから Firebase カスタム認証トークンを抽出
       // ・・・
    }
}];


Android
HashMap<string, string=""> validationObject = new HashMap<>();
validationObject.put("token", accessToken);
Response.Listener responseListener = new Response.Listener() {
   @Override
   public void onResponse(JSONObject response) {
       // レスポンスから Firebase カスタム認証トークンを抽出
       // ・・・
   }
};
JsonObjectRequest fbTokenRequest = new JsonObjectRequest(
       Request.Method.POST,
       "https:///verifyToken",
       new JSONObject(validationObject),
       responseListener, errorListener);

NetworkSingleton.getInstance(activity).addToRequestQueue(fbTokenRequest);</string,>


LINE アクセス トークンと Firebase カスタム認証トークンの交換

LINE アクセス トークンを検証し、ユーザーに対応する Firebase カスタム認証トークンを生成するには、サーバーが必要になります。Firebase Node.js Server SDK と Express ウェブサーバーを使うと、シンプルなサーバーを構築できます。

まず、サーバーはユーザーの端末から LINE アクセス トークンを受信し、LINE Social Rest API を使ってそれを検証します。API のレスポンスで忘れずに channelId の値を確認し、アクセス トークンがアプリから発行されていることを検証してください。これは他のアプリやチャンネルのアクセス トークンを再利用してアプリにログインしようとするなりすまし攻撃を防ぐためです。

サーバー(Node.js)

app.post('/verifyToken', (req, res) => {
  if (!req.body.token) {
    return res.status(400).send('Access Token not found');
  }
  const reqToken = req.body.token;

  // アクセス トークンを検証するために LINE サーバーにリクエストを送信
  const options = {
    url: 'https://api.line.me/v1/oauth/verify',
    headers: {
      'Authorization': `Bearer ${reqToken}`
    }
  };
  request(options, (error, response, body) => {
    if (!error && response.statusCode === 200) {
      const lineObj = JSON.parse(body);
       // なりすまし攻撃を防ぐために必ずトークンの channelId を確認
      if ((typeof lineObj.mid !== 'undefined') 
               && (lineObj.channelId === myLINEChannelId)) {
        // LINE サーバーによるアクセス トークンの検証が成功
        // Firebase トークンを生成し、端末に返却
        const firebaseToken = generateFirebaseToken(lineObj.mid);

        // LINE プロファイルで Firebase ユーザー プロファイルをアップデート
        updateUserProfile(reqToken, firebaseToken, lineObj.mid, () => {
          const ret = {
            firebase_token: firebaseToken
          };
          return res.status(200).send(ret);
        });
      }
    }

    const ret = {
      error_message:'Authentication error:Cannot verify access token.'
    };
    return res.status(403).send(ret);
     
    });

  }
});


LINE アクセス トークンの検証が成功したら、Firebase Server SDK を使って Firebase カスタム認証トークンを生成し、ユーザーの端末に返します。LINE のユーザー ID は、Firebase のユーザー ID として再利用できます。

サーバー(Node.js)
function generateFirebaseToken(lineMid) {
  var firebaseUid = 'line:' + lineMid;
  var additionalClaims = {
    provider:'LINE'
  };
  return firebase.auth().createCustomToken(firebaseUid);
}

Firebase Server Java SDK を使うこともできます。

App Engine Flexible EnvironmentCloud Functions のようなソリューションを使うと、サーバーを管理する必要はなくなります。

カスタム認証トークンによる Firebase へのログイン

Firebase カスタム認証トークンを受信したら、それを使って Firebase にログインします。

iOS(Objective-C)

[[FIRAuth auth] signInWithCustomToken:firebaseToken 
             completion:^(FIRUser * _Nullable user, NSError * _Nullable error) {
        // ログイン結果の処理
        // ・・・
    }];


Android

FirebaseAuth.getInstance()
    .signInWithCustomToken(firebaseToken)
    .addOnCompleteListener(new OnCompleteListener() {
        // ログイン結果の処理
        // ・・・
    });


試してみる

サンプルコードはオープンソースです。ぜひダウンロードしてお試しください。 https://github.com/firebase/custom-auth-samples/tree/master/Line


Posted by Khanh LeViet - Developer Relations Team