spiderweb/docs/middleware/cors.md
2024-09-02 00:38:09 -04:00

7.3 KiB

cors middleware

from spiderweb import SpiderwebRouter

app = SpiderwebRouter(
    middleware=["spiderweb.middleware.cors.CorsMiddleware"],
)

CORS, or Cross-Origin Resource Sharing, is an incredibly important piece of how different parts of the web communicate. As such, there is a CORS handler built into Spiderweb.

Tip

The CorsMiddleware should be placed as high as possible in the middleware list, as it needs as much control as possible over requests and responses.

This implementation is lovingly ripped lifted borrowed from Django CORS Headers, an industry-standard implementation for handing CORS that has existed for over a decade. It is essentially and functionally the same. The below doc is copy-and-pasted also borrowed from Django CORS Headers, with updates where needed. (They just already do a great job of explaining these things.)

The available configurations are listed below, and you must set at least one of three following settings:

  • cors_allowed_origins
  • cors_allowed_origin_regexes
  • cors_allow_all_origins

cors_allowed_origins

A list of origins that are authorized to make cross-site HTTP requests. The origins in this setting will be allowed, and the requesting origin will be echoed back to the client in the access-control-allow-origin header. Defaults to [].

An Origin is defined as a URI scheme + hostname + port, or one of the special values 'null' or 'file://'. Default ports (HTTPS = 443, HTTP = 80) are optional.

app = SpiderwebRouter(
    cors_allowed_origins=[
        "https://example.com",
        "https://sub.example.com",
        "http://localhost:8080",
        "http://127.0.0.1:9000",
    ]
)

cors_allowed_origin_regexes

A list of strings representing regexes that match Origins that are authorized to make cross-site HTTP requests. Defaults to []. Useful when cors_allowed_origins is impractical, such as when you have a large number of subdomains.

app = SpiderwebRouter(
    cors_allowed_origin_regexes=[
        r"^https://\w+\.example\.com$",
    ]
)

cors_allow_all_origins

If True, all origins will be allowed. Other settings restricting allowed origins will be ignored. Defaults to False.

Setting this to True can be dangerous, as it allows any website to make cross-origin requests to yours. Generally you'll want to restrict the list of allowed origins with cors_allowed_origins or cors_allowed_origin_regexes.

app = SpiderwebRouter(
    cors_allow_all_origins=True
)

Optional settings

All the following settings have sensible defaults, but are available if you want to tweak them for your use case. For most cases, you'll just want to leave these alone.

cors_urls_regex

A regex which restricts the URL's for which the CORS headers will be sent. Defaults to r'^.*$', i.e. match all URL's. Useful when you only need CORS on a part of your site, e.g. an API at /api/.

app = SpiderwebRouter(
    cors_urls_regex=r"^/api/.*$"
)

cors_allow_methods

A list of HTTP verbs that are allowed for the actual request. Defaults to:

DEFAULT_CORS_ALLOW_METHODS = (
    "DELETE",
    "GET",
    "OPTIONS",
    "PATCH",
    "POST",
    "PUT",
)

The default can be imported from spiderweb.constants so you can just extend it with custom methods. This allows you to keep up to date with any future changes. For example:

from spiderweb.constants import DEFAULT_CORS_ALLOW_METHODS as default_methods

app = SpiderwebRouter(
    cors_allow_methods=(
        *default_methods,
        "POKE",
    )
)

cors_allow_headers

The list of non-standard HTTP headers that you permit in requests from the browser. Sets the Access-Control-Allow-Headers header in responses to preflight requests. Defaults to:

CORS_ALLOW_HEADERS = (
    "accept",
    "authorization",
    "content-type",
    "user-agent",
    "x-csrftoken",
    "x-requested-with",
)

The default can be imported from spiderweb.constants so you can extend it with your custom headers. This allows you to keep up to date with any future changes. For example:

from spiderweb.constants import DEFAULT_CORS_ALLOW_HEADERS as default_headers

app = SpiderwebRouter(
    cors_allow_headers=(
        *default_headers,
        "my-custom-header",
    )
)

cors_expose_headers

The list of extra HTTP headers to expose to the browser, in addition to the default safelisted headers. If non-empty, these are declared in the access-control-expose-headers header. Defaults to [].

cors_preflight_max_age

The number of seconds (integer) the browser can cache the preflight response. This sets the access-control-max-age header in preflight responses. If this is 0 (or any falsey value), no max age header will be sent. Defaults to 86400 (one day).

Note: Browsers send preflight requests before certain “non-simple” requests, to check they will be allowed. Read more about it in the CORS MDN article.

cors_allow_credentials

If True, cookies will be allowed to be included in cross-site HTTP requests. This sets the Access-Control-Allow-Credentials header in preflight and normal responses. Defaults to False.

Note

The session cookie, by default, uses Lax as the security setting, which will prevent the session cookie from being sent cross-domain. If you want to use cors_allow_credentials, you will need to change session_cookie_same_site to none to bypass the security restriction.

cors_allow_private_network

If True, allow requests from sites on “public” IP to this server on a “private” IP. In such cases, browsers send an extra CORS header access-control-request-private-network, for which OPTIONS responses must contain access-control-allow-private-network: true. Defaults to False.

Refer to:

A note about CSRF

Most sites will need to take advantage of the Cross-Site Request Forgery protection built into Spiderweb. CORS and CSRF are separate, and Spiderweb wants you to be explicit about how the domains that you work with fit together. If you need to exempt sites from the Referer checking that Spiderweb performs does on secure requests, you can use the csrf_trusted_origins setting. For example:

from spiderweb.constants import DEFAULT_CORS_ALLOW_HEADERS as default_headers

app = SpiderwebRouter(
    cors_allowed_origins=[
        "https://read-only.example.com",
        "https://read-and-write.example.com",
    ],
    csrf_trusted_origins=[
        "https://read-and-write.example.com",
    ]
)