CORS not working with Uber API OAuth endpoints - javascript

I am trying to make client-side JS calls to Uber API endpoints which require the OAuth Bearer token, such as /v1/me, but I am receiving an error that the Access-Control-Allow-Origin header is not present on the response.
I have successfully obtained a Bearer token (server-side) to use in the Authorization header in the GET request.
In my Uber API application settings, I have my Origin URI set to my development server (https://localhost:9000).
Here is how I call the /v1/me endpoint:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.uber.com/v1/me');
xhr.setRequestHeader("Authorization", "Bearer MY_BEARER_TOKEN");
xhr.send();
In Chrome developer console I get the following error:
No 'Access-Control-Allow-Origin' header is present on the requested resource
To ensure that my Bearer token is valid, I successfully tested it using curl:
curl -H 'Authorization: Bearer MY_BEARER_TOKEN' 'https://api.uber.com/v1/me'
On a side note, I am able to successfully make calls to the API endpoints which do NOT require OAuth Bearer token, such as /products and /estimates/price using the Authorization Token header like so:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.uber.com/v1/products');
xhr.setRequestHeader("Authorization", "Token MY_SERVER_TOKEN");
xhr.send();
This leads me to believe that the problem is not an Uber API app configuration setting such as incorrect Origin URI.
One final note, when I obtain the OAuth token I am making the request from a Node Express app running on my development server at https://localhost:8080, which is different from where I am running my client-side JS app at https://localhost:9000. Making the request from the same port did not solve the problem however.
Any ideas? Thanks!

Are you using an iFrame anywhere in your app? That problem usually comes up when I'm requesting to use an iFrame with a product that doesn't normally allow people to do so.

After emailing with Uber tech support, the problem was that CORS was not properly setup on their end. As of yesterday the /v1/me endpoint is now working for me with no changes to my app configuration.

Related

Keycloak Introspection Endpoint

I'm trying to access to introspect endpoint in my Keycloak server /openid-connect/token/introspect from my front app, but I get next error:
Access to fetch at 'http://localhost:8180/auth/realms/backoffice/protocol/openid-connect/token/introspect' from origin 'http://localhost:8080' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Using Postman, curl or Node app this request works fine, but from my front-app using fetch method thows this error. I'm not sure it's possible query for introspect endpoint from front-app in the browser or if it's only possible from server app.
Other endpoints like:
openid-connect/token:
openid-connect/userinfo:
Works fine using the Postman JS code.
Keycloak config
My client in Keycloak has set up Web Origins * and Access Type confidential.
Client Code
My front app is simply the Postman code JS, and I deploy it using node http-server.
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("client_id", "my-client");
urlencoded.append("client_secret", "my-secret");
urlencoded.append("token", "eyJ...oCA");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("http://localhost:8180/auth/realms/backoffice/protocol/openid-connect/token/introspect", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
Header Response
The header response in userinfo endpoint comes with Access-Control-Allow-Origin and Access-Control-Allow-Credentials but is not present in introspect endpoint.
From the looks of it, the Keycloak server prevents the CORS headers to be set for the introspection endpoint. This could be a bug or by design. I tried it and I get the same error.
If you really want to access the introspect endpoint from the web app, you could set up a NGINX reverse-proxy in front of your Keycloak server and use it to add the missing headers.
That being said, according to oauth.com you should not leave the introspection endpoint available to the public, which is what you are currently doing since anyone can retrieve the client id and secret from your web app.
If the introspection endpoint is left open and un-throttled, it presents a means for an attacker to poll the endpoint fishing for a valid token. To prevent this, the server must either require authentication of the clients using the endpoint, or only make the endpoint available to internal servers through other means such as a firewall.
This could explain the decision not to allow CORS.
Another thing, it looks like you forgot to set the token_type_hint check out this stackoverflow post for more information.

Authentication failed due to: Missing custom request token cookie

We are running a hapi JS server which uses #hapi/bell with azure provider strategy to authenticate users on the back-end
Basically, say we have our back-end running on port225.5874.com and there is a login route https://port225.5874.com/api/v2/user/sso. Here are our routes server settings.
routes: {
security: true,
cors: {
origin: [
`${configConst.client.host}:${configConst.client.hostport}`
],
headers: ['Access-Control-Allow-Headers', 'Access-Control-Allow-Origin', 'Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'Accept-language'],
additionalHeaders: ['Access-Control-Allow-Headers: Origin, Content-Type, x-ms-request-id , Authorization'],
credentials: true
}
}
Navigating to that route directly in the browser returns us information from the azure provider. However, if we try to go to that back-end route from a front-end client (i.e. localhost) we are being thrown the following CORS error
Access to XMLHttpRequest at 'https://login.microsoftonline.com/... (redirected from 'https://port225.5874.com/api/v2/user/sso') from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
To us this sounds like (we might be wrong) Access-Control-Allow-Origin is missing from 'https://login.microsoftonline.com/... but we obviously don't have control over it.
Seems like we are missing something on the front-end but unsure what it would be. Any ideas?
The front-end should be using e.g. MSAL.js to authenticate the user and use JWT authentication in the back-end.
Or you have to somehow tell the front-end request not to follow redirects and detect the situation.

Apollo Client sending OPTIONS instead of GET HTTP method

I'm having trouble understanding Apollo Client library as it does not work as intended. Instead of sending the GET HTTP method, it sends the OPTIONS HTTP method even though I've put to use GET only when retrieving data from GraphQL server.
const client = new ApolloClient({
link: ApolloLink.from([
new MeteorAccountsLink(),
new HttpLink({
uri: 'https://selo-comments.herokuapp.com/graphql',
useGETForQueries: true
})
]),
cache: new InMemoryCache()
});
Console log from the browser:
OPTIONS https://selo-comments.herokuapp.com/graphql?query=%7B%0A%20%20comments(id%3A%20%22TFpQmhrDxQqHk2ryy%22)%20%7B%0A%20%20%20%20articleID%0A%20%20%20%20content%0A%20%20%20%20userId%0A%20%20%20%20createdAt%0A%20%20%20%20commentID%0A%20%20%20%20votes%0A%20%20%20%20blockedUsers%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D%0A&variables=%7B%7D 405 (Method Not Allowed)
Which obviously means that the HTTP method is incorrect even if it has the query parameter in the url. If you query that url using Postman or simply navigating to the url using browser's address bar, you will get GraphQL data. I have to use https://cors-anywhere.herokuapp.com/ in order to execute the query successfully.
What am I doing wrong?
The options request is probably a preflight request for CORS.
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood.
It is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method, Access-Control-Request-Headers, and the Origin header.
You probably need to configure your server to allow cross origin calls.
Maybe you can find some inspiration here to get u started. Allow CORS REST request to a Express/Node.js application on Heroku

cors issue on github oauth

import request from 'superagent';
const self = this;
request
.post('https://github.com/login/oauth/access_token')
.set('Content-Type', 'multipart/form-data')
.query({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
callback: 'http://127.0.0.1:3000/callback',
code,
state,
})
.end((err, res) => {
const token = res.body.access_token;
console.log(token);
self.setToken(token);
});
The code above will give me an error like this
XMLHttpRequest cannot load
https://github.com/login/oauth/access_token?client_id=112asdecf3805fdada12&…127.0.0.1%3A3000%2Fcallback&code=434ebd7bb98d9809bf6e&state=HelloWorld1234.
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://127.0.0.1:3000' is therefore not allowed
access.
I have no idea why even though I've registered the oauth application with github and callback url is http://127.0.0.1:3000/callback
While all the actual GitHub API endpoints support CORS by sending the right response headers, it is a known issue that the https://github.com/login/oauth/access_token endpoint for creating an OAuth access token does not support CORS requests from Web applications.
The very specific workaround for this case is to use https://github.com/prose/gatekeeper:
Gatekeeper: Enables client-side applications to dance OAuth with GitHub.
Because of some security-related limitations, Github prevents you from implementing the OAuth Web Application Flow on a client-side only application.
This is a real bummer. So we built Gatekeeper, which is the missing piece you need in order to make it work.
The general workaround is: Use an open reverse proxy like https://cors-anywhere.herokuapp.com/
var req = new XMLHttpRequest();
req.open('POST',
'https://cors-anywhere.herokuapp.com/https://github.com/login/oauth/access_token',
true);
req.setRequestHeader('Accept', 'application/json');
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.send('code=' + encodeURIComponent(location.query.code) +
'&client_id=foo' +
'&client_secret=bar');
...
See also How to use Cors anywhere to reverse proxy and add CORS headers.

Why does the preflight OPTIONS request of an authenticated CORS request work in Chrome but not Firefox?

I am writing a JavaScript client to be included on 3rd party sites (think Facebook Like button). It needs to retrieve information from an API that requires basic HTTP authentication. The simplified setup looks like this:
A 3rd party site includes this snippet on their page:
<script
async="true"
id="web-dev-widget"
data-public-key="pUbl1c_ap1k3y"
src="http://web.dev/widget.js">
</script>
widget.js calls the API:
var el = document.getElementById('web-dev-widget'),
user = 'token',
pass = el.getAttribute('data-public-key'),
url = 'https://api.dev/',
httpRequest = new XMLHttpRequest(),
handler = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
console.log(httpRequest.responseText);
} else {
console.log('There was a problem with the request.', httpRequest);
}
}
};
httpRequest.open('GET', url, true, user, pass);
httpRequest.onreadystatechange = handler;
httpRequest.withCredentials = true;
httpRequest.send();
The API has been configured to respond with appropriate headers:
Header set Access-Control-Allow-Credentials: true
Header set Access-Control-Allow-Methods: "GET, OPTIONS"
Header set Access-Control-Allow-Headers: "origin, authorization, accept"
SetEnvIf Origin "http(s)?://(.+?\.[a-z]{3})$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Note that the Access-Control-Allow-Origin is set to the Origin instead of using a wildcard because I am sending a credentialed request (withCredentials).
Everything is now in place to make an asynchronous cross-domain authenticated request, and it works great in Chrome 25 on OS X 10.8.2. In Dev Tools, I can see the network request for the OPTIONS request before the GET request, and the response comes back as expected.
When testing in Firefox 19, no network requests appear in Firebug to the API, and this error is logged in the console: NS_ERROR_DOM_BAD_URI: Access to restricted URI denied
After much digging, I found that Gecko doesn't allow the username and password to be directly in a cross-site URI according to the comments. I assumed this was from using the optional user and password params to open() so I tried the other method of making authenticated requests which is to Base64 encode the credentials and send in an Authorization header:
// Base64 from http://www.webtoolkit.info/javascript-base64.html
auth = "Basic " + Base64.encode(user + ":" + pass);
...
// after open() and before send()
httpRequest.setRequestHeader('Authorization', auth);
This results in a 401 Unauthorized response to the OPTIONS request which lead to Google searches like, "Why does this work in Chrome and not Firefox!?" That's when I knew I was in trouble.
Why does it work in Chrome and not Firefox? How can I get the OPTIONS request to send and respond consistently?
Why does it work in Chrome and not Firefox?
The W3 spec for CORS preflight requests clearly states that user credentials should be excluded. There is a bug in Chrome and WebKit where OPTIONS requests returning a status of 401 still send the subsequent request.
Firefox has a related bug filed that ends with a link to the W3 public webapps mailing list asking for the CORS spec to be changed to allow authentication headers to be sent on the OPTIONS request at the benefit of IIS users. Basically, they are waiting for those servers to be obsoleted.
How can I get the OPTIONS request to send and respond consistently?
Simply have the server (API in this example) respond to OPTIONS requests without requiring authentication.
Kinvey did a good job expanding on this while also linking to an issue of the Twitter API outlining the catch-22 problem of this exact scenario interestingly a couple weeks before any of the browser issues were filed.
This is an old post but maybe this could help people to complete the CORS problem. To complete the basic authorization problem you should avoid authorization for OPTIONS requests in your server. This is an Apache configuration example. Just add something like this in your VirtualHost or Location.
<LimitExcept OPTIONS>
AuthType Basic
AuthName <AUTH_NAME>
Require valid-user
AuthUserFile <FILE_PATH>
</LimitExcept>
It was particular for me. I am sending a header named 'SESSIONHASH'. No problem for Chrome and Opera, but Firefox also wants this header in the list "Access-Control-Allow-Headers". Otherwise, Firefox will throw the CORS error.

Categories

Resources