🐡

[Ruby]サーバー側でのFacebookログインを実装[アクセストークン]

2021/08/31に公開

iOS, Androidアプリでログインなどの処理をAPIを通してサーバー側で担っている場合の実装です。

今回はIDトークンではなくアクセストークンを使うパターンです。
iOSのfacebookログインはIDトークンに対応していますが、AndroidではIDトークンには対応してないようなので。
https://developers.facebook.com/docs/facebook-login/limited-login

流れとしては

  1. AndroidでFacebook認証をしアクセストークンを取得
  2. アクセストークンをサーバー側へ送る
  3. サーバー側はアクセストークンを受け取り、検証して、ユーザー情報を取得
  4. ログイン・新規登録などの処理
    となるかと思います。

この記事では
3. サーバー側はアクセストークンを受け取り、検証し、ユーザー情報を取得
について書いていきます。

アクセストークンの検証

サーバー側は受け取ったアクセストークンを検証する必要があります。
受け取ったアクセストークンが自プロダクトのものなのか、期限が切れていないかをチェックします。
https://developers.facebook.com/docs/facebook-login/access-tokens/debugging-and-error-handling

上記リンク先のようにFacebook側にアクセストークン検証用のエンドポイントが用意されています。
それを使って以下の感じで実装します。

ACCESS_TOKEN_VERIFICATION_ENDPOINT = 'https://graph.facebook.com/debug_token'

def verify_access_token
  response = request_access_token_verification
  data     = response['data']

  data.present? &&
    data['is_valid'] == true &&
    data['app_id'] == app_id &&
    data['data_access_expires_at'] > Time.current.to_i &&
    data['expires_at'] > Time.current.to_i
end
      
def request_access_token_verification
  uri  = URI.parse(ACCESS_TOKEN_VERIFICATION_ENDPOINT)
  body = Faraday.get(uri, {
    input_token:  @access_token, # ここに受け取ったアクセストークンを渡す
    access_token: "#{app_id}|#{app_secret}" # こっちは自プロダクトのアクセストークン的なもの。app_idとapp_secretで生成できる
  }).body
  parsed_body = JSON.parse(body)
end

request_access_token_verificationのreturnは以下の形になります。

{
  "data":{
    "app_id":"{app-id}",
    "type":"USER",
    "application":"{app-name}",
    "data_access_expires_at":1576687825,
    "expires_at":1570820400,
    "is_valid":true,
    "scopes":[ "pages_show_list", "public_profile" ],
    "granular_scopes":[ { "scope":"pages_show_list", "target_ids":[ "{page-1-app-can-access-id}", "{page-2-app-can-access-id}" ] } ],
    "user_id":"10215241773831025"
   }
}

チェックする項目は
・"is_valid"がtrueか
・app_idが自プロダクトのものか
・data_access_expires_at, expires_atが切れていないか

これで受け取ったアクセストークンが、自プロダクト内のフローで正しく生成されたものだと判定できます。

ユーザー情報取得

検証したアクセストークンを用いてユーザー情報を取得します。
https://developers.facebook.com/docs/graph-api/reference/user
今回は、facebook上でのidとemailを取得することとします。

USER_INFO_ENDPOINT = 'https://graph.facebook.com/me'

def fetch_user_info
  uri  = URI.parse(USER_INFO_ENDPOINT)
  body = Faraday.get(uri, {
    access_token: @access_token, # 受け取ったアクセストークン
    fields:       'id,email
  }).body
  JSON.parse(body)
end

これでユーザー情報が取得できるので、あとはログインなり新規登録の処理を行いアプリ側にレスポンスを返してあげればOKです。

セキュリティ強化

Facebookでの設定で証明がないとグラフAPIを使えないようにすることができます。
https://developers.facebook.com/docs/graph-api/securing-requests
これにより、もしアクセストークンが第三者に漏れてしまった時などにそのアクセストークンを用いての情報取得を防ぐことができまし

Discussion