これは何?
前回こちら の記事でPKCEのフローをまとめました。
今回は実際にdoorkeeper
というgem
を使って実装していこうと思います。
doorkeeper gemでPKCEを導入する手順
以前 こちら の記事でdoorkeeper gem
でOAuthプロバイダ機能を作成する記事を投稿しました。
今回は前回の状態からPKCEを導入する手順を記載します。
(※ doorkeeper gem のwikiにもPKCEについて記載があるのでご確認お願いします。)
(https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-PKCE-flow)
導入手順
# wikiに記載されているように以下のコマンドを実行します。
bundle exec rails generate doorkeeper:pkce
# 実行すると以下のmigrationファイルが作成されます。
db/migrate/20210330224805_enable_pkce.rb
migrationファイルの中身は以下のようになっており oauth_access_grants
テーブルへのカラム追加となっています。
# frozen_string_literal: true
class EnablePkce < ActiveRecord::Migration[6.0]
def change
add_column :oauth_access_grants, :code_challenge, :string, null: true
add_column :oauth_access_grants, :code_challenge_method, :string, null: true
end
end
migrateを実行します。
rails db:migrate RAILS_ENV=development
これで oauth_access_grants
テーブルへ code_challenge、code_challenge_method
を保存することができます。
続いてOAuth Applicationを作成していきます。
http://localhost:3000/oauth/applications
へアクセスし[New Application]をクリックします。
今回Client側のアプリのURLは http://localhost:3001
としcallbackURLを http://localhost:3001/pkce_callback
として準備しました。
画面に表示されている CLIENT_ID(UID)、CLIENT_SECRET、scopes
をClient側のアプリへ設定しておきます。
ここでPKCEの仕組みを以下へ記載します。
-
code_verifier
を使ってcode_challenge_method
に合ったcode_challenge
を生成し、code_challenge, code_challenge_method
をパラメータへ指定して認可画面をリクエストします。 - 認可サーバは
code_challenge, code_challenge_method
を保存し、Clientへ認可コードを返します。 - Clientは認可コードと
code_verifier
を使ってアクセストークンを取得します。 - 認可サーバで受け取った
code_verifier
を保存してあるcode_challenge_method
で変換してcode_challenge
のになるか検証します。
それではPKCEを使うためClientアプリケーションの OAuth2::Client
の部分を実装していきます。
以下は認可画面のURLを作成する処理のサンプルになります。
# 認可画面のURLを作成する処理
def pkce_authorization
session[:code_verifier] = SecureRandom.alphanumeric(100)
client = OAuth2::Client.new(
ENV["CLIENT_ID_PKCE"],
ENV["CLIENT_SECRET_PKCE"],
site: ENV["SITE"],
authorize_url: ENV["AUTHORIZE_URL"],
token_url: ENV["TOKEN_URL"]
)
code_challenge = Base64.urlsafe_encode64(OpenSSL::Digest::SHA256.digest(session[:code_verifier]), padding: false)
authorize_url = client.auth_code.authorize_url(
redirect_uri: ENV["CALLBACK_URI_PKCE"],
scope: ENV["SCOPE"],
code_challenge: code_challenge,
code_challenge_method: "S256"
)
redirect_to authorize_url
end
code_verifier
の値は、[A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" からなるランダムな文字列であり、最低43文字、最大128文字の長さが必要となります。
今回は
code_verifier = SecureRandom.alphanumeric(100)
のように100文字の英数字を設定します。ref:https://tools.ietf.org/html/rfc7636
code_challenge
は code_challenge_method
によって値が変わります。
code_challenge_method
を plain
にした場合
code_challenge = code_verifier
となります。
code_challenge_method
を S256
にした場合
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
となります。
ref:https://tools.ietf.org/html/rfc7636
これをrubyで実装すると
code_challenge = Base64.urlsafe_encode64(OpenSSL::Digest::SHA256.digest(session[:code_verifier]), padding: false)
で実装できます。
code_challenge_method
は 2種類あるのですが plain
は code_verifier
と code_challenge
が同じ値のため特に plain
を選ぶ理由がなければセキュリティリスクを考えて S256
を使用しましょう。
続いて認可コードが返ってきてからアクセストークンを取得する部分のサンプルが以下になります。
# 認可コードよりアクセストークンを取得
def pkce_callback
client = OAuth2::Client.new(
ENV["CLIENT_ID_PKCE"],
ENV["CLIENT_SECRET_PKCE"],
site: ENV["SITE"],
authorize_url: ENV["AUTHORIZE_URL"],
token_url: ENV["TOKEN_URL"]
)
code_verifier = session[:code_verifier]
session.delete(:code_verifier)
@access_token = client.auth_code.get_token(
params[:code],
redirect_uri: ENV["CALLBACK_URI_PKCE"],
code_verifier: code_verifier
)
render :callback
end
実際に認可画面から認可後、アクセストークンを取得できるか確認します。
念のため、認可サーバ側で code_challenge, code_challenge_method
が保存されているか確認しておきます。
Doorkeeper::AccessGrant.all
Doorkeeper::AccessGrant Load (0.3ms) SELECT "oauth_access_grants".* FROM "oauth_access_grants" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Doorkeeper::AccessGrant id: 1, resource_owner_id: 1, application_id: 2, token: "ygCPJhHoenE3vfiY1StLqCVBRJWTZfWnZFdOPUcTn7M", expires_in: 600, redirect_uri: "http://localhost:3001/pkce_callback", created_at: "2021-05-03 07:32:25", revoked_at: "2021-05-03 07:32:25", scopes: "public write", code_challenge: "oDJceObZlNuTivuP6EocfJZt2ODCdyjOh5IyUXK_4Rw", code_challenge_method: "S256">]>
正常にcode_challenge, code_challenge_method
が保存されていることが確認できました!
最後に
doorkeeper gem
を使用していればPKCEの導入は非常に簡単にできました。
Authorization Code Flowとの共存も可能なため実際の業務でもPKCEは導入しやすいと思います!