Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

外部サービスで BootCamp ログインができるようにした(OAuth2.0 プロバイダー機能実装) #8250

Open
wants to merge 42 commits into
base: main
Choose a base branch
from

Conversation

MikotoMakizuru
Copy link
Contributor

@MikotoMakizuru MikotoMakizuru commented Dec 15, 2024

Issue

概要

OAuth 2.0 プロバイダー機能を実装しました。これにより、BootCamp のユーザ情報を外部サービスに提供できるようになります。
この機能を利用すると、新たに立ち上げたアプリに BootCamp のログイン機能を実装した場合、ユーザは新規登録をせずに、既存の BootCamp アカウントでログインできます。つまり、ユーザ登録の手間を省き、プロセスを簡素化できます。

ユースケースの一例として、フィヨルドブートキャンプ生が個人開発の課題として BootCamp に関連するサービスを作成する際、新規アカウントを作成することなく、BootCamp アカウントでログインできるようになります。

変更確認方法

  1. クライアントアプリを用意するため、doorkeeper-client リポジトリを git clone
  2. クライアントリポジトリのルートディレクトリで .env ファイルを作成
  3. .env ファイルに下記の OAuth2.0 クライアントの設定 を記載
  4. feature/oauth2-provider-doorkeeper ブランチをローカルに取り込む
  5. BootCamp アプリのサーバを起動
  6. 管理者(komagata)でログイン
  7. 画面右上のユーザアイコンから、「OAuth2 Provider」を選択し、OAuth2 Provider 画面へ遷移
  8. アプリケーション追加ボタンを押下し、新規作成画面に遷移
  9. 以下の内容を入力して、登録するボタン押下
項目 入力内容
Name doorkeeper-client
Redirect URL http://localhost:3001/users/auth/doorkeeper/callback
Confidential
Scopes read
  1. 詳細画面に表示される「Application ID:」と「Secret:」の内容をコピーして、3 の .env ファイルの DOORKEEPER_APP_IDDOORKEEPER_APP_SECRET にペースト
  2. クライアントアプリのサーバを起動(BootCamp アプリが 3000 ポートを使用している場合、bin/rails s -p 3001 など別のポートで起動)
  3. rails console を起動し、User.all を実行してユーザが存在しないことを確認
  4. 管理者がログインしているブラウザとは別のブラウザを使用し、http://localhost:3001/ にアクセス(このブラウザで BootCamp にログインしている場合は事前にログアウトしておく)
  5. クライアントアプリの「BootCampログイン」を押下
  6. BootCamp アプリのログイン画面に遷移することを確認
  7. BootCamp アプリで kimura ユーザとしてログインし、再度 http://localhost:3001/ へアクセス
  8. 「BootCampログイン」ボタンを押下
  9. 認証画面が表示されるので「承認」ボタンを押下
  10. クライアントアプリで kimura ユーザとしてログインできることを確認
  11. rails console を起動し、User.all を実行してログインしたユーザが存在していることを確認

OAuth0.2 クライアントの設定

DOORKEEPER_APP_ID = ""
DOORKEEPER_APP_SECRET = ""
DOORKEEPER_APP_URL = "http://localhost:3000"

Screenshot

変更前

管理者メニューに「OAuth2 Provider」が無い

スクリーンショット 2025-02-09 0 04 02

変更後

管理者メニューに「OAuth2 Provider」を追加

スクリーンショット 2025-02-09 0 02 22

アプリ一覧画面

スクリーンショット 2025-02-09 0 10 14

アプリ新規追加画面

スクリーンショット 2025-02-09 0 10 45

アプリ編集画面

スクリーンショット 2025-02-09 0 11 04

プロフィール情報読み取り認証画面

スクリーンショット 2025-02-22 20 44 43

アプリ認証失敗時のエラー画面(主に「Application ID」や「Secret」が正しく設定されいないときに表示されます)

スクリーンショット 2025-02-09 0 31 23

Redirect URL 先が無効な時に表示されるエラー画面

スクリーンショット 2025-02-09 0 36 21

BootCamp ログインするまでの動作確認動画

2025-02-09.1.48.05.mov

補足

OAuth とは

OAuth の仕組みについて以下の記事が、とてもわかりわかりやすく参考になりました。

show API が提供するユーザ情報

show API が提供するユーザ情報は以下のファイルに記載されている内容に基づいています。

(API の仕様は今後変更される可能性があるため、ここでは項目を詳細に記載しません)

OAuth2 用のテーブル追加

今回の実装により以下3つのテーブルが追加されます。

Table Name Description
oauth_applications クライアントアプリの情報を保持
oauth_access_tokens ユーザのアクセストークンを管理
oauth_access_grants 認可コードを管理

以下のドキュメントにカラムの説明などが記載されています。

Users テーブルとの関連

OAuth2 の各テーブルと Users テーブルの関連性を以下に示します。

  • oauth_applications:id が oauth_access_tokens.application_id と oauth_access_grants.application_id に紐づく

  • oauth_access_tokens:resource_owner_id が users.id に紐づく

  • oauth_access_grants:resource_owner_id が users.id に紐づく

ERD

OAuth2 の各テーブルと Users テーブルの関連性を示した ERD は次のようになります。

スクリーンショット 2025-02-09 2 53 46

@MikotoMakizuru MikotoMakizuru self-assigned this Dec 15, 2024
@MikotoMakizuru MikotoMakizuru force-pushed the feature/oauth2-provider-doorkeeper branch 2 times, most recently from 3eef673 to c732675 Compare December 22, 2024 10:25
@MikotoMakizuru MikotoMakizuru force-pushed the feature/oauth2-provider-doorkeeper branch from 5054dda to 32ff385 Compare January 7, 2025 16:16
@MikotoMakizuru MikotoMakizuru force-pushed the feature/oauth2-provider-doorkeeper branch from 32ff385 to 2aa7f4d Compare January 19, 2025 09:48
@MikotoMakizuru MikotoMakizuru force-pushed the feature/oauth2-provider-doorkeeper branch 4 times, most recently from 43ca3d7 to 45e4c13 Compare February 5, 2025 14:44
@MikotoMakizuru MikotoMakizuru force-pushed the feature/oauth2-provider-doorkeeper branch from 45e4c13 to 1c962e6 Compare February 8, 2025 15:05
@MikotoMakizuru MikotoMakizuru changed the title 外部サービスで BootCamp ログインができるようにした(OAuth2 Providerの機能実装) 外部サービスで BootCamp ログインができるようにした(OAuth2 プロバイダー機能実装) Feb 8, 2025
@MikotoMakizuru MikotoMakizuru changed the title 外部サービスで BootCamp ログインができるようにした(OAuth2 プロバイダー機能実装) 外部サービスで BootCamp ログインができるようにした(OAuth2.0 プロバイダー機能実装) Feb 8, 2025

class Oauth2ProviderTest < ApplicationSystemTestCase
setup do
visit_with_auth root_path, 'komagata'
Copy link
Contributor Author

@MikotoMakizuru MikotoMakizuru Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setup do で管理者ログインの処理を共通化している理由について説明します。

他のテストファイルと同様に、各テストで visit_with_auth メソッドを使用してログイン後にテストを継続しようとすると、意図せずログイン画面にリダイレクトされてしまい、テストが失敗します。

そのため、以下のように setup do 内でルートパス (root_path) に一度遷移し、Oauth2 Provider の画面へアクセスする流れを確立してから、アプリの CRUD 操作をテストするようにしました。

各テスト内でログイン処理を行った場合のテストコード

test 'admin can access oauth2 provider page' do
  visit_with_auth oauth_applications_path, 'komagata'
  assert_equal 'Oauth2 Provider', title
end

実行結果

$ bin/rails test test/system/oauth2_provider_test.rb:15
Running via Spring preloader in process 17722
Run options: --seed 45277

# Running:

[Screenshot Image]: /Users/mikoto/projects/bootcamp/tmp/screenshots/failures_test_admin_can_access_oauth2_provider_page.png
F

Failure:
Oauth2ProviderTest#test_admin_can_access_oauth2_provider_page [/Users/mikoto/projects/bootcamp/test/system/oauth2_provider_test.rb:17]:
--- expected
+++ actual
@@ -1 +1 @@
-"Oauth2 Provider"
+"ログイン | FJORD BOOT CAMP(フィヨルドブートキャンプ)"

rails test test/system/oauth2_provider_test.rb:15

このように、ログインページへリダイレクトされてしまい Oauth2 Provider の画面にアクセスできませんでした。

対策として今回のように setup do でログイン処理を共通化し、テストが期待通り動作するようにしています。

@MikotoMakizuru
Copy link
Contributor Author

@SuzukiShuntarou
はじめまして、PR のレビューをお願いしたいのですが可能でしょうか。
よろしくお願いします。

@SuzukiShuntarou
Copy link
Contributor

@MikotoMakizuru
レビューの件、承知いたしました。お時間をいただければと思います。
恐縮ですが、チーム開発に入ったばかりでレビューに慣れていないということもあり、
難しい場合はご連絡させていただきますので、よろしくお願いいたします。

@SuzukiShuntarou
Copy link
Contributor

SuzukiShuntarou commented Feb 13, 2025

@MikotoMakizuru

前置き

  • レビュー遅くなり申し訳ございません。また、doorkeeperに関する知識がゼロであるため、レビューしきるのが難しそうであることも、先に謝罪いたします。

レビュー

  • 以下が取り急ぎ私の方でレビューできそうな内容です。正直なところdoorkeeperに関する設定config/initializers/doorkeeper.rbについて、テスト周りについては全く分かっておらず、いたずらにお時間を頂いてしまうなという感想です。他コントローラーとビューの変更箇所については指摘事項はございません。
  • もし可能であればdiscord等でご相談をさせていただくか、または、別の方にレビューをお願いとするか、それかこのレビューでメンバーレビューは良いかどうかkomagataさん含めてご相談とするか、いずれかとさせて頂ければと思います。

レビュー1:app/models/oauth_access_grant.rbapp/models/oauth_access_token.rb

  • 明示的にclass_name: 'User'されているのはなぜでしょうか?関連名とモデル名が同じであったため、理由がわかりませんでした。

レビュー2:config/locales/ja.yml

  • 恐らくread: メールアドレスを含む基本的なプロフィール情報の読み取りが表示されているのだと思われ(違う場合はすみません)、改行の位置が気になりました。が、翻訳ファイルにも不適切な改行があるわけではなかったため、理由が私もわかりませんでした。お力になれずすみません。
    image

@MikotoMakizuru MikotoMakizuru force-pushed the feature/oauth2-provider-doorkeeper branch from 3a5cbaf to d95771a Compare February 14, 2025 10:40
@SuzukiShuntarou SuzukiShuntarou self-requested a review February 14, 2025 12:39
@MikotoMakizuru MikotoMakizuru force-pushed the feature/oauth2-provider-doorkeeper branch from bb4bf65 to 176157a Compare February 14, 2025 15:43
@MikotoMakizuru
Copy link
Contributor Author

MikotoMakizuru commented Feb 15, 2025

@SuzukiShuntarou

レビューありがとうございます!また、ペアプロの時間もいただきありがとうございました。お話した内容も含めて、こちらにまとめておきます。

レビュー遅くなり申し訳ございません。また、doorkeeperに関する知識がゼロであるため、レビューしきるのが難しそうであることも、先に謝罪いたします。

とんでもございません!できる範囲でレビューしていただき、とても感謝しております🙏 私も OAuth2.0 Provider 機能や doorkeeper の使い方については手探りで進めていたため、同じ状況でした。

明示的にclass_name: 'User'されているのはなぜでしょうか?関連名とモデル名が同じであったため、理由がわかりませんでした。

不要だったので削除しました。
7782625


恐らくread: メールアドレスを含む基本的なプロフィール情報の読み取りが表示されているのだと思われ(違う場合はすみません)、改行の位置が気になりました。が、翻訳ファイルにも不適切な改行があるわけではなかったため、理由が私もわかりませんでした。お力になれずすみません。

認証画面のデザイン(アプリの管理画面も含め)は、doorkeeper がデフォルトで提供しているものなので、細かなレイアウト調整が難しいです。もし細かな調整をする場合は View を作る必要があります。添付いただいた画像のプロンプトは、本来認証サンプル画像のように、「・Read」や「・Write」など 承認するとクライアントアプリができるようになること記載します。

そのため doorkeeper 側のデザインも width 確認画像 のように 260px の幅しか設けていないため、長いプロンプトを書こうとすると改行が発生します。

ただ、「読み取り」とだけ書かれていても、ユーザは何を読み取るのかわからないのでは?と思い、Google OAuth 認証画面(少し古いものですが)を参考に、今回のプロンプトを作成しました。

しかし、ペアプロでもお話ししたように、

  • 改行の位置が微妙
  • 管理者以外も閲覧する画面なので、見やすいレイアウトにしたほうがよい

といった点があるため、町田さんにもご意見を伺いたいと思います。

認証サンプル画像

image

width 確認画像

スクリーンショット 2025-02-15 18 45 26

Google OAuth認証画面

image


また、ペアプロでいただいた「後置 if を使わずに論理積 (&&) を使ったほうがよいのでは?」という点についてですが、個人的には好みの問題かと思いました。
ただ params[:id] == 'show' の条件が満たされる場合のみ doorkeeper_token の存在を確認する、という意図を明確にするなら、後置 if を使った書き方のほうが読みやすいのではないかと考えています。この点についても、ご意見いただけるとありがたいです!

論理積を使った書き方

def set_user
    @user = if params[:id] == 'show' && doorkeeper_token
            User.find(doorkeeper_token.resource_owner_id)
          else
            User.find(params[:id])
          end
end

後置ifを使った書き方

def set_user
    @user = if params[:id] == 'show'
            User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
          else
            User.find(params[:id])
          end
end

@SuzukiShuntarou
Copy link
Contributor

@MikotoMakizuru
昨日はペアワークありがとうございました!ペアワーク内での質問事項についてもご連絡ありがとうございます。
私のレビューはApproveといたしました。

しかし、ペアプロでもお話ししたように、
改行の位置が微妙
管理者以外も閲覧する画面なので、見やすいレイアウトにしたほうがよい
といった点があるため、町田さんにもご意見を伺いたいと思います。

  • 承知いたしました。doorkeeper側のデザインで修正が難しい。というお話しは伺っておりましたので、町田さんにご確認していただければ幸いです。私としても「読み取り」とだけ記載されているよりは良いのかな…?と思っております。

また、ペアプロでいただいた「後置 if を使わずに論理積 (&&) を使ったほうがよいのでは?」という点についてですが、個人的には好みの問題かと思いました。
ただ params[:id] == 'show' の条件が満たされる場合のみ doorkeeper_token の存在を確認する、という意図を明確にするなら、後置 if を使った書き方のほうが読みやすいのではないかと考えています。この点についても、ご意見いただけるとありがたいです!

  • こちらにつきましても承知いたしました。指摘するほどのことなのかどうか分かっていなかったため、ペアワークでお話をさせて頂きました。doorkeeperについての背景知識がないこともあり、何かしらの意図があるかを確認したかっただけですので、メンバーレビューではOKとさせていただきます!丁寧なご説明ありがとうございます。

@MikotoMakizuru
Copy link
Contributor Author

MikotoMakizuru commented Feb 16, 2025

@SuzukiShuntarou
レビューありがとうございます。

@machida
認証画面のプロンプトについてご意見いただきたいです。

未使用のルートとして doorkeeper/authorizations#show を無視する設定を .traceroute.yml に追加
doorkeeper gem のデフォルトの view を使用するとプロンプトがおかしな位置で開業してしまうため
@MikotoMakizuru MikotoMakizuru force-pushed the feature/oauth2-provider-doorkeeper branch from f951bc1 to 8377a6c Compare February 22, 2025 11:31
@MikotoMakizuru
Copy link
Contributor Author

MikotoMakizuru commented Feb 22, 2025

@machida

こちらの記事を参考に認証画面でプロンプトが改行されないように修正しました。エビデンスはプルリクの「プロフィール情報読み取り認証画面」の箇所にキャプチャ貼ってます。

関連コミット

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants