gothinkster / realworld

"The mother of all demo apps" — Exemplary fullstack Medium.com clone powered by React, Angular, Node, Django, and many more
https://realworld-docs.netlify.app/
MIT License
80.57k stars 7.34k forks source link

[Bug]: Strange authentication error on api.realworld.io #878

Closed robtweed closed 2 months ago

robtweed commented 2 years ago

Relevant scope

Other: describe below

Description

I'm developiong a new front end version of Conduit, and testing it against the api.realworld.io back end. I have it all working perfectly on Chrome, Edge and Firefox, but if I try to run on Safari (desktop and mobile), I get the following error when using requests with the user's JWT in the Authorization header:

{ "status": "error", "message": "missing authorization credentials" }

The weird thing is that the request headers look just fine in web inspector and appear to be the same as the corresponding request headers in Chrome's Developer Tools.

Even stranger, using https://react-ts-redux-realworld-example-app.netlify.app/#/ on Safari works just fine.

Could the latter be because it's not going via heroku to get to the back-end perhaps?

I'm wondering if there was any more info available from the back end as to why it would generate that missing authorzation credentials error? I can only guess they're getting lost somewhere in the redirection chains that occur at the back-end, but why it happens on Safari and not the other browsers I'm using is a mystery so far. Any thoughts/ideas would be welcome!

Rob

robtweed commented 2 years ago

This is really bizarre - seems like a fundamental fetch bug in Safari but I'd have thought it would be something that would have been recognised everywhere. Here's how I'm sending an authenticated request to the api.realworld.io backend:

let url = host + '/api/articles/feed?offset=0&limit=10'; let options = { method='GET', headers: { 'Content-type': '/application/json', authorization: 'Token ' + jwt } }; let response = await fetch(url, options);

This works perfectly for this and every other Conduit REST URL on Chrome, Edge and Firefox, but on Safari the 307 redirect that happens within the back-end exchange isn't creating a request with the authorization header, so I get the 401 error above. It doesn't matter if I add stuff like credentials: 'include' to the fetch parameters, even if I turn off CORS restrictions in Safari - I just can't get Safari to add the authorization header to its redirected request.

I'm beginning to think I'm going mad, this seems like such a fundamental bug in Safari, or something I just can't see a simple solution to. Any ideas anyone?

Thanks, Rob

robtweed commented 2 years ago

A further follow-up - checking some of the other front-end implementations, they're using essentially the same use of fetch as me. They should also show the same problems if run in Safari against the api.realworld.io back end. I might see if I can try one out and confirm.

geromegrignon commented 2 years ago

Hey @robtweed, I confess I'm not familiar with Safari. I made some quick Google searches, and it seems to be a common issue with the browser. I'll try to find more information over the weekend.

robtweed commented 2 years ago

I've just confirmed that the same problem occurs if you run the Vanilla JS version - https://conduit-vanilla.herokuapp.com/#/ in - in Safari, so I'm not going mad! :-)

robtweed commented 2 years ago

Thanks Gerome!

robtweed commented 2 years ago

Some further follow-up. I tried switching to axios instead of fetch when using Safair, but the problem remains. From what I can determine, it's now pretty much impossible to persuade Safari browsers to redirect an Authorization header when using a CORS REST request - which is what most people using a RealWorld Conduit front-end will be doing. This is only a Safari limitation from what I can determine, but of course that means you can't run demos on an iPhone either.

My suggestion would be for Conduit back-end implementation to allow delivery of the JWT via either another, less-restricted header (eg X-Authorization perhaps), and/or as a value in the body payload or as a URL query string. If the back-end implementations first look for the Authorization header, and fall back to other alternatives places for the JWT, this wouldn't break existing implementations.

I've tried pretty much everything and searched on Google for a couple of days now to figure out workarounds for Safari, but I've come to the conclusion that Apple have completely broken Safari as far as Authorization headers via CORS is concerned. No other browser I've tested seems to have the same issue.

geromegrignon commented 2 years ago

Thanks for your suggestion, I will take a look at it.

robtweed commented 2 years ago

Just to confirm after a quick test - it looks like Safari will re-direct the X-Authorization header, so that would be a good alternative/fallback

git-nori commented 1 year ago

@robtweed The dialog box says Token: xxxxxxxx.yyyyyyyyy.zzzzzzzzz, but the actual value to be entered is Token xxxxxxxx.yyyyyyyyy.zzzzzzzzz.

By doing this, I was able to get the correct response without being rejected by the credentials.