SO Questions Reviewed
I'm aware of the following questions:
Safari: "Blocked https://... from asking for credentials because it is a cross-origin request." after updating to Angular 8
How to use Angular behind Basic Auth protection?
Safari blocks URL from asking for credentials because it's a cross-origin request
But I've not been able to solve my issue.
The use case
I have some CSS and JS files that cannot be made openly public and therefore are protected by Basic Authentication. These files are located in a different origin (Let's say under Basic Auth-protected https://mycdn.com/
) from where they're requested (Let's say https://myapp.com/
).
Expectation
When I try to render a simple HTML page at https://myapp.com
that includes these files from https://mycdn.com/
I expect to be prompted for Basic Auth-credentials and for the browser to parse and execute them correctly.
Results
Chromium and Firefox
Latest Chromium-like and Firefox browsers behave as I expect, if my page is behind Basic Auth, then they prompt me for the app's site credentials and then they prompt me a second time for the "CDN" credentials (Not the most convenient thing UX-wise, but it serves the purpose). After that the page renders without issues.
Safari
Safari, (either desktop [14.0.3] or mobile [iOS 14]) on the other hand, behaves different. If my page is behind Basic Auth, it prompts me for credentials and starts rendering its contents, but when the engine reaches my files, it does NOT prompt me for credentials and it logs some error messages in the console stating a 401 Unauthorized
HTTP error occurred and depending on the script, the console will also contain an error saying:
Blocked ... from asking for credentials because it is a cross-origin request.
Analysis so far
It may seem like a mixed-content (HTTP included inside of an HTTPS page) but everything I've talked about so far is under HTTPS with valid certificates.
It also may seem like a CORS issue BUT, in reality the 401
returned by the server does in fact contain all of the needed CORS headers:
HTTP/1.1 401 Unauthorized
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
...
WWW-Authenticate: Basic realm="Fake Realm"
Note that file requests for script
and link
tags are GET
requests and therefore Access-Control-Allow-Origin: *
is permitted.
Test cases
I've tested:
script
andlink
tags without thecrossorigin
attributescript
andlink
tags with thecrossorigin
attributescript
andlink
tags withcrossorigin="anonymous"
script
andlink
tags withcrossorigin="use-credentials"
All of the above working in latest Chromium and Firefox but not in Safari 14.0.3 for macOS nor in Safari for iPadOS 14 nor in Safari for iOS 14.
Question
My question is: Has anyone got a setup like this (Basic authenticated cross-origin resources) working in Safari? Can you tell what may be causing this issue in my code (See below)?
Notice removing authentication from the "CDN" is NOT an option.
All of the this got me thinking that either:
- Safari does not have support for this type of requests (Basic authenticated cross-origin resources)
- Safari's implementation is off-spec
- Chromium and Firefox's implementation are off-spec
How to reproduce
I'm leaving the following example for you to test, it uses HTTPBin to mock a Basic Auth request which prompts for credentials in Chromium and Firefox but just fails to prompt in Safari.
User: user
Password: passwd
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Safari basic authentication issue</title>
</head>
<body>
<!-- This emulates HTTP authentication and when correctly authenticated returns a JSON response which is invalid JS but this serves for the purpose of prompting for credentials -->
<script src="https://httpbin.org/basic-auth/user/passwd" crossorigin="use-credentials"></script>
</body>
</html>