1
votes

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 and link tags without the crossorigin attribute
  • script and link tags with the crossorigin attribute
  • script and link tags with crossorigin="anonymous"
  • script and link tags with crossorigin="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>
1

1 Answers

1
votes

Per https://trac.webkit.org/changeset/228486/webkit/, the Safari behavior described in the question is intentional. Specifically, Safari blocks cross-origin requests for page subresources, and logs this:

Blocked <subresource> from asking for credentials because it is a cross-origin request.

The https://trac.webkit.org/changeset/228486/webkit/ commit message gives this rationale:

Prompts for credentials to load cross-origin subresources are typically seen as unexpected by a person that navigates to- or interacts with- a web page. The cross-origin and implicit loading nature of these subresources makes asking for credentials questionable because they are not being served by the same origin of the page a person explicitly loaded and are not guaranteed to correspond to an explicit user interaction other than the initial load of the page. We know that subresources that ask for credentials can be abused as part of a phishing attack. It seems reasonable to disallow cross-origin subresources from asking for credentials due to their questionable nature and the risk for abuse.

That commit message, written in 2018, also makes this claim:

This will also make the behavior of WebKit match the behavior of Chrome.

But while it’s possible that in 2018 Chrome may have also blocked subresources from prompting for credentials, it’s not true for Chrome 89. However, it does seems to be true for Chrome 91. So it may be that Chrome is in the process of changing its behavior to do the same blocking Safari does.

So I think the bottom line is: It isn’t possible to make Safari prompt for credentials for subresources, and it’s also not safe to build web applications on the assumption that prompting for credentials for subresources will continue to work in Chrome (nor in Firefox either, over the long term).