0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Github OAuthAppでOAuth2.0を体験してみた

Posted at

背景

OAuth2.0の仕組みを触って体験したかったので、Github OAuthAppを使って実践してみることにした。

理解に間違えなどがあればご指摘いただけると幸いです。

OAuth2.0とは?

クライアントアプリ(以降:クライアント)がユーザーが登録しているGihubのユーザー名を取得したいとする。
当然、クライアントはユーザーのGithubへのアクセス権限がない為、OAuth2.0という技術を使って解決する。
方法は、クライアントがユーザーにGithubへの認可・認証をしてもらい、Githubの認可サーバからアクセストークンを発行してもらうことで、クライアントは一時的にユーザーが認可した範囲のGithub情報へアクセスすることができるようになる。

尚、OAuth2.0のフローには4つの種類があり、当記事は認可コードを使ってアクセストークンを取得するフローで検証していく。

前提

・Githubアカウントを持っていること
・rails new でプロジェクトが作成されていること

※当記事はOAuth2.0の体験用に最小限かつ簡易的に作ったものです。ご参考にしていただける場合はセキュリティ面に特にご注意いただき自己責任でお願いします。

環境

Mac : Apple M2
rails "7.1.3.2"
ruby "3.2.3"

事前準備

①Github OAuthAppを作成して、各種設定とClient IDとClient secretsを取得しておく

Githubへログインして、画面右上のアイコンをクリック後
Settings > Developer settings > OAuth Apps > New OAuth App
のように遷移すると以下の登録ページに辿り着くため、各項目を任意で設定しRegister applicationボタンを押下

スクリーンショット 2024-10-11 13.14.29.png

登録後は Client ID と Client secrets が発行される
(機密性が非常に高い情報のため、実装や管理など取り扱い注意!!!)

注意事項

GithubでOAuthAppを作成するためのベストプラクティスでは、OAuthAppではなくGithubAppを使うことが推奨されている。

可能な場合、OAuth app の代わりに GitHub App を使用することを検討してください。 通常、GitHub Apps が OAuth apps より優先されます。 GitHub Apps では、きめ細かいアクセス許可が使われ、アプリでアクセスできるリポジトリをより細かく制御でき、有効期間の短いトークンが使われます。 これらの特徴により、アプリの資格情報が漏洩した場合に発生するおそれがある損害を制限することで、アプリのセキュリティを強化できます。
引用元:OAuth アプリを作成するためのベスト プラクティス

今回は体験のためOAuthAppを使用する

②Faradayをインストールする

APIでGithubのユーザー情報を取得する際、HTTPクライアントにFaradayを使う

# Gemfile
gem 'faraday'
bundle install

③Userモデルを作成する

rails g model User uid:integer name:string
rails db:migrate

実装していく

※Clien ID、Client_secretsはハードコーディングしない!!

①Github認可ページへ誘導する画面を作る

コントローラーとindexファイルを作成

rails g controller welcome index

ルートにしておく

routes.rb
Rails.application.routes.draw do
  root 'welcome#index'
end

indexファイルは、セッションを使ってページ表示を切り替えられるようにしておく

views/welcome/index.html.erb
<% if current_user %>
  <h1>
    <%= "あなたのGithubユーザー名は #{current_user.name} です!" %>
  </h1>
  <%= button_to 'サインアウト', signout_path, method: :delete %>
<% else %>
  <h1>Welcome!!</h1>
  <%= link_to 'Githubとの連携を許可する', github_auth_path %>
<% end %>

②リンクを押下したらGithub認可ページへ遷移させる

rails g controller Sessions

Github OAuthAppで作成したリダイレクトURIのパスを基に、routes.rbに追記していく

routes.rb
Rails.application.routes.draw do
  root 'welcome#index'
  
  get 'github/auth',               to: 'sessions#github_auth'
  # as: 'github_callback'はredirect_uriに設定するエイリアス
  get 'リダイレクトURIで設定したパス',  to: 'sessions#github_callback', as: 'github_callback'
  delete 'signout',                to: 'sessions#destroy',         as: 'signout'
end

indexファイルでセッションの有無によってページ表示を切り替えるためヘルパーメソッドを作る

sessions_helper.rb
module SessionsHelper
  def current_user
    @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
    @current_user
  end
end
application_helper.rb
module ApplicationHelper
  include SessionsHelper
end

このタイミングでサーバを起動すると以下の画面になる

スクリーンショット 2024-10-11 13.18.27.png

sessions_controller.rb
def github_auth
  redirect_uri = github_callback_url # routes.rbで作成したエイリアス
  scope = 'user' # 認可範囲の指定
  state = SecureRandom.hex(16) # 認可サーバからのレスポンスにおける正当化を判別する値を作成
  session[:oauth_state] = state # セッションにstate値を入れておいてレスポンス取得時に比較する

  # リンクを押下された際のリダイレクト先
  redirect_to "https://github.com/login/oauth/authorize?client_id=#{'Client ID'}&redirect_uri=#{redirect_uri}&scope=#{scope}&state=#{state}",
                allow_other_host: true
end

redirect_uri

ユーザーが認可・認証を終えた後に、認可サーバが認可コードやアクセストークンを送り返す先のURI。

scope

クライアントがリクエストする認可範囲の指定。
ユーザーは、認可時にクライアントがリクエストするscope範囲を許可するか拒否するかを選択をし、承諾した場合、クライアントは認可サーバからscope範囲のトークンを取得できる(厳密には、認可コードと引き換えにscope範囲のアクセストークンを取得する)
つまり、このscopeは'user'を指定しているため、プロファイル情報の読み書きを可能とするためのscopeとなる。

OAuth アプリのスコープ

state

OAuth2.0におけるCSRF(cross site request forgeries)対策として推奨される値。
クライアントで任意の整数を発行しリクエスト時のパラメータで送ることで、認可サーバはレスポンスに受取ったstate値を含めて返すことにより、クライアント側でstate値を比較し通信の正当化を判別することができる。

allow_other_host: true

Railsはデフォルトで、リダイレクト先が同じホストであることを求めている。
今回のリダイレクト先のホストはhttps://github.comのように異なるホストとなっているため、この状況を許可するためのオプションとなる。

③認可コードと引き換えにアクセストークンを取得しユーザー名を取得する

ユーザーが認可・認証を終えると、Githubの認可サーバは設定されたリダイレクトURIに向けて、認可コードを含むパラメーターを送る。(この際、ブラウザを経由して送られる)

sessions_controller.rb
# github_callbackアクションの前にFaradayを初期化する
before_action :client, only: %i[github_callback]

def github_callback
  code = params[:code] # 認可コード
  state = params[:state] # リクエスト時に送ったstate値
  redirect_uri = github_callback_url # アクセストークンを取得するリクエスト時に使用

  # state値を比較し異なる場合はセッションデータを削除
  if state != session[:oauth_state]
    session.delete(:oauth_state)
    return redirect_to root_url
  end

  # アクセストークンを取得
  access_token = fetch_access_token(code, redirect_uri)

  # Githubのユーザー情報をapi取得
  user_data = fetch_user_data(access_token)

  # ユーザー名をマッピング保存
  user = find_or_create_user(user_data)

  if user
    session[:user_id] = user.id
    redirect_to root_url
  end

  # セッションの削除
  def destroy
    session[:user_id] = nil
    redirect_to root_url
  end
end

private

# Faradayを初期化
def client
  @client = Faraday.new do |f|
    f.ssl.verify = true
    f.request :url_encoded
    f.adapter Faraday.default_adapter
  end
end

def fetch_access_token(code, redirect_uri)
  body = {
    client_id: # Client ID,
    client_secret: # Client secrets,
    code: code,
    redirect_uri: redirect_uri,
  }

  headers = { 'Accept' => 'application/json' }

  response = @client.post('https://github.com/login/oauth/access_token', body, headers)
  JSON.parse(response.body)['access_token']
end

def fetch_user_data(access_token)
  headers = { 'Authorization' => "token #{access_token}"}

  user_data = @client.get 'https://api.github.com/user', nil, headers
  JSON.parse(user_data.body)
end

def find_or_create_user(user_data)
  user = User.find_or_create_by(uid: user_data['id'])
  user.update(name: user_data['login']) if 
  user_data['login'].present?

  user
end

画面上で試してみる

「Githubとの連携を許可する」リンクを押下
スクリーンショット 2024-10-11 13.18.27.png

認可ページ
スクリーンショット 2024-10-06 12.15.47.png

認証ページ
スクリーンショット 2024-10-06 12.16.22.png

セッションによって表示が切り替わったルートページ
スクリーンショット 2024-10-11 21.32.03.png

Githubのユーザー名が表示されていれば成功です!

参考記事

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?