Office add-in: XMLHttpRequest cannot load XXX due to access control checks - xmlhttprequest

I'm building an Outlook add-in with jQuery and the Office JS API. I have a local server going while developing, and I'm trying to submit a POST request to an endpoint on my site's main server. Every time I try to submit the request, I get the following three errors:
Origin https://localhost:3000 is not allowed by Access-Control-Allow-Origin
XMLHttpRequest cannot load https://myurl.com/my_endpoint due to access control checks
Failed to load resource: Origin https://localhost:3000 is not allowed by Access-Control-Allow-Origin
What I've done so far:
Found this related thread: HTTP fetch from within Outlook add-ins
The only answer says to do three things:
Make the request with XMLHttpRequest. Yup, did that:
function submitForm(var1, var2) {
var http = new XMLHttpRequest();
var params = 'var1=' + encodeURIComponent(var1) + '&var2=' + encodeURIComponent(var2);
http.open("POST", 'https://myurl.com/my_endpoint', true);
http.setRequestHeader('Access-Control-Allow-Origin', 'https://localhost:3000');
http.setRequestHeader('Access-Control-Allow-Credentials', true);
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
http.onreadystatechange = function() {
console.log("response:", http.responseText);
console.log("status:", http.status);
};
http.send(params);
}
Add the service URL into the manifest's AppDomains list. Yup, did that, too. This is from my manifest.xml:
<AppDomains>
<AppDomain>https://myurl.com</AppDomain>
<AppDomain>https://myurl.com/my_endpoint</AppDomain>
<AppDomain>https://localhost:3000</AppDomain>
</AppDomains>
Use only services which are under SSL connection. Yup, the myurl.com server is only accessible via SSL.
I also found this documentation (https://learn.microsoft.com/en-us/office/dev/add-ins/develop/addressing-same-origin-policy-limitations) that recommends to solve this with cross-origin-resource-sharing (CORS), and points to this link: https://www.html5rocks.com/en/tutorials/file/xhr2/#toc-cors
So, I checked the server set-up for https://myurl.com and I am in fact allowing requests from any origin. UPDATE 1: as an example, here's what the output of a successful network request to https://myurl.com/my_endpoint looks like (notice the Accept: */* header):
Request URL: https://myurl.com/my_endpoint
Request Method: POST
Status Code: 200 OK
Referrer Policy: no-referrer-when-downgrade
Cache-Control: no-cache, no-store, must-revalidate, public, max-age=0
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Expires: 0
Pragma: no-cache
Server: nginx/1.10.3 (Ubuntu)
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 52
Content-type: application/x-www-form-urlencoded
Host: myurl.com
Origin: chrome-extension://focmnenmjhckllnenffcchbjdfpkbpie
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
var1: var1
var2: var2
Plus, another thing leading me to believe the problem isn't with https://myurl.com is: when I open my network tab in my debugger, I can see that my request never reaches https://myurl.com. I'm also not seeing the request pings in my https://myurl.com server logs. This is the output of my network request when I try to ping https://myurl.com from the Outlook add-in:
Summary
URL: https://myurl.com/my_endpoint
Status: —
Source: —
Request
Access-Control-Allow-Origin: https://localhost:3000
Access-Control-Allow-Credentials: true
Content-Type: application/x-www-form-urlencoded
Origin: https://localhost:3000
Accept: */*
Referer: https://localhost:3000/index.html?_host_Info=Outlook$Mac$16.02$en-US
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) AppleWebKit/605.1.15 (KHTML, like Gecko)
Response
No response headers
Request Data
MIME Type: application/x-www-form-urlencoded
var1: var1
var2: var2
Any recommendations for what else I need to change to enable making a POST request to myurl.com? Thanks in advance to the kind soul that helps me figure this out.
UPDATE 2: For what it's worth, I haven't done any configs to my node server beyond what came out-of-the box when I ran npm install -g generator-office. E.g. I haven't touched these two files:
.babelrc
{
"presets": [
"env"
]
}
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
polyfill: 'babel-polyfill',
app: './src/index.js',
'function-file': './function-file/function-file.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.html$/,
exclude: /node_modules/,
use: 'html-loader'
},
{
test: /\.(png|jpg|jpeg|gif)$/,
use: 'file-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
chunks: ['polyfill', 'app']
}),
new HtmlWebpackPlugin({
template: './function-file/function-file.html',
filename: 'function-file/function-file.html',
chunks: ['function-file']
})
]
};

Failed to load resource: Origin https://localhost:3000 is not allowed by Access-Control-Allow-Origin
The server responds to your pre-flight request (usually OPTIONS) and does not allow to get a response, that's because your origin localhost:3000 is not allowed on server side.
You need to respond to OPTIONS on server with 204 status code and a header like:
Access-Control-Allow-Origin 'localhost';

Related

CORS with Flask, axios and https not working (response header sends origin as http instead of https)

My frontend (Expo Go web) is running at http://localhost:19006/ but when it receives a response from the backend, it somehow believes it runs under https://localhost:19006/
Also, the iOS version of Expo Go logs the following error:
LOG [AxiosError: Network Error]
I'm using Flask in the backend with CORS set as follows:
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['CORS_HEADERS'] = 'Content-Type'
app.config['SECRET_KEY'] = os.environ.get("SECRET_KEY", 'xxx')
cors = CORS(app, resources={r"/*": {"origins": "*", "allow_headers": "*", "expose_headers": "*", "Access-Control-Allow-Origin": "*"}})
and a simple return function:
#app.route("/matches", methods=["GET"])
def getMatches():
print('request for matches')
response = matches.getMatches()
return response
if __name__ == '__main__':
app.run(ssl_context=('certs/cert.pem', 'certs/key.pem'))
My frontend part is using react native with Expo Go. The query to the backend is done this way:
export default function App() {
const axiosApiCall = () => {
const config = {
headers:{
'origin': 'https://localhost:19006' #<- Here also tried http but no change
}
};
axios
.get("https://127.0.0.1:5000/matches", config)
.then((response) => {
setState({quote : 'yes'});
console.log(response.data);
})
.catch((error) => {
console.log(error);
})
}
The backend works properly fine as I can see in Postman. The result is technically showing up in the response of the web-version of Expo Go, however, it appears that there's an issue with CORS:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://127.0.0.1:5000/matches. (Reason: CORS header ‘Access-Control-Allow-Origin’ does not match ‘https://localhost:19006’).
And here's the response header:
HTTP/1.1 200 OK
Server: Werkzeug/2.2.2 Python/3.9.16
Date: Thu, 05 Jan 2023 10:16:42 GMT
Content-Type: application/json
Content-Length: 274552
Access-Control-Allow-Origin: http://localhost:19006
Access-Control-Expose-Headers: *
Vary: Origin
Connection: close
GET /matches HTTP/1.1
Host: 127.0.0.1:5000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:108.0) Gecko/20100101 Firefox/108.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Origin: http://localhost:19006
DNT: 1
Connection: keep-alive
Referer: http://localhost:19006/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Sec-GPC: 1
Anyone any idea? Thanks!!!
Hardcoding the CORS origins: no change
Changing the query from axios to async fetch calls: no change
Including/modifying the header in the axios config: no change
Other browsers: no change
Deactivating SSL in the backend: caused other problems related to react native

Axios Post return 405 Method Not Allowed on Vue.js

I'm trying to made a POST request on a NET CORE 5 service (hosted on IIS 10) from a Vue.js app with axios.
When I test the service with POSTMAN it's working perfectly but with Axios I'm always getting a 405 from the server.
Analyzing the requests with fiddler are looking very different. IN the axios request the content-type header is missing and the method is OPTIONS instead of POST.
This is the POSTMAN request:
POST https://localhost/apiluxor/api/SignIn HTTP/1.1
Content-Type: application/json
User-Agent: PostmanRuntime/7.28.4
Accept: */*
Postman-Token: acfed43c-731b-437b-a88a-e640e8216032
Host: localhost
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 55
{
"username":"user",
"password":"testpw"
}
And this is the axios request:
OPTIONS https://localhost/apiluxor/api/SignIn HTTP/1.1
Host: localhost
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://172.16.1.110:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Sec-Fetch-Dest: empty
Referer: http://172.16.1.110:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7
In my Vue.js module I've tried to force the settings of the 'content-type' in the main config and in the post request with no results.
import { App } from "vue";
import axios from "axios";
import VueAxios from "vue-axios";
import JwtService from "#/core/services/JwtService";
import { AxiosResponse, AxiosRequestConfig } from "axios";
class ApiService {
public static vueInstance: App;
public static init(app: App<Element>) {
ApiService.vueInstance = app;
ApiService.vueInstance.use(VueAxios, axios);
ApiService.vueInstance.axios.defaults.baseURL =
"https://localhost/apiluxor/api";
ApiService.vueInstance.axios.defaults.headers.post["Content-Type"] =
"application/json";
}
public static setHeader(): void {
ApiService.vueInstance.axios.defaults.headers.common[
"Authorization"
] = `Bearer ${JwtService.getToken()}`;
}
public static post(
resource: string,
params: AxiosRequestConfig
): Promise<AxiosResponse> {
return ApiService.vueInstance.axios.post(resource, params, {
headers: { "content-type": "application/json" },
});
}
export default ApiService;
I'm very new to Vue.js so maybe I'm missing something.
The problem is that axios made a CORS request before the POST, and the NET Core API should be configured to accept CORS request.
I've found an article (here) that saying the problem for this cases is that IIS does not accept CORS request and the CORS module should be installed .
I've tried this change but the result was the I was receving an HTTP/1.1 204 No Content instead of a 403.
In my case the problem was the API service itself.
The CORS should be enabled in the API Service.
In NET CORE 5 for a basic configuration it's enough to add the CORS services in Startup
services.AddCors();
and configure it
app.UseCors(builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
With these changes the service works perfectly for any vue.js requests coming from axios.
To send the data as JSON in the body of the post request, just add a parameter "data" to the axios options. Axios will automatically set the content-tpe to the proper value for you.
// Simple example
axios.post(myUrl, {
data: {
"username":"user",
"password":"testpw"
}
});
This should have the expected outcome :)

Safari doesn't send cookie to Express when requesting image via p5 loadImage()

Background
I set up an Express.js app behind a proxy to let users to login before being directed to a web app. This app is failing to serve up some images in Safari (macOS/iOS) because Safari is not sending the cookie back with requests for images that originate from the loadImage() method in my p5.js code. This does not happen on Chrome (macOS).
When I load the page, the browser requests the resources fine. But requests originating from my application returns a different session, which is not logged in, and gets caught by Express:
// Request for the main page by the browser
Session {
cookie:
{ path: '/',
_expires: 2020-05-04T16:26:00.291Z,
originalMaxAge: 259199998,
httpOnly: true,
secure: true },
loggedin: true }
// Request for image assets by a script in my application
Session {
cookie:
{ path: '/',
_expires: 2020-05-04T16:26:00.618Z,
originalMaxAge: 259200000,
httpOnly: true,
secure: true } }
HTTP Requests from Safari
GET https://mydomain/app/img/svg/Water.svg HTTP/1.1
Host: mydomain
Origin: https://mydomain
Connection: keep-alive
If-None-Match: W/"5e6-171c689d240"
Accept: image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5
If-Modified-Since: Wed, 29 Apr 2020 15:24:13 GMT
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15
Referer: https://mydomain/app/
Accept-Language: en-us
Accept-Encoding: gzip, deflate, br
HTTP/1.1 302 Found
Date: Fri, 01 May 2020 06:50:07 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.3.17
X-Powered-By: Express
Location: /
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 23
Access-Control-Allow-Origin: *
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Found. Redirecting to /
Express app
The app is set up behind an HTTPS proxy, so I set the Express Session object to trust proxy and set security to auto (setting to false doesn't fix the problem):
app.set('trust proxy', 1)
app.use(session({
secret: 'my-secret',
resave: true,
saveUninitialized: true,
cookie: {
secure: 'auto',
maxAge: 259200000
}
}));
When the user signs in, it is sent to /auth to check against the database
app.post('/auth', function (request, response) {
var user = request.body.user;
var password = request.body.password;
if (user && password) {
connection.query('SELECT * FROM accounts WHERE user = ? AND password = ?', [user, password], function (error, results, fields) {
if (results.length > 0) {
request.session.loggedin = true;
// If the user logs in successfully, then register the subdirectory
app.use("/app", express.static(__dirname + '/private/'));
// Then redirect
response.redirect('/app');
} else {
response.send('Incorrect password!');
}
response.end();
if (error) {
console.log(error);
}
});
} else {
response.send('Please enter Username and Password!');
response.end();
}
});
They are redirected to /app when logged in:
app.all('/app/*', function (request, response, next) {
if (request.session.loggedin) {
next(); // allow the next route to run
} else {
// The request for images from my p5 script fails and these request redirect to "/"
response.redirect("/");
}
})
Question
What can I do to ensure Safari pass the session cookie with its request so that Express will return the correct asset?
Edit
Including the function that invokes loadImage(). This is embedded in an ES6 class that loads image assets for particles in a chemical simulation. This class must successfully resolve promises so other higher order classes can set correct properties.
loadParticleImage() {
return new Promise((resolve, reject) => {
loadImage(this.imageUrl, (result) => {
// Resolves the Promise with the result
resolve(result);
}, (reason) => {
console.log(reason);
});
})
}
Edit #2
Including the headers for a successful request directly to the URL of the image asset:
GET https://mydomain/app/img/svg/Water.svg HTTP/1.1
Host: mydomain
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Cookie: connect.sid=s%3A2frOCv41gcVRf1J4t5LlTcWkdZTdc8NT.8pD5eEHo6JBCHcpgqOgszKraD7AakvPsMK7w2bIHlr4
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15
Accept-Language: en-us
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
I suggest using express's static middleware to serve static files. With this, you won't need any session to get images, js, css, etc. Also, it accelerates your application. You need to place
app.use(express.static( ... ))
before the app.use(session( ... )) statement if you want some additional perfomance, because if you do, express won't attepmt to creare session for static files.
The fetch() call in the source code for that loadImage() function is not setting the credentials option that controls whether cookies are included with the request or not, therefore they are not sent with the request.
Do you really need authentication before serving an image? If not, you could rearrange the way you serve images in your server so that they can be served without authentication using express.static() pointed at a directory that contains only resources that can be served without authentication. If they do need to be authenticated, you may have to patch the loadImage() code to use the credentials: include option or load your images a different way.

Issue with accessing Jenkins api with Vue/Axios call

I tried making a get call with axios from my Vue js codebase/ environment to Jenkins API and I'm unable to do so.
I've read every resource that I could but wasn't able to fix this particular problem. I even created a .htaccess file to see if it help but wasn't useful.I ran out of options so I came here for help.
Below are the axios codes that I used within my App.vue file.
axios.get(
*URL to access Jenkins that is currently running on a tomcat server*,
{
headers: {
"jenkins-crumb": "* Some numbers and letters*",
},
auth: {
username: "*obvious username*",
password: "*obvious password*"
},
withCredentials: true,
crossdomain: true
}
)
.then(response => (this.info= response)).catch(error => (console.log(error)));
Console log output:
Access to XMLHttpRequest at 'url' 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.
Network output:
General
Request URL: URL
Request Method: OPTIONS
Status Code: 403
Remote Address: localhost:8080
Referrer Policy: no-referrer-when-downgrade
Request Headers
Provisional headers are shown
Access-Control-Request-Headers: authorization,jenkins-crumb
Access-Control-Request-Method: GET
Origin: http://localhost:8080
Referer: http://localhost:8080/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
Please help!

ember-simple-auth oauth2 authorizer issue

I am trying to set up authorization on an Ember App running on a Node.js server.
I am using the oauth2 Authenticator, which is requesting a token from the server. This is working fine. I am able to provide the app with a token, which it saves in the local-storage.
However, when I make subsequent requests, the authorizer is not adding the token to the header, I have initialized the authorizer using the method described in the documentation (http://ember-simple-auth.simplabs.com/ember-simple-auth-oauth2-api-docs.html):
Ember.Application.initializer({
name: 'authentication',
initialize: function(container, application) {
Ember.SimpleAuth.setup(container, application, {
authorizerFactory: 'authorizer:oauth2-bearer'
});
}
});
var App = Ember.Application.create();
And I have added an init method to the Authorizer, to log a message to the server when it is initialized, so I know that it is being loaded. The only thing is, the authorize method of the authorizer is never called.
It feels like I am missing a fundamental concept of the library.
I have a users route which I have protected using the AuthenticatedRouteMixin like so:
App.UsersRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, {
model: function() {
return this.get('store').find('user');
}
});
Which is fetching the data, fine, and redirects to /login if no token is in the session, but the request headers do not include the token:
GET /users HTTP/1.1
Host: *****
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: application/json, text/javascript, */*; q=0.01
Origin: *****
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36
Referer: *****
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Any help you could give me would be greatly appreciated.
Is your REST API served on a different origin than the app is loaded from maybe? Ember.SimpleAuth does not authorizer cross origin requests by default (see here: https://github.com/simplabs/ember-simple-auth#cross-origin-authorization)