Authentication

Secure your Rails Pulse dashboard with your app's existing authentication. On by default outside development and test.

Rails Pulse has no built-in user accounts. You protect the dashboard with your app’s existing authentication, using one of two hooks in the initializer.

Defaults

  • Authentication is enabled by default outside development and test. Production, staging, and any custom environment name all require it.
  • With nothing configured, Rails Pulse falls back to HTTP Basic against the RAILS_PULSE_USERNAME (default admin) and RAILS_PULSE_PASSWORD environment variables. If RAILS_PULSE_PASSWORD is unset, every request is denied and a warning is logged.
  • Both hooks fail closed. If your check does not explicitly allow the request, it is rejected.

To turn authentication off in an environment where you genuinely do not want it:

config.authentication_enabled = !Rails.env.local?  # the default: off only in development and test

authorize is a predicate. It receives the controller and returns true to allow. Anything else, including false, nil, or a missing user, is a 403 Forbidden.

# config/initializers/rails_pulse.rb
RailsPulse.configure do |config|
  config.authorize = ->(controller) { controller.current_user&.admin? }
end

A zero-argument proc runs in the controller’s context instead, so your app’s helpers are available directly:

RailsPulse.configure do |config|
  config.authorize = proc { user_signed_in? && current_user.admin? }
end

Use this for any check that can be expressed as “is this person allowed in?”. It replaces the HTTP Basic fallback on its own.

Devise with an admin role

config.authorize = proc { user_signed_in? && current_user.admin? }

Rails 8 built-in authentication

config.authorize = proc { authenticated? && Current.user.admin? }

IP address restriction

config.authorize = ->(controller) {
  allowed = ["127.0.0.1", "::1", "10.0.0.0/8"]
  allowed.any? { |ip| IPAddr.new(ip).include?(controller.request.remote_ip) }
}

Redirecting to a login page: authentication_method

If an unauthenticated visitor should be sent to your login page rather than shown a 403, use authentication_method. It runs inside the controller and denies by rendering or redirecting:

# config/initializers/rails_pulse.rb
RailsPulse.configure do |config|
  config.authentication_redirect_path = "/login"

  config.authentication_method = proc {
    unless user_signed_in? && current_user.admin?
      redirect_to main_app.root_path, alert: "Access denied"
    end
  }
end

Rules for this hook:

  • Rendering or redirecting denies the request.
  • Returning false without responding is also a denial (403).
  • Returning nil, which is what unless … end returns on success, allows the request.

Because nil allows, do not write predicate-style checks here. proc { current_user&.admin? } looks right but returns nil for a signed-out visitor and lets them in. Put predicates in authorize.

authentication_redirect_path is where users are sent when the hook raises. Never point it at /rails_pulse or you will create a redirect loop.

Using both

If both are set, authentication_method runs first, then authorize. This lets you redirect anonymous visitors to log in and then apply a stricter role check:

config.authentication_method = proc { redirect_to main_app.new_session_path unless user_signed_in? }
config.authorize = proc { current_user.admin? }

HTTP Basic with your own credentials

The built-in fallback already does this when nothing else is configured. To customise it, use constant-time comparison:

config.authentication_method = proc {
  authenticate_or_request_with_http_basic("Rails Pulse") do |username, password|
    ActiveSupport::SecurityUtils.secure_compare(username, ENV.fetch("RAILS_PULSE_USERNAME")) &&
      ActiveSupport::SecurityUtils.secure_compare(password, ENV.fetch("RAILS_PULSE_PASSWORD"))
  end
}

Security Warning: Store credentials in environment variables or Rails credentials. Never commit them to your repository.

What is not covered

  • The deployments API (POST /rails_pulse/deployments and PUT /rails_pulse/deployments/finish) uses its own token header and skips dashboard authentication. See Deployment Tracking. The browsable Deployments pages use the dashboard’s authentication like everything else.
  • The standalone dashboard (rails_pulse_server) ignores authorize and authentication_method, because your app’s session helpers are not available in that process. It uses HTTP Basic against RAILS_PULSE_USERNAME / RAILS_PULSE_PASSWORD unless you set config.standalone_authentication_method. See Standalone Dashboard.

Best Practices

  • Use authorize for predicates and authentication_method only when you need a redirect
  • Keep the default on for every non-local environment, including staging
  • Limit access to administrators. Performance data and exception messages can reveal details about your application
  • Combine checks such as a role check plus IP restriction for extra protection
  • Never hardcode credentials in your initializer

Next Steps