Adding & Removing Users on Spree Commerce | OpsBlu Docs

Adding & Removing Users on Spree Commerce

Adding & Removing Users on Spree Commerce — setup, configuration, and best practices for Spreecommerce.

Spree Commerce is an open-source Ruby on Rails e-commerce framework. It manages two user types: admin users who access the Spree admin panel, and customer accounts who shop on the storefront. User management is handled through the admin interface, Rails console, or the Spree REST API (Storefront and Platform APIs).

How Spree User Management Works

Spree's user system is powered by the spree_auth_devise gem (built on Devise) or a custom authentication adapter. The key models are:

  • Spree::User -- Base user model for both admin and customer accounts
  • Spree::Role -- Named roles (default: admin). Custom roles can be added.
  • Spree::RoleUser -- Join table connecting users to roles

Admin access is determined by the admin role. Users without this role are treated as storefront customers.

Adding Admin Users via Admin Panel

  1. Log in to the Spree admin at https://your-store.com/admin
  2. Navigate to Settings > Users (or Users in the sidebar)
  3. Click New User
  4. Fill in the required fields:
    • Email (used for login)
    • Password and Password Confirmation
  5. Under Roles, check Admin to grant backend access
  6. Click Create

Adding Users via Rails Console

# Open Rails console
# bundle exec rails console

# Create an admin user
admin = Spree::User.create!(
  email: 'jane@company.com',
  password: 'SecurePass123!',
  password_confirmation: 'SecurePass123!'
)
admin.spree_roles << Spree::Role.find_or_create_by(name: 'admin')
puts "Created admin: #{admin.email} (ID: #{admin.id})"

# Create a customer (no admin role)
customer = Spree::User.create!(
  email: 'customer@example.com',
  password: 'CustPass123!',
  password_confirmation: 'CustPass123!'
)
puts "Created customer: #{customer.email} (ID: #{customer.id})"

Adding Users via Spree Platform API

The Platform API (v2) provides admin-level endpoints for user management:

# Create a new user via Platform API
curl -X POST "https://your-store.com/api/v2/platform/users" \
  -H "Authorization: Bearer $PLATFORM_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "email": "jane@company.com",
      "password": "SecurePass123!",
      "password_confirmation": "SecurePass123!",
      "spree_role_ids": []
    }
  }'

# Create an admin user (include admin role ID)
# First, get the admin role ID
curl "https://your-store.com/api/v2/platform/roles" \
  -H "Authorization: Bearer $PLATFORM_API_TOKEN"

# Then create with admin role
curl -X POST "https://your-store.com/api/v2/platform/users" \
  -H "Authorization: Bearer $PLATFORM_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "email": "admin@company.com",
      "password": "AdminPass123!",
      "password_confirmation": "AdminPass123!",
      "spree_role_ids": [1]
    }
  }'

Storefront API Account Creation

Customers self-register via the Storefront API:

# Customer self-registration via Storefront API
curl -X POST "https://your-store.com/api/v2/storefront/account" \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "email": "shopper@example.com",
      "password": "ShopperPass123!",
      "password_confirmation": "ShopperPass123!"
    }
  }'

Bulk User Management

Rake Task for Bulk Import

# lib/tasks/import_users.rake
namespace :spree do
  namespace :users do
    desc 'Bulk import users from CSV'
    task import: :environment do
      require 'csv'

      file = ENV['CSV_FILE'] || 'tmp/users.csv'
      admin_role = Spree::Role.find_or_create_by(name: 'admin')

      created = 0
      skipped = 0

      CSV.foreach(file, headers: true) do |row|
        if Spree::User.exists?(email: row['email'])
          puts "Skipped (exists): #{row['email']}"
          skipped += 1
          next
        end

        user = Spree::User.create!(
          email: row['email'],
          password: row['password'] || 'TempPass123!',
          password_confirmation: row['password'] || 'TempPass123!'
        )

        if row['role'] == 'admin'
          user.spree_roles << admin_role
        end

        puts "Created: #{row['email']} (role: #{row['role'] || 'customer'})"
        created += 1
      end

      puts "\nDone. Created: #{created}, Skipped: #{skipped}"
    end
  end
end

# Run with: bundle exec rake spree:users:import CSV_FILE=tmp/users.csv

CSV format:

email,password,role
editor1@company.com,TempPass123!,admin
editor2@company.com,TempPass456!,admin
customer1@example.com,CustPass1!,
customer2@example.com,CustPass2!,

Bulk Role Assignment

# Promote all users from a specific domain to admin
domain = 'company.com'
admin_role = Spree::Role.find_or_create_by(name: 'admin')

Spree::User.where('email LIKE ?', "%@#{domain}").find_each do |user|
  unless user.has_spree_role?('admin')
    user.spree_roles << admin_role
    puts "Promoted: #{user.email}"
  end
end

Removing and Deactivating Users

Deactivation via Devise Lock

Spree uses Devise, which supports account locking:

# Lock a user account (prevents login)
user = Spree::User.find_by(email: 'jane@company.com')
user.lock_access!
puts "Locked: #{user.email}"

# Unlock later if needed
user.unlock_access!

Via admin panel: If you have the devise :lockable strategy enabled, locked users appear with a "Locked" status in the admin Users list.

Soft Delete (If Using paranoia/discard Gem)

Many Spree installations add soft-delete support:

# With acts_as_paranoid or discard gem
user = Spree::User.find_by(email: 'jane@company.com')
user.discard  # or user.destroy if using paranoia (soft deletes)
puts "Soft-deleted: #{user.email}"

# Restore later
user.undiscard  # or Spree::User.with_deleted.find_by(email: 'jane@company.com').recover

Permanent Deletion

Via Admin Panel:

  1. Navigate to Settings > Users
  2. Find the user
  3. Click Edit
  4. Click Delete (or the trash icon)
  5. Confirm

Via Rails Console:

user = Spree::User.find_by(email: 'jane@company.com')
if user
  # Reassign orders to prevent orphaned records
  user.orders.update_all(user_id: nil)
  user.destroy!
  puts "Permanently deleted: jane@company.com"
end

What happens to their content:

  • Orders retain the user's email and shipping/billing addresses but the user_id foreign key becomes null (if user is deleted) or the association breaks
  • Reviews/Ratings (if using spree_reviews) are deleted with the user unless configured otherwise
  • Addresses in spree_addresses are deleted via dependent destroy
  • Payment sources (saved credit cards) are removed
  • Wishlist items are deleted
  • Store credits are voided
  • Return authorizations retain historical data but lose the user association

Reassign Orders Before Deletion

departing = Spree::User.find_by(email: 'departing@company.com')
replacement = Spree::User.find_by(email: 'replacement@company.com')

if departing && replacement
  departing.orders.update_all(user_id: replacement.id)
  puts "Reassigned #{departing.orders.count} orders"
end

SSO and External Authentication

OmniAuth Integration

Spree supports OmniAuth strategies for social and enterprise SSO:

# Gemfile
gem 'spree_auth_devise'
gem 'omniauth-google-oauth2'
gem 'omniauth-saml'

# config/initializers/devise.rb
Devise.setup do |config|
  config.omniauth :google_oauth2,
    ENV['GOOGLE_CLIENT_ID'],
    ENV['GOOGLE_CLIENT_SECRET'],
    scope: 'email,profile'

  # SAML for enterprise SSO
  config.omniauth :saml,
    assertion_consumer_service_url: 'https://your-store.com/users/auth/saml/callback',
    idp_sso_service_url: 'https://idp.company.com/sso/saml',
    idp_cert_fingerprint: ENV['SAML_CERT_FINGERPRINT'],
    name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
end
# app/models/spree/user_decorator.rb
module Spree
  module UserDecorator
    def self.prepended(base)
      base.devise :omniauthable, omniauth_providers: [:google_oauth2, :saml]
    end

    def self.from_omniauth(auth)
      user = Spree::User.find_by(email: auth.info.email)
      unless user
        user = Spree::User.create!(
          email: auth.info.email,
          password: Devise.friendly_token[0, 20]
        )
      end
      user
    end
  end
end

Spree::User.prepend(Spree::UserDecorator)

LDAP Authentication

# Gemfile
gem 'devise_ldap_authenticatable'

# config/initializers/devise.rb
Devise.setup do |config|
  config.ldap_logger = true
  config.ldap_create_user = true
  config.ldap_update_password = false
end

# config/ldap.yml
production:
  host: ldap.company.com
  port: 636
  attribute: uid
  base: ou=People,dc=company,dc=com
  admin_user: cn=admin,dc=company,dc=com
  admin_password: <%= ENV['LDAP_ADMIN_PASSWORD'] %>
  ssl: true

Access Audit Checklist

  • Review admin users in Settings > Users quarterly (filter by role)
  • Run Spree::User.joins(:spree_roles).where(spree_roles: { name: 'admin' }).pluck(:email) to list all admins
  • Check for users who have never placed an order but have admin access
  • Audit API tokens: rotate PLATFORM_API_TOKEN if team members with access leave
  • Review spree_log_entries for unusual admin activity
  • Verify Devise lockout settings (maximum_attempts, lock_strategy) in devise.rb
  • Check OmniAuth provider configurations for expired client secrets
  • Document all admin access changes in your deployment log