feat(19.3-02): cookie token refresh across subdomains with hardened credentialed CORS
- bouncer: CookieConfig from auth.cookie.* (LoadCookieConfig validates domain, same_site none without Secure, secure false in production), TokenCookie with Max-Age = refresh window, ForgetTokenCookie, CookieRefreshHandler and CookieLogoutHandler with an app-supplied writer - surf: http.cors refuses credentials with a * origin and bad or PHP-delimited patterns at boot, turns wildcard origins into anchored patterns, echoes the exact origin and sends Vary: Origin on every dynamic CORS answer; the non-credentialed * answer is unchanged - smoke tests for the www -> api refresh and the refused wildcard config
This commit is contained in:
@@ -224,4 +224,25 @@ cors:
|
||||
|
||||
This fragment belongs in `config/http.yaml`. List the frontend's exact origin; `*` is for public, credential-free APIs only.
|
||||
|
||||
The rules surf applies to the origin answer:
|
||||
|
||||
- `supports_credentials: true` together with `*` in `allowed_origins` fails the start-up. A credentialed answer that trusted any origin would let every site read responses made with the user's cookies; Laravel's php-cors reflects the origin in that case, surf refuses it.
|
||||
- An allowed origin is echoed exactly in `Access-Control-Allow-Origin`; `*` is sent only for `allowed_origins: ["*"]` without credentials, and that answer is unchanged from earlier releases.
|
||||
- When the answer depends on the request's `Origin` (with `supports_credentials`, with any pattern, or with more than one listed origin), every response on a CORS path carries `Vary: Origin`, the preflight and a disallowed origin's response included, so a shared cache never serves one origin's answer to another.
|
||||
- An `allowed_origins` entry with a `*` inside, such as `https://*.example.com`, matches like Laravel's `Str::is`: the `*` stands for any characters and the rest is literal.
|
||||
- `allowed_origins_patterns` entries are Go regular expressions. One that does not compile fails the start-up, and so does one still wrapped in PHP delimiters, such as `#^https://.*\.example\.com$#`: drop the delimiters and any flags when porting it.
|
||||
|
||||
A frontend on `https://www.example.com` that calls an API on `api.example.com` with the session cookie (see [Cookie transport](authentication.md#cookie-transport)) needs the www origin listed and credentials on:
|
||||
|
||||
```yaml
|
||||
cors:
|
||||
paths: ["api/*"]
|
||||
allowed_origins: ["https://www.example.com"]
|
||||
allowed_methods: ["*"]
|
||||
allowed_headers: ["*"]
|
||||
supports_credentials: true
|
||||
```
|
||||
|
||||
The browser sends the request with `credentials: "include"`; the answer carries `Access-Control-Allow-Origin: https://www.example.com`, `Access-Control-Allow-Credentials: true` and `Vary: Origin`.
|
||||
|
||||
surf answers every `OPTIONS` request on a CORS path itself, with 204, before any route runs, so a plugin never registers an `OPTIONS` route. A preflight gets the same headers Laravel's `HandleCors` sends: with `allowed_methods: ["*"]` the `Access-Control-Allow-Methods` value is the requested method, upper-cased, and with `allowed_headers: ["*"]` the `Access-Control-Allow-Headers` value is the requested header list.
|
||||
|
||||
Reference in New Issue
Block a user