はじめに
アプリ制作課題の中で外部認証機能に関するコードを入力しているとき「これって同じ意味なのでは?いったい何が違うのか?」疑問に思った事があったのでアウトプットもかねて記事にまとめてみようと思います。
環境
- Windows, WSL
- Docker
- Ruby 3.2.3
- Rails 7.1.3
uid
と user_id
の違い
-
uid
:-
uid
は「ユーザーID」と見なされがちですが、これは外部認証プロバイダー(例えばGoogleやFacebook)が提供する一意の識別子です。各プロバイダーが独自にユーザーを識別するためのIDであり、user_id
とは別物です!!
-
-
user_id
:-
user_id
はアプリケーション内のusers
テーブルで管理されるユーザーの一意の識別子で、アプリケーション全体でそのユーザーを識別するために使用されます
-
uid
と user_id
を使用したコード例3つ
1. ユーザーが認証済みかどうかを確認するコード
# user_id が 1 のユーザーを見つける
user = User.find(1)
# 認証テーブルから、このユーザーの Google の uid を取得
authentication = Authentication.find_by(user_id: user.id, provider: "google")
# その uid が存在するかどうかで、Google 認証済みか確認
if authentication
puts "ユーザーは Google で認証済みです。"
else
puts "ユーザーは Google で認証されていません。"
end
解説:
-
user_id
はアプリ内でのユーザー識別子。 -
uid
は Google など外部サービスでのユーザー識別子です。このコードは特定のユーザーが Google 認証をしているかどうかを確認します。
1の該当ディレクトリとファイルの例
- ユーザーが特定の外部サービスで認証済みかどうかを確認する処理ようなロジックは、コントローラーのアクション内やサービスオブジェクトで使用されることが多いです。
-
コントローラー内:
app/controllers/sessions_controller.rb
-
サービスオブジェクト内:
app/services/authentication_service.rb
# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def check_authentication
user = User.find(params[:id])
authentication = Authentication.find_by(user_id: user.id, provider: "google")
if authentication
puts "ユーザーは Google で認証済みです。"
else
puts "ユーザーは Google で認証されていません。"
end
end
end
2. 外部サービスからのログイン処理
# Google から返された uid と provider を受け取る
received_uid = "1234567890"
provider = "google"
# 認証テーブルから該当するユーザーを探す
authentication = Authentication.find_by(uid: received_uid, provider: provider)
if authentication
user = User.find(authentication.user_id)
puts "ログイン成功: ユーザー #{user.id} が見つかりました。"
else
puts "ログイン失敗: ユーザーが見つかりません。"
end
解説:
- このコードは、外部サービスから渡された
uid
とprovider
を使って、該当するユーザーをアプリ内で探し、ログイン処理を行います。 -
authentication.user_id
を使って、関連するユーザーを見つけます。
2の該当ディレクトリとファイルの例
- 外部サービス(例えばGoogle)からのログイン処理で使用されるこのロジックは、通常、コントローラーのログインアクションや、OAuthのコールバックアクションに記載します。
-
コントローラー内:
app/controllers/sessions_controller.rb
# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def create_from_omniauth
received_uid = params[:uid]
provider = params[:provider]
authentication = Authentication.find_by(uid: received_uid, provider: provider)
if authentication
user = User.find(authentication.user_id)
puts "ログイン成功: ユーザー #{user.id} が見つかりました。"
else
puts "ログイン失敗: ユーザーが見つかりません。"
end
end
end
3. 新しい認証情報の保存
# 既存のユーザーに Google 認証を追加
user = User.find(1)
# 新しい認証情報を作成し保存
authentication = Authentication.create(
user_id: user.id,
provider: "google",
uid: "1234567890"
)
if authentication.persisted?
puts "認証情報が保存されました。"
else
puts "認証情報の保存に失敗しました。"
end
解説:
- このコードは、既存のユーザーに新しい外部サービス(Google)の認証情報を追加する例です。
-
user_id
とuid
を関連付けて、新しい認証情報をauthentications
テーブルに保存します。
3の該当ディレクトリとファイルの例
- このコードは、新しい認証情報をユーザーに関連付けて保存する処理です。通常は、ユーザーが新しい外部サービスでログインした際に、その情報をデータベースに保存するために使用されます。
-
コントローラー内:
app/controllers/authentications_controller.rb
-
サービスオブジェクト内:
app/services/authentication_service.rb
rubyコードをコピーする
# app/controllers/authentications_controller.rb
class AuthenticationsController < ApplicationController
def create
user = User.find(params[:user_id])
authentication = Authentication.create(
user_id: user.id,
provider: params[:provider],
uid: params[:uid]
)
if authentication.persisted?
puts "認証情報が保存されました。"
else
puts "認証情報の保存に失敗しました。"
end
end
end
ついでにマイグレーションファイルのコードだともっとわかりやすい
class SorceryExternal < ActiveRecord::Migration[7.1]
def change
create_table :authentications do |t|
t.integer :user_id, null: false
t.string :provider, :uid, null: false
t.timestamps null: false
end
add_index :authentications, [:provider, :uid]
end
end
1:class SorceryExternal < ActiveRecord::Migration[7.1]
この行は、新しいマイグレーションを作成していることを示します。SorceryExternal はマイグレーションの名前で、ActiveRecord::Migration[7.1] はこのマイグレーションが Rails 7.1 に対応していることを意味します。
2:def change
このメソッド内で、データベースに対する変更を記述します。change メソッドは、マイグレーションを実行するときと元に戻すときの両方を自動的に処理してくれます。
3:create_table :authentications do |t|
authentications という新しいテーブルを作成します。do |t| は、このテーブルのカラム(列)を定義するために使用されます。
4:t.integer :user_id, null: false
user_id という名前のカラムを整数型で作成します。null: false は、このカラムが NULL 値を持つことが許されない、つまり必須のカラムであることを意味します。このカラムは、User モデルと関連付けるための外部キー(外部テーブルのキー)です。
5:t.string :provider, :uid, null: false
provider と uid という2つのカラムを文字列型で作成します。null: false がついているため、これらのカラムも必須です。
provider は認証プロバイダの名前(例: "google", "facebook")を保存するために使用されます。
uid は、そのプロバイダ内でのユーザーIDを保存します。これにより、同じユーザーが複数のプロバイダで認証を持つことが可能になります。
6:t.timestamps null: false
created_at と updated_at という2つのタイムスタンプカラムが自動的に追加されます。これらは、レコードが作成された日時と更新された日時を記録します。null: false により、これらの値も必須になります。
7:add_index :authentications, [:provider, :uid]
provider と uid の組み合わせにインデックスを追加します。インデックスは、データベースの検索を高速化するために使用されます。特に、provider と uid の組み合わせが一意(ユニーク)であることを保証したい場合に有効です。
流れとしてはこんな感じ
- ユーザーがGoogleでログインする:
- authentications テーブルにそのユーザーの user_id、provider("google")、および uid を保存
- 後のログイン時にユーザーを識別
さいごに改めて
-
user_id
: アプリケーション内で一意にユーザーを識別するために使用します。 -
uid
: 外部サービスでのユーザー識別子で、provider
と組み合わせてユーザーを識別します。 -
SessionsController
: 主にセッション管理や認証に関連するコードが含まれるコントローラーです。外部サービスからのログイン処理や、ユーザーが認証済みかどうかの確認などのロジックが記載されます。 -
AuthenticationsController
: 外部サービスの認証情報を管理するためのコントローラーです。認証情報の保存や更新を行います。 - サービスオブジェクト: より複雑なビジネスロジックを管理するために使用されます。特定の機能に関連するロジックをまとめて管理するのに役立ちます。
ユーザーと外部サービスの認証情報を関連付けるために user_id
と uid
をどのように使用するかの、少し複雑。にしてもこんな流れで外部認証機能を利用してログインできているんだ、と知ることは面白いと感じました。
今回の記事が何か参考になれば幸いです。