Autoriser les requêtes cross-origin uniquement pour le bus de messages

J’essaie d’implémenter un widget de chat qui peut être intégré dans n’importe quel site web et pour cela, j’ai décidé d’utiliser MessageBus pour communiquer entre le widget et mon backend Rails. Comme il peut être intégré dans n’importe quelle origine, j’ai dû gérer les requêtes inter-origines.

Cependant, je veux activer CORS uniquement pour les requêtes MessageBus et non pour toutes mes autres routes. J’ai déjà vu ce problème CORS configuration · Issue #135 · discourse/message_bus · GitHub

C’est ce que j’ai fait dans mon config/initializers/cors.rb.

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options]
  end
end

Cela fonctionne, mais cela autorise les requêtes inter-origines pour n’importe quelle route, ce qui n’est pas ce que je veux.

J’ai aussi essayé de créer mon propre middleware

# app/middleware/message_bus_cors_middleware.rb
class MessageBusCorsMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    # S'il s'agit de MessageBus et d'une requête OPTIONS, renvoyer les en-têtes CORS
    if env['PATH_INFO'].start_with?('/message-bus') && env['REQUEST_METHOD'] == 'OPTIONS'
      # Appliquer les en-têtes CORS pour les requêtes /message-bus
      headers = {
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD',
        'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Visitor-Token',
        'Access-Control-Max-Age' => '86400'
      }
      [200, headers, []]
    else
      @app.call(env)
    end
  end
end

Mais j’ai toujours des problèmes de requêtes inter-origines avec ce middleware.

Une autre idée ?

Une autre chose qui serait formidable serait d’activer les requêtes inter-origines uniquement pour des canaux spécifiques. (Dans mon cas, les canaux MessageBus de chat). J’utilise MessageBus dans d’autres parties de mon application qui ne font pas de requêtes inter-origines et je voudrais que cela reste ainsi.

2 « J'aime »

J’ai réussi à le faire fonctionner en utilisant la deuxième méthode (middleware personnalisé pour activer CORS uniquement pour les requêtes Message Bus). Le problème venait de access-control-allow-headers qui n’étaient pas correctement définis. La correction consistait à remplir access-control-allow-headers de la réponse avec les en-têtes de requête Access-Control-Request-Headers.

Voici l’implémentation finale :

class MessageBusCorsMiddleware
  HTTP_ORIGIN = 'HTTP_ORIGIN'
  HTTP_X_ORIGIN = 'HTTP_X_ORIGIN'

  HTTP_ACCESS_CONTROL_REQUEST_METHOD = 'HTTP_ACCESS_CONTROL_REQUEST_METHOD'
  HTTP_ACCESS_CONTROL_REQUEST_HEADERS = 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS'
  OPTIONS = 'OPTIONS'
  REQUEST_METHOD = 'REQUEST_METHOD'
  PATH_INFO      = 'PATH_INFO'

  def initialize(app)
    @app = app
  end

  def call(env)
    # Si c'est un message bus et une requête OPTIONS, renvoyer les en-têtes CORS
    if env[PATH_INFO].start_with?('/message-bus') && env[REQUEST_METHOD] == OPTIONS && env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]
      origin = env['HTTP_ORIGIN']
      # Appliquer les en-têtes CORS pour les requêtes /message-bus
      headers = {
        "access-control-allow-origin" => "*",
        "access-control-allow-methods" => "GET, POST, PUT, PATCH, DELETE, OPTIONS",
        "access-control-expose-headers" => "",
        "access-control-max-age" => "7200"
      }

      headers.merge!('access-control-allow-headers' => env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]) if env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
      [200, headers, []]
    else
      @app.call(env)
    end
  end
end

J’avais des difficultés à comprendre le rôle de l’en-tête HTTP « Access-Control-Request-Headers », j’ai donc demandé à ChatGPT :

L’en-tête HTTP « Access-Control-Request-Headers » est utilisé dans le contexte des requêtes Cross-Origin Resource Sharing (CORS). CORS est un mécanisme qui permet aux applications web d’effectuer des requêtes vers un domaine différent de celui d’origine de l’application.

Lorsqu’une application web effectue une requête cross-origin, elle envoie généralement une requête initiale « preflight » au serveur pour déterminer si la requête réelle est autorisée. La requête preflight inclut l’en-tête « Access-Control-Request-Headers », qui liste les en-têtes supplémentaires que l’application souhaite inclure dans la requête réelle.

Par exemple, si une application web souhaite inclure des en-têtes personnalisés tels que « X-Auth-Token » ou « Authorization » dans sa requête cross-origin, elle spécifierait ces en-têtes dans l’en-tête « Access-Control-Request-Headers » de la requête preflight.

Le serveur recevant la requête preflight peut alors inspecter l’en-tête « Access-Control-Request-Headers » pour déterminer si les en-têtes demandés sont autorisés pour la requête réelle. Si le serveur approuve les en-têtes demandés, il répond avec l’en-tête Access-Control-Allow-Headers approprié dans la réponse preflight, permettant à l’application web de poursuivre avec la requête réelle.

En résumé, « Access-Control-Request-Headers » est un en-tête HTTP utilisé dans les requêtes preflight CORS pour spécifier les en-têtes supplémentaires qu’une application web souhaite inclure dans sa requête cross-origin.

Je partage cela au cas où quelqu’un d’autre essaierait de faire la même chose. Je suis également ouvert aux commentaires, en particulier concernant les problèmes de sécurité que cette implémentation pourrait ouvrir.

1 « J'aime »

Vous pouvez ajouter des en-têtes de réponse supplémentaires par la documentation :

Par exemple dans config/initializers/message_bus.rb :

MessageBus.extra_response_headers_lookup do |env|
  [
    ["Access-Control-Allow-Origin", "http://example.com:3000"],
  ]
end
1 « J'aime »

J’ai oublié de le mentionner, mais c’est la première chose que j’ai essayée et ça n’a pas fonctionné. Mais c’était avant que je découvre Access-Control-Allow-Headers. Peut-être que ça fonctionnera si je les ajoute comme ça.


MessageBus.extra_response_headers_lookup do |env|
  [
    ["Access-Control-Allow-Origin", "http://example.com:3000"],
    ["Access-Control-Allow-Headers", env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]]
  ]
end

J’essaierai à nouveau de cette façon au lieu du middleware personnalisé et je mettrai à jour ce fil de discussion.

Pas de chance non plus. Pour l’instant, j’ai conservé le middleware personnalisé.