Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion blacksheep/server/cors.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,14 @@ async def cors_middleware(request: Request, handler):
return _get_invalid_origin_response()

# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
origin_response = b"*" if "*" in policy.allow_origins else origin
# A literal "*" is invalid alongside credentials per the Fetch spec - browsers
# reject the response outright - so the specific request origin must be echoed
# back instead whenever credentials are allowed.
origin_response = (
b"*"
if "*" in policy.allow_origins and not policy.allow_credentials
else origin
)

if next_request_method:
# This is a preflight request;
Expand Down
60 changes: 60 additions & 0 deletions tests/test_cors.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,66 @@ async def post_example(): ...
assert response.headers.get_single(b"Access-Control-Allow-Credentials") == b"true"


async def test_cors_preflight_request_allow_any_origin_with_credentials(app):
"""
A literal "*" can't be combined with credentials - browsers reject the response
outright (Fetch spec) - so when both are configured, the request's own Origin must
be echoed back instead of "*", on both preflight and actual responses.
"""
app.use_cors(
allow_methods="GET POST",
allow_origins="*",
allow_credentials=True,
)

@app.router.get("/")
async def home():
return text("Hello, World")

@app.router.post("/")
async def post_example(): ...

await app.start()

await app(
get_example_scope(
"OPTIONS",
"/",
[
(b"Origin", b"https://www.neoteroi.dev"),
(b"Access-Control-Request-Method", b"POST"),
],
),
MockReceive(),
MockSend(),
)

response = app.response
assert response.status == 200
assert (
response.headers.get_single(b"Access-Control-Allow-Origin")
== b"https://www.neoteroi.dev"
)
assert response.headers.get_single(b"Access-Control-Allow-Credentials") == b"true"

await app(
get_example_scope(
"GET",
"/",
[(b"Origin", b"https://www.neoteroi.dev")],
),
MockReceive(),
MockSend(),
)

response = app.response
assert (
response.headers.get_single(b"Access-Control-Allow-Origin")
== b"https://www.neoteroi.dev"
)
assert response.headers.get_single(b"Access-Control-Allow-Credentials") == b"true"


async def test_cors_preflight_request_allow_any(app):
app.use_cors(allow_methods="*", allow_origins="*", allow_headers="*")

Expand Down