Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
34
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Organization

Amazon Cognito Identity でFacebookやTwitterログインの名寄せをしてみた

AWS SDK for Ruby V2のCognitoIdentity::Clientクラスを使ってみました。

サンプルコード

logins = {
    'graph.facebook.com' => 'facebook_oauth2_access_token',
    'api.twitter.com' => ['twitter_user_token', 'twitter_user_secret'].join(';')
}

cognito = Aws::CognitoIdentity::Client.new(region: 'us-east-1')
resp = cognito.get_id(
    identity_pool_id: 'us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX',
    logins: logins
)
pp resp.identity_id # => "us-east-1:YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"

説明

まず、Cognitoを使うとき、FacebookやTwitterのアクセストークンの取得操作は、自前で行う必要があります。(Amazon Cognito側ではない)

利用する際は事前に取得したアクセストークンをCognitoに渡すことで、Cognitoの側でユーザーID等を取得して、Cognito IDを割り当ててくれます。

その際に、複数の認証プロバイダのトークンをハッシュで渡すことで、それらを1つのCognito IDに割り当てる処理を行ってくれます。

アプリケーション側ではこのCognito IDを使ってユーザーを識別することで、簡単に名寄せを実現できます。

プロバイダトークンのハッシュを準備

logins = {
    'graph.facebook.com' => 'facebook_oauth2_access_token',
    'api.twitter.com' => ['twitter_user_token', 'twitter_user_secret'].join(';')
}

'プロバイダ' => 'トークン'のハッシュを作ります。ドキュメントに記載がありますが、TwitterについてはAPIから得られるtokensecret;でつないだ文字列を使う必要がある点に注意して下さい。

Aws::CognitoIdentity::Clientインスタンスを作る

cognito = Aws::CognitoIdentity::Client.new(region: 'us-east-1')

Aws::CognitoIdentity::Client#get_idでCognito IDを得る

resp = cognito.get_id(
    identity_pool_id: 'us-east-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX',
    logins: logins
)
pp resp.identity_id # => "us-east-1:YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"

事前に作成しておいたIdentity PoolのID(REGION:GUID形式)と、先ほど作成したプロバイダトークンのハッシュを渡します。

応答の#identity_idでREGION:GUID形式のCognitoIDを得られました。

なお、このAPIは公開APIなので、AWSのアクセストークンは不要とのこと。

Rails + Omniauthで実験したコード

以下、実際に実験したコードの一部を張っておきます。

class AuthController < ApplicationController
  # GET /auth/:provider/callback
  def callback
    # current_user.logins
    # ログイン中ならユーザーの(有効期限内の)アクセストークンのハッシュを得る、
    # 未ログインなら空の配列を用意する
    logins = current_user.present? ? current_user.logins : {}
    # 今回ログインしたプロバイダのトークンを準備する
    case omniauth[:provider]
      when 'facebook'
        logins['graph.facebook.com']  = omniauth[:credentials][:token]
      when 'twitter'
        logins['api.twitter.com']  = [omniauth[:credentials][:token], omniauth[:credentials][:secret]].join(';')
      else
        raise 'Unknown Provider'
    end

    # Cognito IDの取得し、それをユーザーの識別に使う
    cognito = Aws::CognitoIdentity::Client.new(region: 'us-east-1')
    resp = cognito.get_id(
        identity_pool_id: Rails.application.secrets.aws_cognito_identity_pool_id,
        logins: logins,
    )
    identity_id= resp.identity_id
    Rails.logger.debug "Cognito IdentityID: #{identity_id}"
    user = User.where(identity: identity_id).first_or_create

    # Credential.provide_for(provider_name)
    # 認証プロバイダに対応するSTIクラス(TwitterCredentialとかFacebookCredentialとかを返す)
    if provider_class = Credential.provide_for(omniauth[:provider])
      # ユーザーとアクセストークンを関連付ける。同一プロバイダの古いトークンがあれば削除。
      # (アクセストークンはサーバで持って置いて必要な時や名寄せの時に使います)
      provider_class.from_user_omniauth(
          user: user,
          omniauth: omniauth)
    else
      Rails.logger.error "Unsupported provider: #{omniauth[:provider]}"
    end

    # ログイン中のユーザーをセッションに入れておく
    session[:user_id] = user.id

    redirect_to root_url
  end

  def logout
    session.delete(:user_id)
    redirect_to root_url
  end

  private
  def omniauth
    request.env['omniauth.auth']
  end
end

感想

Cognitoを使うことで、割と面倒な名寄せを上手く実装できた気がします。

Tokyoリージョンはありませんが、用途的に頻繁にアクセスするものではないので、個人的には多少のレイテンシの高さは気にならないかなと思っています。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
34
Help us understand the problem. What are the problem?