Skip to content

Instantly share code, notes, and snippets.

@crrobinson14
Created October 30, 2018 03:25
Show Gist options
  • Save crrobinson14/28356e52e51424915c268845c2eb518e to your computer and use it in GitHub Desktop.
Save crrobinson14/28356e52e51424915c268845c2eb518e to your computer and use it in GitHub Desktop.
ActionHero v19+ CORS middleware
const { Initializer, api } = require('actionhero');
// Adjust to suit... Or optionally move into config...
const allowedOrigins = [
'https://staging.mydomain.com',
'https://www.mydomain.com',
'https://mydomain.com',
'http://localhost:8080',
];
const fallbackOrigin = 'https://www.mydomain.com';
const setHeaders = (connection, origin) => {
const { responseHeaders } = connection.rawConnection;
const setOrigin = allowedOrigins.indexOf(origin) !== -1 ? origin : fallbackOrigin;
responseHeaders.push(['Access-Control-Allow-Methods', 'HEAD, GET, POST, PUT, DELETE, OPTIONS']);
responseHeaders.push(['Access-Control-Allow-Origin', setOrigin]);
};
const processCORS = data => {
const { action, connection } = data;
if (action === 'getSystemStatus') {
return;
}
const rawConnection = connection.rawConnection || {};
const headers = (rawConnection.req || {}).headers || {};
setHeaders(connection, headers.origin);
};
module.exports = class CORS extends Initializer {
constructor() {
super();
this.name = 'CORS';
this.loadPriority = 1000;
this.startPriority = 1000;
this.stopPriority = 1000;
}
async start() {
// Check incoming requests for authentication requirements
// NOTE: Doesn't work because OPTIONS requests don't get processed through middleware. Left here as
// documentation for that fact.
api.actions.addMiddleware({
name: 'Request Processing : CORS',
global: true,
priority: 50,
preProcessor: processCORS,
});
const webServer = api.servers.servers.web;
webServer.respondToOptions = connection => {
const { origin } = connection.rawConnection.req.headers;
setHeaders(connection, origin);
webServer.sendMessage(connection, '');
};
}
};
@xadamxk
Copy link

xadamxk commented Nov 23, 2021

@crrobinson14 Where do you put this file so it get's loaded? First time using a middleware for action hero and this is very much appreciated!

@crrobinson14
Copy link
Author

crrobinson14 commented Nov 23, 2021

@xadamxk Good question! This part of the documentation is a bit buried so it's really easy to miss. If you look at https://www.actionherojs.com/tutorials/initializers, it says To use a custom initializer, create a initializers directory in your project. Export a class that extends actionhero.Initializer and implements at least one of start, stop or initialize and specify your priorities. What happens is that Actionhero actually auto-discovers initializers as long as you put them in the right directory, just like it does with actions and tasks.

Please bear in mind the above Gist is several years old. You may need to make some tweaks to use it on the latest ActionHero.

The important thing about CORS is that a lot of the online documentation is out of date. You can hard-code or wildcard Access-Control-Allow-Methods and some of the other headers (like Access-Control-Allow-Headers). But you cannot wildcard or hard-code Access-Control-Allow-Origin. You must reply to the caller with the exact Origin they provided in the OPTIONS request (after checking to be sure you want to allow it). That's why there is a function doing this.

@xadamxk
Copy link

xadamxk commented Nov 23, 2021

Much appreciated Chad! Looks like the only changes needed to get the initializer running are:

const { Initializer, api } = require('actionhero'); -> import { Initializer, api, action } from 'actionhero';
---
api.actions.addMiddleware({ -> action.addMiddleware({

However, it still doesn't appear to be replying back with the set allowed origin (that, or Chrome just doesn't allow it with localhost).
Any more ideas? CORS is becoming public enemy #1.

Access to XMLHttpRequest at 'localhost:8080/api/endpoint' from origin 'http://localhost:3000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

@crrobinson14
Copy link
Author

crrobinson14 commented Nov 23, 2021

I think your issue is there in your error. Don't access localhost:8080/api/endpoint. It needs to be http://localhost:8080/api/endpoint. CORS only applies with certain URI "schemes" so you need "http" at the front of it.

@xadamxk
Copy link

xadamxk commented Nov 23, 2021

I tried that first and tried removing the protocol, still no luck.

Access to XMLHttpRequest at 'http://localhost:8080/api/endpoint' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:8080' that is not equal to the supplied origin.

@xadamxk
Copy link

xadamxk commented Nov 29, 2021

I never got this working, but I also wanted to share this error I ran into incase anyone else does:
actionhero/actionhero#2032

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment