Eric Workman

Trix and CORS

Published on

Managing files can be a complex task, especially when it comes to integrating different tools and services. If you've been working with Ruby on Rails, Action Text, Trix, Active Storage, and S3-compatible storage, you may have encountered the challenge of dealing with CORS (Cross-Origin Resource Sharing) restrictions.

When attempting to use Action Text and Trix for rich text editing with the scalable storage of Digital Ocean Spaces, CORS can become a significant hurdle. CORS is a security mechanism implemented by web browsers to restrict cross-origin requests and protect user data. Without proper configuration, you will face issues when trying to use these tools together.

Understanding the Components

Let's first gain a clear understanding of the key components involved:

Action Text: Action Text brings rich text content and editing to Ruby on Rails. It integrates the Trix editor with Active Storage.

Trix: Trix serves as a powerful WYSIWYG editor framework that enhances the editing experience. It provides features like formatting options, attachments, and lots of embedding capabilities.

Active Storage: Active Storage is a file attachment management system integrated into the Ruby on Rails framework. It simplifies file uploads, associations, and storage by using adapters, making it easier to handle attachments in web applications.

Digital Ocean Spaces: Digital Ocean Spaces is an S3-compatible object storage service for files. This is a blob-storage type service as opposed to a filesystem.

The Need for CORS Configuration

When allowing any client-side upload to a server resource on a different domain, it is crucial to address the issue of Cross-Origin Resource Sharing (CORS) restrictions. CORS is a security mechanism implemented by web browsers to protect users' data by limiting cross-origin requests. Cross-origin means any requests from one URL to a URL with a different scheme, port, or host. This means that requests following these patterns (or any combination of them)

are all cross-origin. CORS configuration is fetched via a "preflight" OPTIONS request by the client to the cross-origin domain, with a configurable time to allow a followup request after the preflight request.

By configuring CORS, you enable controlled access between your client-side web application and Digital Ocean Spaces, allowing the necessary cross-origin requests to be made. This configuration grants permissions for specific domains, headers, methods, and time durations, ensuring secure and authorized communication between the components involved. Neglecting to configure CORS properly will result in frustrating experiences such as blocked requests, errors, or missing functionalities.

Configuration Guide

Let's dive into the step-by-step configuration process to integrate these components. Start with the Rails Action Text guide for installing, configuring, and avoiding some common pitfalls of Action Text with Active Storage.

Configuring Digital Ocean Spaces for CORS:

Sign in to your Digital Ocean Spaces account and access the desired bucket where your files will be stored.

bucket settings

bucket settings

Navigate to the settings page within the space and add a new CORS configuration. This step allows you to define which external domains are allowed to access your files and in what way.

CORS rule

CORS rule

Define the allowed headers, methods, origins, and max age within the CORS settings. You will need to define all origins that will access this bucket. Restrict the origins to only domains you have control over or trust, and always separate environments. For a locally hosted application on port 3000 like a Rails app, an origin of http://localhost:3000 will work but will also allow any person hosting from this domain to access files in the methods specified. Do not use * for origin, as that will allow any domain to attempt to access this resource.

For Action Text and Trix, the proper domain, the GET and PUT methods, and an Allowed Headers value of * will allow the editor to function. Refinement should be done on the Headers, but I'll leave that as an exercise for the reader.

Save the configuration.

Configuring Active Storage for Digital Ocean Spaces:

Now we have to configure the Rails app. We'll start with setting up Active Storage to use Digital Ocean Spaces.

Add your Access Key and Secret from a Spaces API key for the project to config/credentials.yml.enc

digitalocean:
  access_key: <redacted>
  secret: <redacted>

Update config/storage.yml with a new configuration. Replace the nyc3 in the endpoint with your bucket's region and mybucketname in the bucket key with your bucket's name. The region key is not used, so it can be any value.

digitalocean:
  service: S3
  endpoint: https://nyc3.digitaloceanspaces.com
  access_key_id: <%= Rails.application.credentials.dig(:digitalocean, :access_key) %>
  secret_access_key: <%= Rails.application.credentials.dig(:digitalocean, :secret) %>
  bucket: mybucketname
  region: unused

And finally, modify config/environments/development.rb or the appropriate environment files to use the new configurtion.

config.active_storage.service = :digitalocean

Implementing Rich Text Fields:

Integrate Action Text and Trix in your application. This involves configuring the fields to be Rich Text Fields.

With a model for Article with a string field of body, modify the model definition at app/models/article.rb to

class Article < ApplicationRecord
  has_rich_text :body
end

Enable the Trix editor by changing the form at app/views/articles/_form.html.erb to

<%= form_with model: article do |form| %>
  <%= form.rich_text_area :body %>
<% end %>

The controller needs no additional configurtion, but at a minimum it should include a create method and use Strong Params.

class ArticlesController < ApplicationController
  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render :new, status: :unprocessable_entity
    end
  end

  private
    def article_params
      params.require(:article).permit(:body)
    end
end

Testing and Troubleshooting

Once you have completed the configuration of Action Text, Trix, Active Storage, and Digital Ocean Spaces with CORS, it is essential to thoroughly test and troubleshoot your implementation. This step ensures that your file management system functions seamlessly and without any unexpected issues.

Testing:

Validate file uploads and attachments within Action Text and Trix. Confirm that files are properly associated and stored in Digital Ocean Spaces. Validate that the stored rich text renders the attachments correctly. This is often a source for the GET method. Validate that attachments can be deleted in the editor. If I recall correctly, Trix does not delete from storage, but if it did, it would require the DELETE method. Verify that the permissions and access restrictions defined in your CORS configuration are functioning as intended. Test the file access from different domains to confirm that access is granted or denied correctly. Perform integration testing by using your web application with different scenarios, such as simultaneous uploads or collaborations involving multiple users. Validate the responsiveness and performance of the file management system to ensure that it meets your application's requirements.

Troubleshooting:

CORS issues will pop up in the browser console, often with an error about a preflight request. In my experience, seeing or debugging these from the server resource is usually a frustrating experience with too little logging or configurable options.

Fin

That's pretty much a wrap. I would highly recommend trimming down the allowed headers in the CORS configuration of the bucket, but that's a fairly brittle configuration since it is generally out of your control as the developer when using Trix.