HIGH api rate abuserailsopenid connect

Api Rate Abuse in Rails with Openid Connect

Api Rate Abuse in Rails with Openid Connect — how this specific combination creates or exposes the vulnerability

Rate abuse in a Ruby on Rails API that uses OpenID Connect (OIDC) typically occurs when protection mechanisms are applied inconsistently across authentication boundaries. OIDC introduces its own tokens—ID tokens and access tokens—carrying identity and authorization claims that Rails must validate before enforcing rate limits.

Consider a scenario where an endpoint accepts an OIDC access token and maps current_user from the token payload. If rate limiting is applied only after token validation, an attacker can make many unauthenticated or minimally authenticated requests to the token endpoint or to public endpoints that perform token introspection. Each request consumes token validation resources and can lead to token replay or substitution attempts before rate limits kick in.

In Rails, common misconfigurations include placing before_action :authenticate_user! after rate-limit logic or applying rate limits at the controller level without considering the identity derived from OIDC claims. For example, if different scopes or roles (e.g., admin vs. user) map to the same rate limit bucket, privilege escalation becomes feasible via token substitution.

Another vector involves the userinfo endpoint or custom claims used for authorization. An attacker can exploit endpoints that rely on claims that haven’t been strictly validated for freshness or binding to the token issuer. Without binding rate limits to the authenticated subject (sub) and issuer (iss), tokens from different users can share the same rate quota, effectively bypassing intended throttling.

Operational concerns also arise when token introspection or JWKS fetching is performed on each request. Without proper caching and rate limiting on the introspection path itself, an attacker can amplify load on the authorization server or on Rails’ OIDC validation logic, leading to denial-of-service conditions.

Real-world patterns mirror findings seen in frameworks like OWASP API Top 10 2023’s Broken Object Level Authorization (BOLA) and excessive resource consumption. CVE examples often involve missing binding between token identity and rate-limit identifiers, allowing attackers to exhaust quotas under a single identity or across shared namespaces.

Openid Connect-Specific Remediation in Rails — concrete code fixes

To remediate rate abuse in Rails with OIDC, tie rate limits to the authenticated identity derived from the token and enforce limits at the earliest point in the request cycle. Use a stable subject derived from the validated OIDC claims.

Example OIDC configuration with the omniauth-openid-connect gem:

# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :openid_connect,
    name: :oidc,
    identifier: ENV['OIDC_CLIENT_ID'],
    secret: ENV['OIDC_CLIENT_SECRET'],
    discovery: true,
    issuer: 'https://auth.example.com/',
    redirect_uri: '/auth/oidc/callback',
    scope: 'openid email profile offline_access',
    response_type: 'code',
    prompt: 'consent',
    client_options: {
      ssl: { verify_mode: OpenSSL::SSL::VERIFY_PEER }
    },
    id_token: true,
    access_token: true
end

After a successful callback, derive a stable subject for rate limiting:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :set_oidc_identity
  before_action :enforce_rate_limit, :only => [:api_action]

  private

  def set_oidc_identity
    return unless current_user_from_oidc
    # Use subject and issuer to uniquely identify the token holder
    @rate_limit_key = "oidc:#{current_user_from_oidc[:sub]}:#{current_user_from_oidc[:iss]"}
  end

  def current_user_from_oidc
    return @current_user_from_oidc if defined?(@current_user_from_oidc)
    # Assuming you store the decoded token in the session or request env
    @current_user_from_oidc = request.env['omniauth.auth']&.info&.to_hash || {}
  end

Apply rate limiting using a store that supports the composite key, for example with Redis and the rack-attack gem:

# config/initializers/rack_attack.rb
class Rack::Attack
  throttle('oidc/requests', limit: 60, period: 60) do |req|
    if req.env['omniauth.auth']&.info
      "oidc:#{req.env['omniauth.auth'][:info][:sub']}:#{req.env['omniauth.auth'][:info][:iss]}"
    end
  end

  throttle('oidc/introspect', limit: 30, period: 60) do |req|
    "introspect:#{req.env['HTTP_AUTHORIZATION']&slice(/Bearer\s(\w+)/, 1)"}" if req.path.match?(/introspect|token/) && req.post?
  end
end

For endpoints that rely on claims for authorization, validate the token’s scope and roles before applying business logic:

# app/controllers/api/v1/admin_controller.rb
class Api::V1::AdminController < ApplicationController
  before_action :require_scope!

  def require_scope!
    token_scopes = request.env['omniauth.auth']&.extra&.raw_info&.scopes || []
    unless token_scopes.include?('admin')
      render json: { error: 'insufficient_scope' }, status: :forbidden
    end
  end
end

Additionally, apply rate limits on the introspection and token validation paths separately to prevent amplification against the authorization server. Cache validated tokens and their claims for the period defined by exp, and ensure that the nonce claim is validated for ID tokens to mitigate replay.

These steps align with guidance from frameworks such as OWASP and address patterns found in real advisories by ensuring that rate limits are bound to a unique and trustworthy identity derived from OIDC validation, rather than IP or unauthenticated endpoints alone.

Frequently Asked Questions

How does binding rate limits to the OIDC subject prevent abuse?
Binding limits to the subject (sub) and issuer (iss) ensures that each authenticated token holder has an independent quota, preventing token sharing or substitution attacks that would allow an attacker to exhaust another user's rate allowance.
Should rate limiting be applied before or after OIDC validation?
Apply lightweight rate limits on unauthenticated paths (e.g., token discovery) before full validation, and enforce stricter limits after successful OIDC authentication using the derived subject to protect backend resources without blocking legitimate discovery traffic.