axios POST method seems to be 'transformed' to OPTIONS method on the fly - json

I use this sample code:
axios({
method: 'post',
headers: { 'content-type': 'application/json' },
url: 'http://somePlace:8040/someWSendpoint',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
But that request never reach its destination. Apparently, because the POST method is transformed to OPTIONS method, and rejected by the endpoint url.
This is what the 'Network' window shows in the Chrome inspector:
Request Method: OPTIONS
Status Code: 405 Method Not Allowed
This is what the 'Console' window shows in the Chrome inspector:
Access to XMLHttpRequest at 'http://somePlace:8040/someWSendpoint' 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.
xhr.js?b50d:160 POST http://somePlace:8040/someWSendpoint net::ERR_FAILED
dispatchXhrRequest # xhr.js?b50d:160
xhrAdapter # xhr.js?b50d:11
dispatchRequest # dispatchRequest.js?5270:59
Promise.then (async)
request # Axios.js?0a06:51
wrap # bind.js?1d2b:9
send_firma # AltaFirma.vue?c240:504
click # AltaFirma.vue?3ed8:1812
invokeWithErrorHandling # vue.runtime.esm.js?2b0e:1854
invoker # vue.runtime.esm.js?2b0e:2179
original._wrapper # vue.runtime.esm.js?2b0e:6917
createError.js?2d83:16 Uncaught (in promise) Error: Network Error
at createError (createError.js?2d83:16)
at XMLHttpRequest.handleError (xhr.js?b50d:69)
I am wondering if people "at the other side" need to configure something related to that CORS thing in his server ¿?
Any help will be very appreciated.

Related

How to secure a Google Cloud Function with API Gateway and CORS?

I created an API Gateway which uses the x-google-backend to a cloud functions.
When I tried to access it via browser I received a CORS error so I researched and find a solution by adding this to the OpenAPI config where the address part is the same as the cloud function.
options:
operationId: cors
x-google-backend:
address: https://europe-west3-myproject.cloudfunctions.net/api/query
responses:
'200':
description: A successful response
This works! So I removed the public access to the cloud function and gave the gateway service account access to it and tried again.
Which gave me a permission error. After research I found this post explaining the problem and giving me a solution to fix it.
The issue was that I call my define the cloud function with an additional path to call query. I added this to the OpenAPI config:
jwt_audience: https://europe-west3-myproject.cloudfunctions.net/api
So I tried it again in Postman and it works, however in the browser I now get again a CORS error.
So now I am at square one... what should I do?
Here is my complete OpenAPI config:
# openapi2-functions.yaml
swagger: '2.0'
info:
version: 1.0.0
schemes:
- https
produces:
- application/json
paths:
/query:
post:
operationId: api
parameters:
- in: "body"
name: "message"
schema:
$ref: '#/definitions/messasge'
x-google-backend:
address: https://europe-west3-myproject.cloudfunctions.net/api/query
jwt_audience: https://europe-west3-myproject.cloudfunctions.net/api
x-google-quota:
metricCosts:
"read-requests": 1
security:
- api_key: []
responses:
'200':
description: A successful response
schema:
type: string
options:
operationId: cors
x-google-backend:
address: https://europe-west3-myproject.cloudfunctions.net/api/query
responses:
'200':
description: A successful response
securityDefinitions:
# This section configures basic authentication with an API key.
api_key:
type: "apiKey"
name: "key"
in: "query"
x-google-management:
metrics:
# Define a metric for read requests.
- name: "read-requests"
displayName: "Read requests"
valueType: INT64
metricKind: DELTA
quota:
limits:
# Define the limit or the read-requests metric.
- name: "read-limit"
metric: "read-requests"
unit: "1/min/{project}"
values:
STANDARD: 100
definitions:
chatmessage:
type: "object"
properties:
id:
type: string
description: session id
example: "2vr34524tg3"
query:
type: string
description: message
example: "Hello"
required:
- id
- query
According to the documentation Cross-Origin Resource Sharing (CORS) on Cloud Functions has some limitations:
CORS preflight requests are sent without an Authorization header, so they will be rejected on all non-public HTTP Functions. Because the preflight requests fail, the main request will also fail.
To overcome this limitation in your case the mentioned documentation recommends to deploy a Cloud Endpoints proxy and enable CORS. Also you might find useful the Support CORS documentation page for a description of available CORS support options

Can't create webhook through v2 api endpoint

I am trying to dispatch webhook create endpoint and getting 403 Forbidden response code with no error information in body. My request looks like:
POST /v2/apps/618ce23498488b00e1582723/integrations/618ce235a6ccf400e1a4b992/webhooks HTTP/1.1
Host: api.smooch.io
Content-Type: application/json
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImFwcF82MThjZTIzNGZiYTk5MDAwZTFiZDgyYWMifQ.eyJzY29wZSI6ImFwcCJ9.hXigeREGoUQ8YtM_gPUXfgWEGumbnlbBNLk_*******
Content-Length: 120
{
"target": "https://example.com/inbound-smooch-request",
"triggers": [
"conversation:message"
]
}
Response I am getting:
{"errors":[{"code":"forbidden","title":"Forbidden"}]}
That's how I generate Bearer token:
$signer = new Sha256();
$a = (new Builder())
->withHeader('alg', 'HS256')
->withHeader('typ', 'JWT')
->withHeader('kid', 'app_618ce234fba99000e1bd8***')
->withClaim('scope', 'app')
->getToken($signer, new Key('4K5KoVqLixLOUrkJSebr4rFsvZAu66d2WD8WEhXDgAZnJaltEpnHWpF_PUwQUGuNlFnBXvr8mtGsvNKj******'));
echo $a . PHP_EOL;
die();
Webhook api documentation: https://docs.smooch.io/rest/#operation/createWebhook
There is a bearerAuth (integration, app) explanation but there is no information what does this means. Documentation says that there are only two scopes: account and app. So what is this?
Other endpoints work normally (app create, app keys create, integration create)
Tried Basic auth also but it did not help.
The *Webhook endpoints are used (in v2) to create/update/remove webhooks from an existing customIntegration (integration with type: custom).
This endpoint will allow you to create a new integration, and when you set type: custom, it will also accept a webhooks list: https://docs.smooch.io/rest/#operation/createIntegration

I am trying to call a DAG( wrtitten in Python) using Cloud Function(Python 3.7) and getting error "405 Method Not Found" Could someone help here?

I have used below code available on the Google Cloud Docs platform:
from google.auth.transport.requests import Request
from google.oauth2 import id_token
import requests
IAM_SCOPE = 'https://www.googleapis.com/auth/iam'
OAUTH_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
def trigger_dag(data, context=None):
"""Makes a POST request to the Composer DAG Trigger API
When called via Google Cloud Functions (GCF),
data and context are Background function parameters.
For more info, refer to
https://cloud.google.com/functions/docs/writing/background#functions_background_parameters-python
To call this function from a Python script, omit the ``context`` argument
and pass in a non-null value for the ``data`` argument.
"""
# Fill in with your Composer info here
# Navigate to your webserver's login page and get this from the URL
# Or use the script found at
# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/composer/rest/get_client_id.py
client_id = '87431184677-jitlhi9o0u9sin3uvdebqrvqokl538aj.apps.googleusercontent.com'
# This should be part of your webserver's URL:
# {tenant-project-id}.appspot.com
webserver_id = 'b368a47a354ddf2f6p-tp'
# The name of the DAG you wish to trigger
dag_name = 'composer_sample_trigger_response_dag'
webserver_url = (
#'https://'
webserver_id
+ '.appspot.com/admin/airflow/tree?dag_id='
+ dag_name
#+ '/dag_runs'
)
# Make a POST request to IAP which then Triggers the DAG
make_iap_request(
webserver_url, client_id, method='POST', json={"conf": data, "replace_microseconds": 'false'})
# This code is copied from
# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/iap/make_iap_request.py
# START COPIED IAP CODE
def make_iap_request(url, client_id, method='GET', **kwargs):
"""Makes a request to an application protected by Identity-Aware Proxy.
Args:
url: The Identity-Aware Proxy-protected URL to fetch.
client_id: The client ID used by Identity-Aware Proxy.
method: The request method to use
('GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE')
**kwargs: Any of the parameters defined for the request function:
https://github.com/requests/requests/blob/master/requests/api.py
If no timeout is provided, it is set to 90 by default.
Returns:
The page body, or raises an exception if the page couldn't be retrieved.
"""
# Set the default timeout, if missing
if 'timeout' not in kwargs:
kwargs['timeout'] = 90
# Obtain an OpenID Connect (OIDC) token from metadata server or using service
# account.
google_open_id_connect_token = id_token.fetch_id_token(Request(), client_id)
# Fetch the Identity-Aware Proxy-protected URL, including an
# Authorization header containing "Bearer " followed by a
# Google-issued OpenID Connect token for the service account.
resp = requests.request(
method, url,
headers={'Authorization': 'Bearer {}'.format(
google_open_id_connect_token)}, **kwargs)
if resp.status_code == 403:
raise Exception('Service account does not have permission to '
'access the IAP-protected application.')
elif resp.status_code != 200:
raise Exception(
'Bad response from application: {!r} / {!r} / {!r}'.format(
resp.status_code, resp.headers, resp.text))
else:
return resp.text
# END COPIED IAP CODE
You encounter the error "405 method not found" because you are trying to send a request to your_webserver_id.appspot.com/admin/airflow/tree?dag_id=composer_sample_trigger_response_dag which is the URL seen "Tree view" in the airflow webserver.
To properly send request to the Airflow API you need to construct the webserver_url just like in the documentation in Trigger DAGs in Cloud Functions. The webserver_url was constructed to use Trigger a new DAG endpoint to send requests. So if you'd like to trigger the DAG you can stick with the code below.
Airflow run DAG endpoint:
POST https://airflow.apache.org/api/v1/dags/{dag_id}/dagRuns
webserver_url = (
'https://'
+ webserver_id
+ '.appspot.com/api/experimental/dags/'
+ dag_name
+ '/dag_runs'
)
Moving forward, if you would like to perform different operations using the Airflow API you can check the Airflow REST reference

Receiving 408 request timeout when we try to put an object to a Forge bucket

We are trying to upload a 'bigger' model to a forge bucket via the model derivative API.
This model has a file size of 956MB and the extension .nwd
const uploadPromise = http(uploadUrl(socketId), {
method: 'PUT',
data: Body,
headers: {
'Content-Type': 'application/octet-stream',
Authorization: `Bearer ${Forge.urns[socketId].autoDeskToken}`,
},
maxContentLength: 1024 * 1024 * 1024,
timeout: 600000,
})
{ Error: Request failed with status code 408
at createError (/nodejs/node_modules/axios/lib/core/createError.js:16:15)
at settle (/nodejs/node_modules/axios/lib/core/settle.js:18:12)
at IncomingMessage.handleStreamEnd (/nodejs/node_modules/axios/lib/adapters/http.js:202:11)
at IncomingMessage.emit (events.js:203:15)
at IncomingMessage.EventEmitter.emit (domain.js:448:20)
at endReadableNT (_stream_readable.js:1145:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
The url seems to be correct:
https://developer.api.autodesk.com/oss/v2/buckets/-BUCKETNAME-/objects/-filename-.nwd
What could be the reason of this error?
This is a problem a customer is experiencing.
kind regards,
Thomas
That's interesting, the error code 408 is not listed in the documentation of the endpoint: https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectName-PUT. The error code is most likely coming from some middle-layer (Apigee), and not from the Forge servers directly.
However, to upload a file this big, I'd strongly suggest using the "resumable upload" feature that's available in the Data Management service. You can find an example of how the resumable upload works in my TypeScript code here: https://github.com/petrbroz/vscode-forge-tools/blob/develop/src/commands/data-management.ts#L230-L272.

Sinatra access-control-allow-origin for sinatra public folder

How do I set up Sinatra so that static files in the public folder
are returned with the response Access-Control-Allow-Origin = "*" ?
Have a look at this question here: Sinatra OPTIONS HTTP Verb. It's implemented in sinatra now so you don't have to hack around it.
If that doesn't help take a look at this blog post: Cross Origin Resource Sharing with Sinatra, and its repo at github: sinatra-corss_origin
Although the simplest way to do it should work just by adding this:
response['Access-Control-Allow-Origin'] = 'http://whatever.org'
before the return value in your route.
get '/foo' do
headers 'Access-Control-Allow-Origin' => 'http://example.com'
'hello world'
end
There's also a nice extension for cross origin sharing:
https://github.com/britg/sinatra-cross_origin
require 'sinatra'
require 'sinatra/cross_origin'
# To enable cross origin requests for all routes:
configure do
enable :cross_origin
end
# To only enable cross origin requests for certain routes:
get '/cross_origin' do
cross_origin
"This is available to cross-origin javascripts"
end
I did this on a server side, my file was called server.rb:
before do
content_type :json
headers 'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => ['OPTIONS', 'GET', 'POST']
end
This setup works for me:
Gemfile:
# Gemfile
gem 'sinatra'
gem 'sinatra-cross_origin'
Sinatra App:
# app.rb
require 'sinatra'
require 'sinatra/cross_origin'
class MyApp < Sinatra::Base
set :bind, '0.0.0.0'
configure do
#This is enable cross on the server
enable :cross_origin
end
#This before blocks gets invoked on every request and
#the (*) mark tells your server that share the resource with anyone,
#if you want to share it with specific domain you can mention the domain/s
#by removing the asterisk sign.
before do
response.headers['Access-Control-Allow-Origin'] = '*'
end
# routes...
options "*" do
response.headers["Allow"] = "GET, PUT, POST, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "Authorization,
Content-Type, Accept, X-User-Email, X-Auth-Token"
response.headers["Access-Control-Allow-Origin"] = "*"
200
end
end
The options block described above sends a 200 response to the preflight request sent by the browser. Then the browser makes the CORS request. In response to this request, the server sends Access-Control-Allow-Origin = * in response headers.
If we want only a specific domain to access the resources:
before do
response.headers['Access-Control-Allow-Origin'] = 'http://example.com'
end
this solution works for me and is based on an answer on a similar question How to add "Access-Control-Allow-Origin" headers to API Response in Ruby
get '/' do
response['Access-Control-Allow-Origin'] = '*'
"asdf" # return "asdf"
end