[basecamp/lexxy] 認証付きActiveStorageアップロードのサポート追加
Context
ActiveStorageを使用したファイルアップロードにおいて、認証が必要なストレージコントローラーを使用する場合、通常のXHRリクエストではCookieが送信されないため、認証に失敗する問題がありました。特にCORS環境下では、withCredentialsオプションを明示的に有効化する必要があります。
このPRは、Lexxyエディタで認証付きアップロードをサポートするための設定オプションと、テスト環境での検証機構を追加します。
Technical Detail
グローバル設定の追加
新たにauthenticatedUploads設定が追加され、ActiveStorageのアップロードリクエストにwithCredentials: trueを設定できるようになりました。
const global = new Configuration({
attachmentTagName: "action-text-attachment",
authenticatedUploads: false // 新規追加
})
この設定を有効化するには、以下のように記述します。
import * as Lexxy from "lexxy"
Lexxy.configure({ global: { authenticatedUploads: true }})
DirectUploadでの認証対応
ActionTextAttachmentUploadNodeにおいて、DirectUploadの2つのフックポイントでwithCredentialsを設定します。
upload.delegate = {
directUploadWillCreateBlobWithXHR: (request) => {
if (shouldAuthenticateUploads) request.withCredentials = true
},
directUploadWillStoreFileWithXHR: (request) => {
if (shouldAuthenticateUploads) request.withCredentials = true
// ...
}
}
-
directUploadWillCreateBlobWithXHR: Blobメタデータ作成時のリクエスト -
directUploadWillStoreFileWithXHR: 実際のファイルアップロード時のリクエスト
両方のフェーズで認証情報を送信する必要があります。
CORS設定とサブドメイン対応
テスト用のダミーアプリケーションでは、rack-cors gemを使用してCORS設定を行います。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins /^http:\/\/(([^.]*\.)?lexxy\.)?localhost(:\d+)?$/i
resource "/authenticated_direct_uploads", methods: :post, headers: :any, credentials: true
resource "/authenticated_direct_uploads/*", methods: [:post, :put], headers: :any, credentials: true
resource "/rails/active_storage/*", methods: [:post, :put], headers: :any, credentials: true
end
end
認証付きアップロード専用のコントローラーも追加されています。
class AuthenticatedDirectUploadsController < ActiveStorage::DirectUploadsController
skip_forgery_protection
before_action :require_auth_cookie
private
def require_auth_cookie
unless cookies[:auth_token].present?
head :unauthorized
end
end
end
このコントローラーはstorage.lexxy.localhostサブドメインでマウントされ、Cookie認証を要求します。
resources "authenticated_direct_uploads",
only: [:create, :update],
subdomain: LexxyApp.authenticated_storage_subdomain
Attachment Nodeのリファクタリング
PRには、Attachment関連のコードのクリーンアップも含まれています。
重要な変更点:
- conversion関数の引数: Lexicalのスタイルガイドに従い、conversion関数は要素を引数として受け取るように修正されました。
// 変更前
[this.TAG_NAME]: (attachment) => {
return {
conversion: () => ({
node: new ActionTextAttachmentNode({ ... })
})
}
}
// 変更後
[this.TAG_NAME]: () => {
return {
conversion: (attachment) => ({
node: new ActionTextAttachmentNode({ ... })
})
}
}
- グローバル設定の評価タイミング: タグ名などのグローバル設定は、静的クラスレベルのgetterで評価されるようになりました。
static get TAG_NAME() {
return Lexxy.global.get("attachmentTagName")
}
static importDOM() {
return {
[this.TAG_NAME]: (element) => { ... }
}
}
テストの追加
認証付きアップロードの動作を検証するシステムテストが追加されています。
test "authenticated upload succeeds with auth cookie" do
add_auth_cookie
visit edit_post_path(posts(:empty),
authenticated_storage: true,
configure_authenticated_uploads: true)
attach_file file_fixture("example.png") do
click_on "Upload file"
end
assert_image_figure_attachment content_type: "image/png", caption: "example.png"
end
Capybaraの設定も、サブドメインテストに対応するよう更新されています。
Capybara.app_host = "http://lexxy.localhost"
利用上の注意
認証付きアップロードを使用する際は、以下の設定が必要です。
- Cookie domain: サブドメイン間でCookieを共有できるよう、適切なドメイン設定を行う
-
CORS設定:
credentials: trueを含むCORS設定を行う - CSRF対策: 認証コントローラーでは適切なCSRF設定を行う
これらの設定が正しく行われていない場合、アップロードは401 Unauthorizedで失敗します。