DEV Community

Cover image for Build Secure Shopify Apps with App Bridge
Sulman Baig
Sulman Baig

Posted on • Originally published at sulmanweb.com

Build Secure Shopify Apps with App Bridge

As a developer working with Shopify's ecosystem, I recently built a multi-tenant SaaS application that synchronizes customer data between Shopify stores and external services. In this article, I'll share my experience and technical insights into creating a secure, scalable Shopify app using App Bridge.

Project Overview

Our application needed to:

  • Handle multiple Shopify stores (multi-tenancy)
  • Process customer data securely
  • Provide a seamless embedded experience
  • Manage OAuth flows and webhooks
  • Handle billing subscriptions

Let's dive into how we accomplished these requirements using Rails 8 and Shopify's App Bridge.

Setting Up the Foundation

First, we set up our Rails application with the necessary Shopify integrations. Here's how our initial configuration looked:

ShopifyApp.configure do |config| config.embedded_app = true config.scope = "read_customers,write_customers" config.after_authenticate_job = false config.api_version = "2024-10" config.shop_session_repository = "Shop" config.webhooks = [ { topic: "app/uninstalled", address: "webhooks/app_uninstalled" }, { topic: "customers/create", address: "webhooks/customers_create" }, { topic: "customers/update", address: "webhooks/customers_update" } ] end 
Enter fullscreen mode Exit fullscreen mode

Multi-tenant Data Model

Our core data model revolves around the Shop model, which handles multi-tenancy:

class Shop < ActiveRecord::Base include ShopifyApp::ShopSessionStorageWithScopes has_many :customers, dependent: :destroy encrypts :api_key, deterministic: true end 
Enter fullscreen mode Exit fullscreen mode

Embedded App Architecture

One of the key aspects was creating a seamless embedded experience. We achieved this through our layout configuration:

<%# app/views/layouts/embedded_app.html.erb %> <!DOCTYPE html> <html lang="en"> <head> <script src="https://unpkg.com/@shopify/app-bridge@3.7.9"></script> <script> var AppBridge = window['app-bridge']; var createApp = AppBridge.default; var app = createApp({ apiKey: "<%= ShopifyApp.configuration.api_key %>", host: "<%= @host %>", forceRedirect: true }); </script> </head> <body> <%= yield %> </body> </html> 
Enter fullscreen mode Exit fullscreen mode

Secure Authentication Flow

We implemented authenticated controllers to ensure secure access:

class AuthenticatedController < ApplicationController include ShopifyApp::EnsureHasSession before_action :ensure_store_settings private def ensure_store_settings redirect_to settings_path unless current_shop.setup_completed? end end 
Enter fullscreen mode Exit fullscreen mode

Webhook Processing

Handling webhooks securely was crucial for our app. Here's our webhook processing implementation:

class WebhooksController < ApplicationController include ShopifyApp::WebhookVerification def customers_create shop = Shop.find_by(shopify_domain: params[:shop_domain]) if shop shop.with_shopify_session do process_customer_data(params) end end head :ok end private def process_customer_data(data) CustomerProcessingJob.perform_later( shop_id: shop.id, customer_data: data ) end end 
Enter fullscreen mode Exit fullscreen mode

Background Job Processing

We used Solid Queue for reliable background processing:

class CustomerProcessingJob < ApplicationJob def perform(shop_id:, customer_data:) shop = Shop.find(shop_id) shop.with_shopify_session do customer = shop.customers.find_or_initialize_by( shopify_customer_id: customer_data["id"] ) customer.update!( email: customer_data["email"], first_name: customer_data["first_name"], last_name: customer_data["last_name"] ) end end end 
Enter fullscreen mode Exit fullscreen mode

Billing Integration

We implemented Shopify's billing API to handle subscriptions:

config.billing = ShopifyApp::BillingConfiguration.new( charge_name: "App Subscription", amount: 4.99, interval: ShopifyApp::BillingConfiguration::INTERVAL_EVERY_30_DAYS, trial_days: 7 ) 
Enter fullscreen mode Exit fullscreen mode

User Interface with Tailwind CSS

We created a clean, responsive interface using Tailwind CSS:

<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-12 pt-20"> <div class="sm:flex sm:items-center"> <div class="sm:flex-auto"> <h1 class="text-base font-semibold leading-6 text-gray-900"> Customers </h1> <p class="mt-2 text-sm text-gray-700"> Manage your synchronized customers </p> </div> </div> <%= render "customer_list", customers: @customers %> </div> 
Enter fullscreen mode Exit fullscreen mode

Lessons Learned

Throughout this project, I learned several valuable lessons:

  1. Session Management: Always use Shopify's session tokens for authentication rather than storing raw access tokens.

  2. Webhook Reliability: Implement idempotency in webhook processing to handle potential duplicate events.

  3. Background Jobs: Use background jobs for any operations that might take more than a few seconds to complete.

  4. Error Handling: Implement comprehensive error handling and logging, especially for webhook processing and API calls.

  5. Security: Always encrypt sensitive data and never expose API keys in the frontend.

Conclusion

Building a multi-tenant Shopify app requires careful consideration of security, scalability, and user experience. By leveraging Rails, App Bridge, and modern development practices, we created a robust application that securely handles multiple stores and their data.

The combination of Shopify's App Bridge, Rails 8, and modern tools like Solid Queue and Tailwind CSS provided a solid foundation for building a scalable SaaS application.

Next Steps

If you're building a Shopify app, consider these recommendations:

  1. Start with a solid authentication and authorization system
  2. Implement webhook handling early in the development process
  3. Use background jobs for long-running tasks
  4. Plan for scalability from the beginning
  5. Follow Shopify's security best practices

Remember that building a multi-tenant application requires careful consideration of data isolation and security at every level of your application.


Happy Coding!


Originally published at sulmanweb.com

Top comments (0)