Calling an API client side from webpage served by Express - javascript

I'll keep it short, I am getting a CSP (Content Security Policy) error when using the Fetch API in my client-side code. I cannot change the CSP because the request CSP is more strict than connect-src 'self' http://localhost:5000 (the meta tags of the page and 'use' functions in the middleware had no effect on the request CSP).
Maybe I'm missing something, there must be a way for client-side code to call an API?
Error:
Relevant code:
let USER_DETAILS = null;
async function get_by_api(url, data) {
const res = await fetch(url, {
method: "POST",
headers: {'Content-Type': 'application/json; charset=UTF-8'},
body: JSON.stringify(data)
});
// res.catch(function (_) {
// alert("Internal error. Please try again later.\nWe are sorry for the inconvenience.");
// localStorage.clear();
// window.location.replace("https://example.com");
// });
return res;
}
async function update_user_display() {
let user_email = localStorage.email;
let user_key = localStorage.app_key;
console.log("User email: " + user_email);
console.log("User key: " + user_key);
let user_res_data = await get_by_api("http://localhost:5000/v1/info", {
"email": user_email,
"key": user_key
});
user_res_data.then(async function (res) {
if (!(res.status === 200)) {
alert("Internal error. Please try again later.\nWe are sorry for the inconvenience.");
localStorage.clear();
window.location.replace("https://example.com");
return;
}
USER_DETAILS = await res.json();
document.getElementById("tb_user").innerHTML = USER_DETAILS.name;
});
}
fyi: localhost:5000 is what the test API is running on and localhost:5555 is what the app is running on
Thanks for any help.

Check the response headers when your page (response with content type "text/html") loads. You will likely see a CSP header with "default-src 'self'" there. This is the policy you need to change. First you will need to identify where it is set and then how your can change or override it.

Maybe I'm missing something, there must be a way for client-side code to call an API?
By default you just need the API server to grant permission to access it's data across origins via CORS.
In this case http://localhost:5000 has a CSP which prevents code in its pages from accessing anything other than http://localhost:5000. That's an explicit extra level of security that http://localhost:5000 has turned on (one key benefit is that it helps against XSS attacks). If you want to access http://localhost:5555 then you need to change the CSP to say that http://localhost:5555 is trusted (i.e. that you know it isn't a malicious site being used for XSS).
You say you can't change the CSP (it isn't clear why; http://localhost:5000 is your local development environment!) so you can't do that. This means that in your case it isn't possible to call that API.

Related

Is it possible to fetch WikiData by given random pageId?

I'm trying to write a simple Wikipedia Search App with included 'Feel Lucky' button.
Is it possible to send request for Wikidata by given pageid? Just like below:
async function luckySearch(){
cleanResults();
try{
let random = Math.floor(Math.random()*10000 + 1);
let endpoint = `https://en.wikipedia.org/w/api.php?action=parse&format=json&formatversion=2&pageid=${random}`;
let response = await fetch(endpoint,{
method:'GET',
headers:{
accept:'application/json',
},
type:'cors',
});
if(!response.ok){
throw new Error(`Error! status: ${response.status}`);
}
else{
let json = await response.json();
}
}
catch(err){
console.log(err.message);
}
}
and always throws 'Failed to Fetch'. Frankly, I'm not sure if either request is wrong, or CORS does not allow to fetch without origin set up. Can somebody explain that to me slowly with mercy, please?
I've changed properties of endpoint to make sure that request has right method. I've checked as well if API contains any action that replies to pageid of Wikipedia article
Yes, you are right: it's a CORS error : the browser does not let you access remote data on Wikipedia server because the server did not mark the request as safe for your domain. The Access-Control-Allow-Origin header is missing, as explained in this MDN article.
You should explicitly ask the wikipedia server to add this header using the origin parameter (see mediawiki API doc for details).
Changing your endpoint to this should work :
let endpoint = `https://en.wikipedia.org/w/api.php?action=parse&format=json&origin=*&formatversion=2&pageid=${random}`;

CORS issue while getting access token using client_credentials using javascript/reactjs from microsoft graph /token?

I need to get the access token using fetch() method but i am unable to do so with the fetch() method. Tried allowinng Cors-policy or cross-Access-Origin to all but nothing seems to working.
Access to fetch at 'https://login.microsoftonline.com/xxxxxxxxxxxxxxxxxxxxxxxxxxxx/common/oauth2/v2.0/token' from origin 'http://localhost:3050' has been blocked by CORS policy: 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.
graphService.ts:81 POST https://login.microsoftonline.com//common/oauth2/v2.0/token net::ERR_FAILED
Thanks in Advance:)
You are getting cors error because Azure AD rejects the request when you include Origin header while using client_credentials. To start with You are using the client_credentials flow the wrong way.
Consider reading through this guide on client credentials flow to help you understand why its preferred for server side / daemon applications.
To see the error just add Origin header to postman and do the request again.
Also read here to understand the oauth2 flows that you can use
For your case, you can use authorization code flow because you have an SPA.
It is not the flow or configuration that is causing the issue. The real issue is front-end or SPA. IF you want to call the API for getting token with client Credential flow,You must follow either of the two approaches that is a mandatory thing I guess.
1.Daemon Services
2.Server side Implementation
I have called the API with Node and with same configuration as mention in the docs its working fine now.
msal-node npm package is an alternate to call the api or you can call the graph api by yourself.
async getAppTokenFromAzureAD() : Promise <any | Error> {
const requestHeaders : HeadersInit = new Headers();
var details = {
'client_secret' : 'xxxxxxxxxxxxxxxxxxxxxx',
'client_id':'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'scope':'https://graph.microsoft.com/.default',
'grant_type':"client_credentials"
};
let formBody:any = [];
for (var property in details) {
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
requestHeaders.set('Content-Type', 'application/x-www-form-urlencoded');
requestHeaders.set('Host','login.microsoftonline.com');
return new Promise((resolve,reject)=>{
this.connection.transaction(async (entityManager) => {
try {
let tokenResponse = fetch('https://login.microsoftonline.com/xxxxxxxxxxxxxxxxxxxx9/oauth2/v2.0/token',{
method : 'POST',
headers : requestHeaders,
body : formBody
})
tokenResponse.then(data=>data.json()).then(responses=>{
resolve({
statusMessage: getMessageByKey('apllicationTokenSuccess'),
responses,
})
}).catch(error=>{
reject(error);
})
} catch (error) {
reject(error)
}
})
})
}
Thanks

Invalid input response and secret when verifying google reCaptcha

I am really struggling to get a successful response when doing a post request to the google recaptcha api. I am receiving the following response:
{
"success": false,
"error-codes": [
"invalid-input-response",
"invalid-input-secret"
]
}
I had a look at reCAPTCHA - error-codes: 'missing-input-response', 'missing-input-secret' when verifying user's response (missing details on POST) and followed the answer as closely as possible but with no success.
Here is my file below:
var request = require('request');
module.exports = {
verifyCaptcha: function(req, res) {
var secret = 'SECRET_KEY';
var response = JSON.stringify(req.body.response);
request({
url: 'https://www.google.com/recaptcha/api/siteverify',
method: 'POST',
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `secret=${secret}&response=${response}`,
}, function (err, response, body) {
if (err) {
res.status(500).send({
error: "Could not verify captcha"
});
} else {
res.status(200).send({
message: body
});
}
});
},
}
If anyone has a solution to this problem please let me know!
Due to the docs: https://developers.google.com/recaptcha/docs/verify
invalid-input-secret: The secret parameter is invalid or malformed.
Maybe you have mixed the site_key and the secret_key.
You need to add the user remote IP address.
var user_ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
request({
url: 'https://www.google.com/recaptcha/api/siteverify',
method: 'POST',
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `secret=${secret}&response=${response}&remoteip=${user_ip}`}...
Another thing I see that you are not using template literal, you should change the quotes to ` instead of '.
OR, You should use a ready-made module for reCaptcha, like this one:
https://www.npmjs.com/package/recaptcha
For reCAPTCHA Enterprise, check the official docs: https://cloud.google.com/recaptcha-enterprise/docs/create-assessment.
In short, you need to use the library that Google provides:
const { RecaptchaEnterpriseServiceClient } =
require('#google-cloud/recaptcha-enterprise');
const client = new RecaptchaEnterpriseServiceClient();
const [ response ] = await client.createAssessment({...});
RecaptchaEnterpriseServiceClient requires a service account to be created beforehand as described here. The key for that account with the right roles set can then be read by the app. Check the arguments of the constructor to see the available options to pass the data if the file cannot be retrieved automatically.
var response = JSON.stringify(req.body.response);
The stringifying here is probably the cause of the invalid-input-response error.
If your body is something like {"g-recaptcha-response": "..."}, you need to pull out the response value and pass that directly in your post.
Regarding invalid-input-secret, if you have set up your key and secret through the classic interface at https://www.google.com/u/1/recaptcha/admin/create, then you shouldn't have a problem.However if you set up a key with recaptcha Enterprise on Google Cloud, then it requires that you do Oauth authentication to the Google Cloud API and then use the create.assessment endpoint to get back information on the validity of the user. As Yuuhn implied, the Google provided library makes interaction with recaptcha Enterprise easier, without a lot of documentation digging to find where your REST API calls need to go.

How to handle redirect request in react to download file on browser.

I am new to react & JS. Please help me out in solving below problem.
Scenario: I am getting a SEE_OTHER type of response from the backend which contains a URL of the file, I need to download that file on browser.
Response from the backend is something like :
{ status:"SEE_OTHER"
context: {
...
...
headers:
Access-Control-Allow-Methods: ["GET, POST, DELETE, PUT"]
Access-Control-Allow-Origin: ["*"]
Location:[url from where file is to be downloaded]
.....
}
}
I'm trying to download the file after extracting URL from the response and execute below function below:
async download(url) {
let res = await fetch(URL.format(url));
if(res.status >= 400) {
throw Error(`HTTP error: ${res.status}`);
}
if(res.status === 200) {
setTimeout(() => {
window.location.href = res.url;
}, 100);
}
}
But I'm getting below error on browser console.
Failed to load
'www.xyz.com/abc.json'
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:2000' is therefore not allowed
access. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
Above code works if I use this extension on chrome. But what is a permanent solution for this problem.
Also, is there any way I could download file directly from the response.
If you are using express use the following package in your backend service to solve the issue.
https://github.com/expressjs/cors
The issue is because browsers by default prevent cross origin/domain access for few security reasons. Please refer the following link,
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

CORS issue with ASP.net Identity

I am working on an angular.js project with one of my friends, and we are running into a specific CORS (cross origin request) issue. The server is a Microsoft ASP.NET restful API, and I am using angular.js with Node.js.
We enabled CORS on the server side, and are able to get responses for everything else, accept the user login, which we are using ASP.NET Identity with. We always get the same error which I will post bellow, as well as the POST from the Client side. So basically my question is, does any one have an idea on how to fix this? Thanks!
XMLHttpRequest cannot load http://lectioserver.azurewebsites.net/api/v1/accounts/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'localhost' is therefore not allowed access. The response had HTTP status code 400.
function login(username, password) {
var innerconfig = {
url: baseUrl + "/api/v1/accounts/login",
data: {
username: username,
password: password,
grant_type: "password"
},
method: "POST",
headers:
{
'Accept': 'text/json'
}
};
return $http(innerconfig).then(onSuccess, requestFailed);
function onSuccess(results) {
if (results && results.data) {
$rootScope.access_token = results.data.access_token;
return results.data;
}
return null;
}
}
Try to set the content-type in the headers, this might fix the issue
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
This usually happens because app that provides you token starts before CORS initiates.
Fixing it is very easy. You just need to go to IdentityConfig.cs and inside that there is function called as
public static ApplicationUserManager Create
(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
Insert this following line of code there
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
This will enable CORS for Token request.
But problem is when we do this other normal requests will start throwing error since we have granted access origin * twice. Once in identiy and other in cors.
if you run into this error use this if statement on cors code in identity config you just pasted.
if(context.Request.ContentType == "text/plain")

Categories

Resources