CORSPlug

repository·main·Indexed 19 days ago

https://github.com/mschae/cors_plug

An Elixir Plug providing configurable Cross-Origin Resource Sharing (CORS) support for Phoenix and other Plug-based applications. It allows developers to define allowed origins via strings, regexes, or dynamic functions, and manage preflight responses, allowed methods, and headers.

Tokens
2.4K
Snippets
8
Records
10
Agent score
15%

What's inside cors_plug

  1. Integrate CorsPlug into a Phoenix application

    main

    When using Phoenix, placing CORSPlug in a standard pipeline may not work because pipelines are only invoked for matched routes.

    There are two recommended integration patterns:

    1. Endpoint Level (Recommended): Add the plug directly to your Endpoint module so it intercepts all requests before they reach the router.
    2. Router/Pipeline Level: Add CORSPlug to a specific pipeline and ensure you define options routes for your resources to handle preflight requests.

    Note: Options passed directly to the plug override application configuration, which in turn overrides default options.

    # Pattern 1: Endpoint Level
    defmodule YourApp.Endpoint do
      use Phoenix.Endpoint, otp_app: :your_app
    
      # ...
      plug CORSPlug
    
      plug YourApp.Router
    end
    
    # Pattern 2: Router Pipeline Level
    pipeline :api do
      plug CORSPlug
      # ...
    end
    
    scope "/api", PhoenixApp do
      pipe_through :api
    
      resources "/articles", ArticleController
      options   "/articles", ArticleController, :options
      options   "/articles/:id", ArticleController, :options
    end
  2. Control preflight response with send_preflight_response?

    main

    By default, CORSPlug handles OPTIONS requests. If you want to retain manual control over the response sent to OPTIONS requests and only want CORSPlug to set the necessary headers, set send_preflight_response? to false.

    This can be configured via the plug options or in your application configuration.

    # Via plug options
    plug CORSPlug, send_preflight_response?: false
    
    # Via app config
    config :cors_plug,
      send_preflight_response?: false
  3. Configure the `origin` option

    main

    The origin option determines which request origins are allowed. It supports several types of values:

    • String: An exact match for the origin (e.g., "https://example.com").
    • List of Strings: A whitelist of allowed origins.
    • List containing "*": Allows all origins.
    • Regex: A regular expression to match the request origin.
    • :self: A special atom that whitelists the internal request origin (defaults to "*" if no origin is present).
    • Function: A function used to dynamically determine the allowed origin. Supported arities are:
      • 0: The function takes no arguments and returns the origin string.
      • 1: The function takes the %Plug.Conn{} as an argument and returns the origin string.
    • Single non-list value: If you provide a single string or regex instead of a list, it will be automatically wrapped into a list.
    # Using a Regex
    origin: ~r/https://.*\.example\.com$/
    
    # Using a List
    origin: ["https://example.com", "https://app.example.com"]
    
    # Using a Function (Arity 1)
    origin: fn conn -> if conn.method == "GET", do: "https://allowed.com", else: nil end
    
    # Using :self to whitelist internal requests
    origin: [:self]
  4. Configure allowed origins for CorsPlug

    main

    You can define which origins are allowed to access your resources using several methods. The origin configuration accepts strings, regular expressions, or a mix of both.

    Methods for defining origins:

    • List of strings or regexes: Pass a list directly to the plug.
    • Single Regex: Pass a single regex to the plug.
    • Application Configuration: Set the origin key in your config.exs.
    • Dynamic Functions: Pass a function (function/0 or function/1) that returns a list of allowed origins as strings.
      • Caveat: You cannot use anonymous functions because they cannot be quoted. Use a named function from a module instead.
      • function/0 takes no arguments.
      • function/1 receives the conn as an argument, allowing you to perform logic based on the connection before returning the allowed origins.
    # Using a list
    plug CORSPlug, origin: ["http://example1.com", "http://example2.com", ~r/https?.*example\d?\.com$/]
    
    # Using a regex
    plug CORSPlug, origin: ~r/https?.*example\d?\.com$/
    
    # Using config.exs
    config :cors_plug,
      origin: ["http://example.com"],
      max_age: 86400,
      methods: ["GET", "POST"]
    
    # Using a function/0
    plug CORSPlug, origin: &MyModule.my_fun/0
    
    defmodule MyModule do
      def my_fun do
        ["http://example.com"]
      end
    end
    
    # Using a function/1 (accessing conn)
    plug CORSPlug, origin: &MyModule.my_fun/1
    
    defmodule MyModule do
      def my_fun(conn) do
        # Do something with conn
        ["http://example.com"]
      end
    end
  5. Configure `headers`, `expose`, and `methods`

    main

    Use these options to control which headers and HTTP methods are permitted in CORS requests:

    • headers: A list of strings representing the allowed request headers. If set to ["*"], it will allow all headers requested by the client via the access-control-request-headers header.
    • expose: A list of strings representing the headers that the browser is allowed to access from the response. This is mapped to the access-control-expose-headers response header.
    • methods: A list of strings representing the allowed HTTP methods (e.g., ["GET", "POST"]). This is mapped to the access-control-allow-methods response header.
    [
      headers: ["Authorization", "Content-Type"],
      expose: ["X-Custom-Header"],
      methods: ["GET", "POST"]
    ]
  6. Configure CORSPlug options

    main

    CORSPlug can be configured via the options passed to the plug or via the application configuration using the :cors_plug key in your config.exs.

    When using the plug directly, you can pass a keyword list of options. When using application configuration, the values in config.exs are merged with the options passed to the plug, with the plug options taking precedence.

    # Example of default options structure
    [
      origin: "*",
      credentials: true,
      max_age: 1_728_000,
      headers: ["Authorization", "Content-Type", "Accept", ...],
      expose: [],
      methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
      send_preflight_response?: true
    ]
  7. Configure `send_preflight_response?`

    main

    Determines how the plug handles OPTIONS requests (CORS preflights):

    • If true: The plug will intercept OPTIONS requests, add the necessary CORS headers, return a 204 No Content response, and halt the connection.
    • If false: The plug will add CORS headers to the OPTIONS request but will not halt the connection, allowing the request to proceed to the next plug in the pipeline.
  8. Configure `credentials` and `max_age`

    main

    Control how the browser handles credentials and preflight caching:

    • credentials: A boolean. If true, the access-control-allow-credentials: true header is added to the response.
    • max_age: An integer representing the number of seconds the browser should cache the preflight (OPTIONS) response. This is mapped to the access-control-max-age header.
    [
      credentials: true,
      max_age: 3600
    ]
  9. Reference: CorsPlug headers and configuration keys

    main

    Headers returned by CorsPlug

    On preflight (OPTIONS) requests:

    • Access-Control-Allow-Origin
    • Access-Control-Allow-Credentials
    • Access-Control-Max-Age
    • Access-Control-Allow-Headers
    • Access-Control-Allow-Methods

    On GET, POST, etc. requests:

    • Access-Control-Allow-Origin
    • Access-Control-Expose-Headers
    • Access-Control-Allow-Credentials

    Configuration Keys

    KeyDescription
    originA list of strings/regexes, a single regex, or a function returning a list of strings.
    max_ageThe maximum age for the preflight request.
    methodsA list of allowed HTTP methods.
    send_preflight_response?Boolean. If false, the plug only sets headers and does not send the response for OPTIONS requests.

    Note: If no configured origin matches the request, the string null is returned for Access-Control-Allow-Origin as per W3C recommendations.