はじめに
こんにちは、たけむーです。
今回は、ポートフォリを開発していてtwitterログインの実装で詰まったのでメモとして残しておきます。
アプリ側の設定はSorceryのwikiとこの記事を参考にしました。
Twitter Developerの設定はこの記事を参考にしました。
この記事では設定の説明は省略させもらいます。
結論
先に結論を言うと、Twitter Developerでアクセス権限をEssentialからElevatedにしましょうという話です。
発生したエラー
上記の記事等を参考に設定を行い、実際に認証できるか動かしてみました。
すると、以下のようなエラーが出ました。
ActiveRecord::NotNullViolation - PG::NotNullViolation: ERROR: null value in column "name" of relation "users" violates not-null constraint
DETAIL: Failing row contains (55, null, null, null, null, 18668cfb-c19d-4d9b-85f3-28ac2719e9a9, 0, 2022-03-19 04:36:58.732046, 2022-03-19 04:36:58.732046, null, null, null, null, 0).:
app/controllers/oauths_controller.rb:26:in `create_user_from'
app/controllers/oauths_controller.rb:14:in `callback'
パッと見た感じだと、「Users
テーブルのname
カラムにはNOT NULL制約を付与しているのに、NULLで保存しようとして制約に引っ掛かってるよ」みたいなエラーだと思います。
確かに今回はUsers
テーブルのname
カラムにNOT NULL制約を付与しています。
create_table "users", force: :cascade do |t|
t.string "name", null: false
t.string "email", null: false
t.string "crypted_password"
t.string "salt"
t.string "uuid", null: false
t.integer "role", default: 0, null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
エラーが発生している箇所はoauths
コントローラのcreate_from
の部分です。
class OauthsController < ApplicationController
skip_before_action :require_login
def oauth
login_at(auth_params[:provider])
end
def callback
provider = auth_params[:provider]
if auth_params[:denied].present?
redirect_to boards_path, success: t('.success', item: provider.titleize)
return
end
create_user_from(provider) unless (@user = login_from(provider))
redirect_to foods_path, success: t('.success', item: provider.titleize)
end
private
def auth_params
params.permit(:code, :provider, :denied)
end
def create_user_from(provider)
@user = create_from(provider) # ←ここ
reset_session
auto_login(@user)
end
end
このcreate_from
でtwitterの情報を使ってユーザーを作成しますが、今回はここで失敗しています。
この時点で、twitterの情報がうまく取得できていないのかなと考えることができます。
試したこと(意味なかった)
- API keyやAPI Secret keyが設定できているかの確認、念のため再発行した
- CallbackURLがTwitter Developer側とアプリ側で一致しているか確認
- 開発環境で
http://127.0.0.1:3000/
にアクセスしているか
他にも今回の認証で触ったファイルでタイポ等がないか何回も確認しましたが、そういったミスは見つかりませんでした。
解決法
色々見たけど、特に原因はわからず...
ふと思った、そもそもcreate_from
って何してるの?
sorceryの定義されている部分を見てみます。
ここで定義されてました。
以下がメソッドの部分です。
def create_from(provider_name, &block)
sorcery_fetch_user_hash provider_name
# config = user_class.sorcery_config # TODO: Unused, remove?
attrs = user_attrs(@provider.user_info_mapping, @user_hash)
@user = user_class.create_from_provider(provider_name, @user_hash[:uid], attrs, &block)
end
一度、create_fromの前で処理を止めて一つずつ見てみましょう。
24: def create_user_from(provider)
25: binding.pry
=> 26: @user = create_from(provider)
27: reset_session
28: auto_login(@user)
29: end
[1] pry(#<OauthsController>)> provider
=> "twitter"
[2] pry(#<OauthsController>)> sorcery_fetch_user_hash(provider)
=> nil
次に、sorcery_fetch_user_hash
の部分を見てみます。
def sorcery_fetch_user_hash(provider_name)
# the application should never ask for user hashes from two different providers
# on the same request. But if they do, we should be ready: on the second request,
# clear out the instance variables if the provider is different
provider = sorcery_get_provider provider_name
if @provider.nil? || @provider != provider
@provider = provider
@access_token = nil
@user_hash = nil
end
# delegate to the provider for the access token and the user hash.
# cache them in instance variables.
@access_token ||= @provider.process_callback(params, session) # sends request to oauth agent to get the token
@user_hash ||= @provider.get_user_hash(@access_token) # uses the token to send another request to the oauth agent requesting user info
nil
end
ひとつずつ見ていきました。
(特に問題なかったので省略させていただきます。)
[4] pry(#<OauthsController>)> sorcery_get_provider provider
省略
[5] pry(#<OauthsController>)> @provider
省略
[6] pry(#<OauthsController>)> @provider != provider
省略
[7] pry(#<OauthsController>)> @access_token
省略
問題は次です。
[8] pry(#<OauthsController>)> @user_hash
=> {:token=>*******************************,
:user_info=>
{"errors"=>
[{"message"=>
"You currently have Essential access which includes access to Twitter API v2 endpoints only. If you need access to this endpoint, you’ll need to apply for Elevated access via the Developer Portal. You can learn more here: https://developer.twitter.com/en/docs/twitter-api/getting-started/about-twitter-api#v2-access-leve",
"code"=>453}]},
:uid=>""}
本来ならユーザーの情報が格納されていて欲しいのに、エラーメッセージが格納されています。
翻訳してみると、以下のようになりました。
You currently have Essential access which includes access to Twitter API v2 endpoints only. If you need access to this endpoint, you’ll need to apply for Elevated access via the Developer Portal. You can learn more here: https://developer.twitter.com/en/docs/twitter-api/getting-started/about-twitter-api#v2-access-leve
現在、Twitter API v2エンドポイントへのアクセスのみを含むEssentialアクセス権をお持ちです。このエンドポイントへのアクセスが必要な場合は、開発者ポータルからElevatedアクセスに申請する必要があります。詳しくはこちらをご覧ください: https://developer.twitter.com/en/docs/twitter-api/getting-started/about-twitter-api#v2-access-leve
なるほど。
このメッセージに従って、アクセス権をEssential
からElevated
に変更します。
最初にも載せたこの記事を参考に進めていきます。
Twitter Developerにアクセスすると以下のようになっていると思います。
右下を見ると、Essentialになっています。
これを先ほどの記事を参考にして設定すると以下のようになると思います。
これでアクセス権をElevatedに変更できました。
※申し訳ございませんが、細かいやり方については省略させていただきます。
この状態で再度twitter認証を試したところ成功しました👏
ちゃんとユーザーのデータも作られていました。
補足
Twitter APIを使う際に、利用目的を運営側に伝え審査を受ける必要があります。
これまではネットに転がっていた例文で通っていたのですが、これが2022年4月から厳しくなったようです。
あくまで私の予想なのですが、改正個人情報保護法が関係していると思われます。
何度か運営側と英語でやりとりする必要がありそうですが、そこは各々でがんばってもらえればと思います。
このあたりについては詳しいことが分かれば追記しようと思います。
最後に
今回はこれで以上となります。
調べてもなかなか参考になる記事が出てこなかったので書かせていただきました。
少しでも参考になれば幸いです。
最後まで読んでいただきありがとうございました。