How to access headers in react.js frontend built by rails backend? - reactjs

I have a get endpoint in billings_controller.rb like:
def index
plans = PricingPlan.all
response.header['publishable-key'] = Rails.configuration.stripe[:publishable_key]
render json: { status: true, message: 'List of Plans.', data: plans.to_a }
end
Now, I would like to access that HTTP header in react.js front-end app, the way I'm doing is like:
componentDidMount() {
zenApi.get(SERVER)
.then(response => {
this.setState({ pricing_plans: response.data, pub_key: response.headers['publishable-key'] });
});
}
But the response.headers is undefined. console.log(response) gives as:
{status: true, message: "List of Plans.", data: Array(6)}
but, when I look HTTP response headers in network tab in chrome developers tools, it looks like:
HTTP/1.1 304 Not Modified
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS, DELETE, PUT, PATCH
Access-Control-Expose-Headers: access-token, expiry, token-type, uid, client
Access-Control-Max-Age: 1728000
publishable-key: pk_test_fYfWQWtbSsGbb1*****50VrT
ETag: W/"15cb9de83c1d4eb95f83936d5413c695"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: b5288bbe-6783-4516-b628-110c1a29ea05
X-Runtime: 0.593409
Vary: Origin
I'm missing something or have't understood the concept I think. How can I access the "publishable-key: pk_test_fYfWQWtbSsGbb1*****50VrT" present in HTTP response header in react.js front-end.
Thank you all.

You can access $http headers like this:response.headers();
componentDidMount() {
zenApi.get(SERVER)
.then(response => {
var headers = response.headers();
var publishable_key = headers['publishable-key']
this.setState({ pricing_plans: response.data, pub_key: response.headers['publishable-key'] });
});
}

Related

Cors Error When I do a redirect from the server

I have a server running on localhost:8090, which I make a request to from a React App running on localhost:3000 . The aim of this request is to perform some operations and when it is done, it does a redirect to https://www.google.com/ from the backend. This is what it looks like.
Frontend:
function processReq() {
fetch(`http://localhost:8090/some-process`,
{
method: "GET",
headers: {
Accept: "application/json",
}
}
)
.then(response => {
console.log(response);
}).catch(err => console.log(err))
}
Backend
r.GET("/some-process", handlers.DoProcess)
func DoProcess(c *gin.Context) {
// processes request
var w http.ResponseWriter = c.Writer
http.Redirect(w, c.Request, "https://www.google.com", http.StatusSeeOther)
}
All of these works well, but I get a Cors error that looks like this
Access to fetch at 'https://www.google.com/' (redirected from 'http://localhost:8090/some-process') from origin 'http://localhost:3000' 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.
Mind you, I have setup cors on my backend and it looks something like this
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Accept, Origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "*")
if c.Request.Method == "OPTIONS" {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Accept, Origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "*")
c.AbortWithStatus(204)
return
}
c.Next()
}
}```
Your situation is similar to the one described in the answer to Mongoose redirect not waiting for findByIDAndDelete.
Instead of letting the server respond with a redirect, let it respond with 200 OK and have the client execute
location.href = "https://www.google.com";
when it receives this response.
The reason you're seeing cors errors is because google is not returning permissible access control headers.
The more fundamental issue is you're trying to redirect the browser as part of a fetch request which won't work; if google did allow cross origin access, you would just be returning the HTML response in your fetch call which isn't that useful.
Instead, you should just return a 200 in your server response and have the client redirect the browser window to google.
function processReq() {
fetch(`http://localhost:8090/some-process`,
{
method: "GET",
headers: {
Accept: "application/json",
}
}
)
.then(response => {
console.log(response);
window.location.href = 'https://google.com';
}).catch(err => console.log(err))
}

API Gateway - 200 response from Postman, 500 from app

I have an API on AWS API Gateway that takes a POST request and forwards headers and body to a third party API and returns the result. This process works fine when I send the request from Postman, but doesn't work either via cURL or JavaScript.
NB - all URIs, auth tokens etc below are modified so may be inconsistent between screengrabs.
The request in Postman is below
Postman console for this looks like
POST https://dsvdvsdvsdrc.execute-api.eu-west-1.amazonaws.com/Prod/
200
957 ms
Network
Request Headers
Authorization: Basic <myauthtoken>NTAwYTQxNDdmYzcyLWFkZDgtNDZmMy05ZWU0LWQzYWM=
Content-Type: application/x-www-form-urlencoded
User-Agent: PostmanRuntime/7.26.8
Accept: */*
Cache-Control: no-cache
Postman-Token: 9dab6f01-67bf-4611-8d8e-c3d5fe725067
Host: tsfsfsdrc.execute-api.eu-west-1.amazonaws.com
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 82
Request Body
grant_type: "client_credentials"
scope: "https://api.ebay.com/oauth/api_scope"
In my JavaScript app I have the following code:
var data = qs.stringify({
'grant_type': 'client_credentials',
'scope': 'https://api.ebay.com/oauth/api_scope'
});
var config = {
method: 'post',
url: 'https://fddgdgddrc.execute-api.eu-west-1.amazonaws.com/Prod/',
headers: {
'Authorization': 'Basic sssscyLWFkZDgtNDZmMy05ZWU0LWQzYWM=',
'Content-Type': 'application/x-www-form-urlencoded'
},
data : data
};
console.log("******Here is ebayData REQUEST***** "+ JSON.stringify(config));
axios(config)
.then(function (response) {
console.log("******Here is ebayData***** "+ JSON.stringify(response.data));
})
.catch(function (error) {
console.log( "******Here is ebay Error***** "+ error);
});
However when the application runs I get a 500 response. Below are the request headers and body that is being sent in the request
I've enabled cloudwatch logs on the API and below is an example of a successful request via Postman
and here is an example of an unsuccessful request from the browser
Looking further into the response headers for a failed and a successful response I see the headers with comments against them are different
Failed request
(d360923b-eff2-433f-8f76-a9038547dcdf) Endpoint response headers: {rlogid=t6ldssk67%3D9whhldssk67*qc1qr%28rbpv6710-17dd35648ce-0x129,
x-ebay-c-version=1.0.0,
x-frame-options=SAMEORIGIN,
x-content-type-options=nosniff,
x-xss-protection=1; mode=block,
set-cookie=ebay=%5Esbf%3D%23%5E;Domain=.ebay.com;Path=/; Secure,dp1=bu1p/QEBfX0BAX19AQA**6581b87b^;Domain=.ebay.com;Expires=Tue, 19-Dec-2023 15:36:27 GMT;Path=/; Secure,
content-encoding=gzip,
cache-control=private, <--- doesn't appear in successful response
pragma=no-cache, <--- doesn't appear in successful response
date=Sun, 19 Dec 2021 15:36:26 GMT,
server=ebay-proxy-server,
x-envoy-upstream-service-time=19,
x-ebay-pop-id=UFES2-RNOAZ03-api,
transfer-encoding=chunked}
Successful request
(fe565553-3283-4593-8b07-b4e2d58dd2a6) Endpoint response headers: {rlogid=t6ldssk67%3D9vjdldssk67*5cddm%28rbpv6775-17dd23fa53c-0x124,
x-ebay-c-version=1.0.0,
x-ebay-client-tls-version=TLSv1.2,<--- doesn't appear in failed response
x-frame-options=SAMEORIGIN,
x-content-type-options=nosniff,
x-xss-protection=1; mode=block,
set-cookie=ebay=%5Esbf%3D%23%5E;Domain=.ebay.com;Path=/; Secure,dp1=bu1p/QEBfX0BAX19AQA**65817126^;Domain=.ebay.com;Expires=Tue, 19-Dec-2023 10:32:06 GMT;Path=/; Secure,
content-encoding=gzip,
content-type=application/json,<--- doesn't appear in failed response
date=Sun, 19 Dec 2021 10:32:06 GMT,
server=ebay-proxy-server,
x-envoy-upstream-service-time=96,
x-ebay-pop-id=UFES2-SLCAZ01-api,
transfer-encoding=chunked}
I think I've been looking at this for too long and am probably missing something obvious, but headers and body etc all seem to be consistent across the app and Postman, so I'm confused why the request from one is successful and the other is failing. Any advice much appreciated.
Add all other headers as in Postman , some application rejects requests without proper user-agent header or some other required headers.

CORS: Response to preflight request doesn't pass access control check ( axios )

I'm building a SPA with Reactjs and laravel as API. I use axios to make API calls, some requests work without any issue, however on some pages when I make a request i'll receive a error that the the request is blocked by the CORS policy.
In laravel i'm using the spatie/laravel-cors package to add CORS headers so the preflight requests won't get blocked, by default it will allow any type of request from any origin.
Request:
componentDidMount() {
const url = BACKEND_URL+API+'/XXX/';
let headers = {
"Authorization": "Bearer " + token,
"Content-Type": "application/json",
"Accept": "application/json",
}
axios({
url: url,
method: "GET",
headers: headers,
credentials: 'same-origin',
})
.then(response => {
const data = response.data.data;
this.setState({
data: data,
loading: false
})
})
.catch(error => {
console.log(error);
});
}
Expected response ( from localhost )
{
campaign_id: XXX
category: []
form_entry: {id: XXX, form_id: XXX, lead_id: XXX, fields: Array(1)}
form_entry_id: XXX
id: XXX
landing__page_id: XXX
last_contacted: "XXX"
name: "XXX"
notes: XXX
status: [{…}]
tag: []
}
Error message:
Access to XMLHttpRequest at 'XXX.XXX' from origin 'XXX.XXX' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
from localhost everything works fine with the same setup, the strange thing is that i'm only getting the errors once I put both front and backend online.
Anyone has a clue why this is happening?

How to upload file to amazon s3 bucket using axios?

first of all, I am very very new to react. I am trying to upload a file to my amazon S3 bucket using axios.
I get the signature url correctly but I don't know how to continue.. I tried this but it doesn't work.
The error I get is the following:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://xxx.s3.amazonaws.com/wolves.jpeg?AWSAccessKeyId=xxxxxxxxxx&Content-Type=image%2Fjpeg&Expires=1502773987&Signature=ZXQya8d3xZoikzX6dIbdL3Bvb8A%3D. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
import React, { Component } from 'react'
import Dropzone from 'react-dropzone'
import aws from 'aws-sdk'
import axios from 'axios'
export default class ImageAWS extends Component{
uploadFile(files){
console.log('uploadFile: ')
const file = files[0]
aws.config.update({
accessKeyId: 'xxxxxxxxx',
secretAccessKey: 'xxxxxxxx'
});
var s3 = new aws.S3();
var params = {
Bucket: 'xxx',
Key: file.name,
Expires: 60,
ContentType: file.type
};
s3.getSignedUrl('putObject', params, function(err, signedUrl) {
if (err) {
console.log(err);
return err;
} else {
console.log(signedUrl);
var instance = axios.create();
instance.put(signedUrl, file, {headers: {'Content-Type': file.type}})
.then(function (result) {
console.log(result);
})
.catch(function (err) {
console.log(err);
});
return signedUrl;
}
});
}
render () {
return (
<div>
Images Component
<Dropzone onDrop={this.uploadFile.bind(this)} />
</div>
)
}
}
OPTIONS https://xxbucket.s3.amazonaws.com/wolves.jpeg?
AWSAccessKeyId=xxxxxxxxxxxxxxxxxx&Content-Type=image%2Fjpeg&Expires=1502894764&Signature=FqAccUimhyrLgLBldVy%2Fkyx2Xmc%3D HTTP/1.1
Host: xxbucket.s3.amazonaws.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101
Firefox/54.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: access-control-allow-headers,access-control-allow-methods,access-control-allow-origin
Origin: http://localhost:8080
Connection: keep-alive
HTTP/1.1 403 Forbidden
x-amz-request-id: 4282B87BA935EF9A
x-amz-id-2: jWOwOQ/7BCzvw1xPmJroOzUBhbCmpfGx5HCPaPUvMoYTFMrlhoG5wN902B1brZ5cjYnKHMLWmpQ=
Content-Type: application/xml
Date: Wed, 16 Aug 2017 14:45:05 GMT
Server: AmazonS3
Content-Length: 514
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessForbidden</Code><Message>CORSResponse: This CORS request is not allowed. This is usually because the evalution of Origin, request method / Access-Control-Request-Method or Access-Control-Request-Headers are not whitelisted by the resource's CORS spec.</Message><Method>PUT</Method><ResourceType>OBJECT</ResourceType><RequestId>4282B87BA935EF9A</RequestId><HostId>jWOwOQ/7BCzvw1xPmJroOzUBhbCmpfGx5HCPaPUvMoYTFMrlhoG5wN902B1brZ5cjYnKHMLWmpQ=</HostId></Error>
You'll need to add the required CORS headers to your dev server.
Read about CORS. Depending on your development server, search for the proper way to add response headers. Then, add the following headers to every response:
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
This will enable to request data from other websites on the web. Make sure you add them to the development version only(I assume you are learning, so that's OK). On production, you'll need to limit the "Access-Control-Allow-Origin": "*", to specific urls.
Further reading:
webpack dev server CORS issue
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
For those who are facing this issue while uploading files from front end,
Go to AWS S3 -> select your bucket -> permission tab -> scroll to last
Under section Cross-origin resource sharing (CORS), edit and drop your configuration
[
{
"AllowedHeaders": [
"Authorization",
"Content-Range",
"Accept",
"Content-Type",
"Origin",
"Range",
"Access-Control-Allow-Origin"
],
"AllowedMethods": [
"GET",
"PUT"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": [
"Content-Range",
"Content-Length",
"ETag"
],
"MaxAgeSeconds": 3000
}
]
You can edit this as per your requirements.
Finally, Publish/Save it.
This will resolve your CORS issue.

ASP.NET WebApi Answer 400 Bad Request to OPTIONS on ValidateClientAuthentication, even on context.Validated()

I have an angularjs HTML client to a WebApi project. When I test the APIs via POSTMAN or other REST Client, it seems that everything is ok.
When I start using browsers with my angularjs client, browsers always start preflight requests with OPTIONS. There, my WebAPI always answers 400 Bad Request - I am still on a "/api/token" phase.
I have already attached every single point of my WebAPI project to the debugger. I have also changed several points according to several answers here in SO on how to enable CORS. Some of them I have already tried: changing web.config to add headers enabling cors on every request, adding cors to WebApi startup, enabling cors at "/token" overridden functions.
Here is what I got so for:
Angularjs TypeScript call to "/api/token":
logIn = (userName: string, userPassword: string): ng.IPromise<void> => {
var postData = {
"grant_type": "password",
"client_id": this.appConfiguration.ClientId,
"client_secret": this.appConfiguration.ClientSecret,
"username": userName,
"password": userPassword
};
return this.$http.post<models.LoggedUserModel>('http://local.web.api/api/token', $.param(postData), {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then((result) => {
this.localStorageService.set('Auth', result);
this.goHome(true);
}).catch((error) => {
console.warn(error);
});
}
Here is the only function that is called on my WebApi:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// handle cors requests
if (!string.IsNullOrEmpty(context.OwinContext.Request.Headers.Get("Origin")))
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new string[] { "*" });
}
try
{
// retrieve clientId and clientSecret from request body
string clientId;
string clientSecret;
if (context.TryGetFormCredentials(out clientId, out clientSecret))
{
// here it comes our application specific security code....
}
else
{
// this is part of enabling CORS..
if (context.Request.Method.ToUpper() == "OPTIONS")
{
// it returns OK to preflight requests having an empty body
context.Validated();
}
}
}
finally
{
// log stuff...
}
}
If I just left OWIN Cors stuff, adding headers and calling ´context.Validated()´ it all continues the same. Here is what I get:
Firefox Network Tab:
--------------------
Request URL: http://local.web.api/api/token
Request method: OPTIONS
Remote address: 127.0.0.1:80
Status code: 400 Bad Request
Version: HTTP/1.1
Request headers:
----------------
Host: local.web.api
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.7,pt-BR;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization
Origin: http://local.web.client
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Response headers:
-----------------
Access-Control-Allow-Origin: *
Cache-Control: no-cache
Content-Length: 34
Content-Type: application/json;charset=UTF-8
Date: Tue, 22 Dec 2015 15:24:23 GMT
Expires: -1
Pragma: no-cache
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
I would really appreciate some ideas of where to got.
This is brand new to me, and I do operate some other WebApi projects + angularjs.
Ok, that's is terrible but I found the issue.
I am using a http interceptor on angularjs that would automatically check for a logged user and add an Authorization header with the Bearer token when needed. Problem is I was doing it wrongly.
I created a new property in the config object, bypassToken as boolean, that would be the flag to add or not the Authorization header. Removing this actually fixed the code. Not sure why, but analyzing the request now I can see that all headers are actually sending as expected: with the Content-Type which was not being filled on the first case correctly. Weird though no warning was thrown by angularjs.
// http auth interceptor
angularApplication.factory('httpAuthInterceptor', ['$rootScope', '$injector', '$location', ($rootScope, $injector, $location): ng.IHttpInterceptor => {
var $q: ng.IQService = $injector.get('$q');
var localStorageService: ng.local.storage.ILocalStorageService = $injector.get('localStorageService');
return {
request: (config: ng.IRequestConfig): ng.IRequestConfig => {
// check if headers are present
config.headers = config.headers || {};
// the error was here! I was trying to add properties to config that I think angular was not expecting
// removing this line solved the issue
// if (!config.bypassToken) {
// check if user is logged in
var loggedUserInfo = localStorageService.get<models.LoggedUserInfoModel>('Auth');
if (loggedUserInfo) {
config.headers['Authorization'] = 'Bearer ' + loggedUserInfo.access_token;
}
return config;
},
responseError: (rejection) => {
// check if user is logged in
var loggedUserInfo = localStorageService.get<models.LoggedUserInfoModel>('Auth');
if ((rejection.status === 401) && (loggedUserInfo)) {
// if so, then the user must login againd
localStorageService.remove('Auth');
$location.path('/home');
console.error(rejection);
}
return $q.reject(rejection);
}
};
}]);
I appreciate your help.
I am only posting this here in case someone faces a similar issue.
Don't mess with the config object!

Resources