Error when loading WebAssembly file from NestJs backend - nestjs

I'm trying to load a WebAssembly file into the browser. I'm trying to load this library and when I try to do what is described I get an error
const worker = new Worker('http://localhost:3000/webworker-wasm')
VM114:1 Refused to create a worker from 'http://localhost:3000/webworker-wasm' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'worker-src' was not explicitly set, so 'default-src' is used as a fallback.
So I guess it has to do with how I serve that file. I have a NestJS backend, and the code serving that file looks like
#Get('webworker-wasm')
private getWebworkerWasm(req: Request, res: Response) {
fs.readFile('./node_modules/stockfish.js/stockfish.wasm.js', (err: any, data: Buffer) => {
res.writeHead(200, {'Content-Type': 'application/wasm'});
res.status(200).end(data);
});
}
Is there anything in this setup that is incorrect or did I forget something?
UPDATE: I've changed the code a bit and now the error is a bit different
fs.readFile('./node_modules/stockfish.js/stockfish.wasm.js', 'binary', (err: any, data: Buffer) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Request-Method', '*');
res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader('Content-Type', 'application/wasm');
// res.writeHead(200, {'Content-Type': 'application/wasm'});
res.status(200).end(data);
});
gives:
x = new Worker('http://localhost:3000/files/webworker-wasm');
VM48:1 Uncaught DOMException: Failed to construct 'Worker': Script at 'http://localhost:3000/files/webworker-wasm' cannot be accessed from origin 'chrome-search://local-ntp'.
at <anonymous>:1:5
My my case its fine if I can fix this with a chrome setting!

I can’t add a comment, so I post this as an answer.
Please post all the response headers as seen in the browser.
Also, you’re serving a JavaScript file as WebAssembly, application/wasm should be application/javascript.

Related

How to pass data along with redirect using express?

I am attempting to set up SAML Single Sign On with my React application. The backend is an express / passport setup. Front end is Reactjs.
I load the initial Reactjs SPA and it notices I don't have a username set. ... so then it redirects to the SSO page. I enter my information (as needed) and then after success I am to point where the identity provider forwards my client back to the /login/callback route. This route, in turn, is supposed to forward the user back to their original URL (as defined by req.body.RelayState).
The callback route looks like this:
samlRouter.use(session(config.session));
samlRouter.use(passport.initialize());
samlRouter.use(passport.session());
samlRouter.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', req.header('origin'));
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-Width, Content-Type, Accept, Authorization');
res.header('Access-Control-Allow-Credentials', 'true'); //SAML
if (req.method == 'OPTIONS') {
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET');
return res.status(200).json({})
}
next();
})
samlRouter.post('/login/callback',
(req, res, next) => {
passport.authenticate('saml', config.saml.options)(req, res, next)
},
(req, res, next) => {
console.log('req.user.nameID :>> ', req.user?.nameID); // ***THIS IS CORRECT**
req.session.user = req.user;
return res.redirect(`${req.body.RelayState}`);
});
The problem is - I need to tell the front-end reactjs application who the req.user.nameID is. I can't just put it in a query string on the redirect because it's the login (and thus anyone that wanted to just type in xxxx.com/privatePage?myusername could pretend to be me).
How can I securely pass data to my front end after authentication?
There are a number of ways to pass data with a redirect.
Put data in a query string that is part of the redirect URL so that data will be available in the URL when the redirect URL is rendered.
Put data into a cookie that will be present when the redirected URL is rendered.
Put data into a server-side user session that will be present when the redirected URL is rendered.
Both options 2 and 3 have potential concurrency issues or race conditions if the same user is very involved in multiple redirects at the same time. These options 2 and 3 also need to clean up the data after it is used so it isn't left hanging around.
Options 2 and 3 are more private than option 1 if that is desired. If this data is involved in authentication (e.g. who the user is and needs to be secure), then it really ought to be in the server-side session (which is indexed by the session cookie). This would typically be saved in the session so it's available as req.session.user to future requests.
One possible solution depending on the type and quantity of data you want to pass:
Make another get route e.g.
router.get('samepage/:some param',
(req,res)=>{
///get data with param or use param as the data !
res.render('samepage',{data})
})
////redirect to that route with param you need

Custom value in http response with express (not in body)

Short story:
I wish to add a custom value to each http response with express using a middleware. I don't want it to be in my body. How to do it?
Long story:
The specific value is a string named apiVersion.
I wrote this middleware:
function apiVersionMiddleware(req, res, next) {
res.apiVersion = "1.1";
next();
}
I see the apiVersion field populated in my response object in my backend. However, I don't see it in my http response.
After reading some express documentation, I found: https://expressjs.com/en/api.html#res.append. But is it a good practice to add a custom response in http headers? Should it be here or in another variable?
Realized that my question was more about the use of http headers.
Found this in the documentation response header. On the headers page, you can find this definition for response's headers:
Response header: Headers with additional information about the response, like its location or about the server itself (name and version etc.).
So it perfectly fits my use case.
That led me to this solution in express documentation: https://expressjs.com/en/api.html#res.set
function apiVersionMiddleware(req, res, next) {
res.set("api-version", "1.1");
next();
}
I also had to add my custom header to my exposedHeaders in cors:
app.use(
cors({
exposedHeaders: ["api-version"]
})
);

Express, React, & Webpack: CORS Preflight issue - can't pass headers in GET

React 16 and Node 9.9 on MacOS. Using Webpack Dev Server to test. I'm trying to pass headers in a GET request and they do not make it through.
On the server I'm allowing everything:
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,PUT,POST,DELETE,OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, value1, value2, value3");
next();
});
My client request is made with Axios, like so:
const request = axios({
method: "GET",
url: `http://my.api.co/endpoint`,
headers: {
"value1": "1",
"value2": "2",
"value3": "3"
}
});
When checking the incoming request, req.headers does not contain these values but I can see the keys (not the values) in the request.
I see:
req.headers.access-control-request-headers:"value1,value2,value3"
...and same in req.rawHeaders. But I cannot get my hands on the values - they're not coming through. I cannot access the following, for example:
req.headers.value1
I think this has something to do w/ "preflight" but am unsure how to solve the issue after hunting around. Not finding a straight answer. I would need this for every type of request, not just GET. How can I pass headers to the server from Axios?
Is the issue strictly with webpack-dev-server? Will I see a difference when I'm not using it? If it's going to be different in production, I'd definitely like to know what my steps are.
Sorry, simple mistake on my part. The middleware in the OP needed to be moved above the middleware block where I'm actually checking for those header values. I misunderstood how it works. All good now!

Angular 2 - Consuming restful api calls with windows authentication

I have a .net web api hosted on IIS 7 on a remote server which uses windows authentication. I want to access the web api using Angular 2 using TypeScript with node. Earlier i was getting an error 'Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource'
I added this on the hosted Application's web.config
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
But now i get Unauthorised 401 error. I have read about adding the following code to allow cross domain access - but I don't have any idea where do i add this in the angular 2 app and how to compile.
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.get('/', function(req, res, next) {
// Handle the get for this route
});
app.post('/', function(req, res, next) {
// Handle the post for this route
});
Here is the sample code for service that I am trying to make the get call with
#Injectable()
export class TodoService {
todos$: Observable<Todo[]>;
private _baseUrl: string;
private _todosObserver: Observer<Todo[]>;
private _dataStore: {
todos: Todo[]
};
constructor(private _http: Http) {
//let headers: Headers = new Headers();
this._baseUrl = 'http:/SampleServer/Server/api/LoadTodo';
this.todos$ = new Observable(observer => this._todosObserver = observer).share();
this._dataStore = { todos: [] };
}
loadTodos() {
let headers: Headers = new Headers();
//headers.append('Access-Control-Allow-Origin');
headers.append('Authorization', 'Basic ' +
btoa('username:password'));
//let opts: RequestOptions = new RequestOptions();
//opts.headers = headers;
this._http.get(`${this._baseUrl}`,headers).map(response => response.json()).subscribe(data => {
this._dataStore.todos = data;
this._todosObserver.next(this._dataStore.todos);
}, error => console.log('Could not load todos.'));
}
Any help to resolve this issue would be great.
You need to check if the Authorization header is correctly sent within your request. If you forgot to import the Headers class, the header won't be sent:
import {Http, Headers, ...} from 'angular2/http';
Another option would be that, since you are in the case of a preflighted request (GET method with the Authorization header), an OPTIONS request is sent. In fact, this request is transparently sent by the browser and the credentials are present in it. So you don't have to check security here on the server side. Otherwise you will have a 401 error since the server won't be able to authenticate the request...
See these articles for more details:
http://restlet.com/blog/2015/12/15/understanding-and-using-cors/
http://restlet.com/blog/2016/09/27/how-to-fix-cors-problems/
I have stumbled upon the same problem when using Angular 2 within an ASP.NET Core solution. For me, I had the explicitly specify withCredentials: true:
getSomeModels() {
return this.http.get(this.apiBasePath + 'models', { withCredentials: true })
.map(res => res.json());
}
CORS must be enabled on the server side (Web API) and no other changes were required on the client side.
NOTE: Server side code to enable CORS (Startup.cs)
public void ConfigureServices(IServiceCollection services)
{
// injected services
services.AddAuthorization();
services.AddMvc();
//db context
var corsBuilder = new CorsPolicyBuilder();
corsBuilder.AllowAnyHeader();
corsBuilder.AllowAnyMethod();
corsBuilder.AllowAnyOrigin(); // For anyone access.
corsBuilder.AllowCredentials();
services.AddCors(options =>
{
options.AddPolicy("SiteCorsPolicy", corsBuilder.Build());
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// middleware configuration
app.UseCors("SiteCorsPolicy");
}
When you use Windows authentication in a REST API and make calls from a client with Angular (or any other JavaScript solution), you have to do some configuration on both sides: server and client.
Server
If you only enable Windows authentication for the REST API from IIS, you are likely to get the 401 error from the client, since browsers make an OPTIONS call before making the original call to validate CORS access.
To enable this validation, you must also enable Anonymous Authentication in your REST API from IIS.
And add this exception in the web.config file of the REST API:
<system.webServer>
.....
<security>
<authorization>
<add accessType="Allow" verbs="OPTIONS" users="?" />
<add accessType="Deny" verbs="GET, PUT, POST, DELETE" users="?" />
</authorization>
</security>
</system.webServer>
Where Anonymous Authentication is allowed for OPTIONS type requests, in order to allow CORS validation from the browser. And Anonymous Authentication is denied for the rest of the operations: GET, PUT, POST, DELETE.
You must consider that the OPTIONS method will be enabled for all anonymous calls and you should not use it in your REST API if you are going to validate the authentication.
Client
On the other hand, in the client you can enable only the Windows authentication in the IIS where it's hosted. And from the JavaScript API call, include the parameter { withCredentials: true }, to specify that the call must include the credentials:
callApiPost(url: string, body: object) {
return this.http
.post(`${environment.api}/${url}`, body, { withCredentials: true })
.pipe(catchError(this.handleError));
}

Can not get CORS to work with restify, all preflight OPTIONS return with 405

I've read MANY articles at this point and tried just about every configuration I can think of to make CORS work with restify. I've used restify.CORS() with restify.fullResponse() and every other combination. I've also tried just using the cors lib (npm install cors) to no avail. My app looks like:
/**
* Setup middlewares.
*/
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.use(restify.cors());
server.use(restify.fullResponse());
server.use(morgan('dev'));
I've also tried adding opts handling with:
server.opts('/\.*/', (req, res, next) => {
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, OPTIONS, DELETE');
res.send(204);
return next();
});
In every case, I get back:
XMLHttpRequest cannot load http://localhost:3000/1.0.0/clients. Response to preflight
request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is
present on the requested resource. Origin 'http://run.plnkr.co' is therefore not
allowed access. The response had HTTP status code 405.
Any ideas? This is with restify 4.0.3. Thanks!
Use the built-in module for CORS:
server.use(restify.CORS({
origins: ['*'],
credentials: false, // defaults to false
headers: [''] // sets expose-headers
}));
Your solution should work as well if you actually add the Access-Control-Allow-Origin header ;-)
Use server.opts method to wirte your own handler for OPTIONS request. Below is the example you can use.
Also tell me if you are using set-credentials flag to true while making request from the browser. This handle in that case would have to respond with access cookies.
In the example below, I am returning the allowed origin for exact match. You can tweak it to be substring match also. But always return the exact value as found in request header origin in the response header 'Access-Control-Allow-Origin'. Its a good practice.
server.opts('/\.*/', (req, res) => {
const origin = req.header('origin');
const allowedOrigins = ['example.com', 'example.org'];
if (allowedOrigins.indexOf(origin) === -1) {
//origin is not allowed
return res.send(405);
}
//set access control headers to allow the preflight/options request
res.setHeader('Access-Control-Allow-Origin', header);
res.setHeader('Access-Control-Allow-Headers', 'Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
// Access-Control-Max-Age header catches the preflight request in the browser for the desired
// time. 864000 is ten days in number of seconds. Also during development you may want to keep
// this number too low e.g. 1.
res.setHeader('Access-Control-Max-Age', 864000);
return res.send(200);

Resources