How to implement Faust.js Basic Http Authentication - node.js

I'm implementing Headless Wordpress using Faust.js and ran into a problem. The current Wordpress backend requires basic http authentication, protected by base64-encoded credentials, before being able to access the backend site contents and I'm running the frontend with Faust.js. On a local environment, there is no need to implement adding the credentials to the header but on production (because the http authentication exists), I can't retrieve the post content as well as other assets such as images, etc. from the Wordpress backend.
I was doing some research into how to add the http authentication but so far I've only found limited examples of how to implement basic authentication to do this. One is with typescript (https://github.com/wpengine/faustjs/issues/845) but since I currently have the project on js code, it seems I would need to convert a lot of the files into typescript (maybe including the packages included in node_modules which I don't want to break if I did the conversion). I want to find a way to add this http basic authentication as part of the request header on my Faust.js frontend project without converting to js.
On the example, I've tried implementing this with the ts example, while using js code, but I'm getting all sorts of errors, when building it. Here's the code:
import { IncomingMessage } from 'http';
import { getClient, logQueries } from '#faustjs/next';
import {
generatedSchema,
scalarsEnumsHash,
GeneratedSchema,
SchemaObjectTypes,
SchemaObjectTypesNames,
} from './schema.generated';
export const client = getClient({
GeneratedSchema,
SchemaObjectTypesNames,
SchemaObjectTypes,
schema: generatedSchema,
scalarsEnumsHash,
applyRequestContext: async (url, init) => {
const newInit = {
...init,
headers: {
...init.headers,
authorization: 'Basic YmxhbmtkZXhzaXRzdGc6OTMzODVlNjY=',
},
};
return { url, init: newInit };
},
});
export function serverClient(req) {
return getClient<GeneratedSchema, SchemaObjectTypesNames, SchemaObjectTypes>({
schema: generatedSchema,
scalarsEnumsHash,
context: req,
});
}
if (process.env.NODE_ENV === 'development') {
logQueries(client);
}
export * from './schema.generated';
The errors I'm getting when building it are among the following:
1:1 Error: There should be at least one empty line between import groups import/order
8:3 Error: GeneratedSchema not found in './schema.generated' import/named
9:3 Error: SchemaObjectTypes not found in './schema.generated' import/named
10:3 Error: SchemaObjectTypesNames not found in './schema.generated' import/named

Related

Django: In Vue.js using axios post request 403 "CSRF Failed: CSRF token missing or incorrect."

I'm new to web development and am currently stucked at a problem I can't solve easily. I'm using Django3.2.6, django restframework (DRF) 3.14, vue3.0 and axios (to make API calls). I wrote an APIView to lock a model while editing an it's instance:
class LockCmAPI(APIView):
def post(self, request, **kwargs):
obj = get_object_or_404(CM, id=self.kwargs['pk'])
obj.lock()
print('locking object')
return HttpResponse(status=status.HTTP_200_OK)
For the frontend I created a Vue app that calls periodically my LockCmAPI to lock the instance and prevent others from editing it:
let vue = Vue.createApp({
delimiters: ['[[', ']]'],
data: function(){
return{
current_cm: cm_obj,
intervall: null,
}
},
methods: {
lockCmHeartbeat(){
console.log('locking');
console.log(`${BACKEND_PATH+LOCK_PATH+this.current_cm.id}/`);
axios.post(`${BACKEND_PATH+LOCK_PATH+this.current_cm.id}/`, this.current_cm, {
xsrfCookieName: 'csrftoken',
})
.then((response) => {
console.log('lock');
console.log(response);
});
}
},
mounted() {
this.lockCmHeartbeat();
this.intervall = setInterval(function(){
this.lockCmHeartbeat();
}.bind(this), FIVE_SEC_IN_MILISEC);
},
beforeDestroy() {
clearInterval(this.interval);
}
});
vue.mount('#cm_vue_block');
After running my code I get a 403 response with the message "Request failed with status code 403". When I looked further into the response I got this "{\"detail\":\"CSRF Failed: CSRF token missing or incorrect.\"}" in my responseText.
My Question:
Why does it tell me I sent an incorrect csrftoken since it's the same csrftoken in the cookie named csrftoken?
Can someone clarify it for me?
How can I fix this problem?
THX :D
For everyone who is going to have the same problem. Since the csrftoken I provided is exactly the same as the csrftoken I saw in my cookie named csrftoken. It had to be another issue...After reading the django documentation https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-CSRF_HEADER_NAME :
Default: 'HTTP_X_CSRFTOKEN' The name of the request header used for
CSRF authentication.
As with other HTTP headers in request.META, the header name received
from the server is normalized by converting all characters to
uppercase, replacing any hyphens with underscores, and adding an
'HTTP_' prefix to the name. For example, if your client sends a
'X-XSRF-TOKEN' header, the setting should be 'HTTP_X_XSRF_TOKEN'.
I realized my csrf headername is named different to djangos default CSRF_HEADERNAME. In order to solve this problem I configured xsrfHeadername in my axios request, which looks like this:
axios.post(`${BACKEND_PATH + LOCK_PATH + this.current_cm.id}/`, this.current_cm, {
xsrfCookieName: 'csrftoken',
xsrfHeaderName: 'X-CSRFTOKEN',
})

Google Cloud Trace NodeJS for bundled applications

I have a server side rendered web application using express that we were sending tracing info to Google using #google-cloud/trace-agent which worked fine. But THEN.... we bundled our application, and all trace information disappeared. We still see the requests in google cloud console, but now there are no child spans.
I scoured the documentation and stumbled upon this disappointing passage:
Tracing bundled or webpacked server code.
unsupported
The Trace Agent does not support bundled server code, so bundlers like webpack or #zeit/ncc will not work.
From: https://github.com/googleapis/cloud-trace-nodejs#tracing-bundled-or-webpacked-server-code
which I thought just meant that I need to start my own root spans... but that doesn't seem to be part of the API.
Does this mean I can't send spans in a bundled server? Is there a programmatic way to manually start root spans in express?
I ended up copying the middleware function from https://github.com/googleapis/cloud-trace-nodejs/blob/main/src/plugins/plugin-express.ts and adding it to my express app manually via app.use(...).
For completeness, the code is:
import { get as getTracer } from '#google-cloud/trace-agent';
import type { RootSpanOptions } from '#google-cloud/trace-agent/build/src/plugin-types';
import type { NextFunction, Request, Response } from 'express';
export function googleCloudTracing(request: Request, response: Response, next: NextFunction): void {
const tracer = getTracer();
const options: RootSpanOptions = {
name: request.path,
traceContext: tracer.propagation.extract(key => request.get(key)),
url: request.originalUrl,
method: request.method,
skipFrames: 1,
};
tracer.runInRootSpan(options, rootSpan => {
// Set response trace context.
const responseTraceContext = tracer.getResponseTraceContext(
options.traceContext!,
tracer.isRealSpan(rootSpan)
);
if (responseTraceContext) {
// This propagates the likes of tracer.constants.TRACE_CONTEXT_HEADER_NAME for cross-service tracing.
tracer.propagation.inject((k, v) => response.setHeader(k, v), responseTraceContext);
}
if (!tracer.isRealSpan(rootSpan)) {
next();
return;
}
tracer.wrapEmitter(request);
tracer.wrapEmitter(response);
const url = `${request.protocol}://${request.headers.host}${request.originalUrl}`;
rootSpan.addLabel(tracer.labels.HTTP_METHOD_LABEL_KEY, request.method);
rootSpan.addLabel(tracer.labels.HTTP_URL_LABEL_KEY, url);
rootSpan.addLabel(tracer.labels.HTTP_SOURCE_IP, request.ip);
response.on('finish', () => {
request.route?.path && rootSpan.addLabel('express/request.route.path', request.route.path);
rootSpan.addLabel(tracer.labels.HTTP_RESPONSE_CODE_LABEL_KEY, response.statusCode);
rootSpan.endSpan();
logger.info('Request tracing ended.');
});
next();
});
}

How to request data with token authorization in custom functions in Office JS Excel-Add-In?

What I have:
I adapted this example from Microsoft docs.
// functions.js
/**
* Get data
* #customfunction
* #returns {string[][]}
*/
async function getData() {
try {
const url = "https://api.example.com/some/objects/";
const token = await OfficeRuntime.storage.getItem("Token");
const authString = `Token ${token.toString()}`;
const response = await fetch(url, {
headers: { Authorization: authString }
});
if (!response.ok) {
throw new Error(response.statusText);
}
const jsonResponse = await response.json();
return jsonResponse.map(obj => {return [obj.id.toString(), obj.name]};
} catch (error) {
return [["ERROR", error.message]];
}
}
Added api.example.com to <AppDomains>
Item "Token" is present in OfficeRuntime.storage
Same API call with Postman works fine
The Add-In is not served from localhost (because of CORS reasons etc.)
What I get:
Because it is not developed locally it is very hard to debug ui-less custom functions. Therefore the only visible error I get so far, is the one I receive and return to Excel in the catch-block. It is an unhelpful error message: Network request failed
Can anyone help me with any suggestions?
Reason why it did not work was a known issue that custom functions run in a separate JavaScript runtime as described here. This runtime allows only CORS-safelisted request header because it lacks of CORS-Preflight -> therefore the Network request failed error with the Authorization header.
How I solved it:
Configure the Excel-Add-in to use a shared JavaScript runtime as described here.
Add <script src="functions.js"></script> just before the <\head> element in taskpane.html as described here.
Build the project npm run build
Clear the cache as described in the first two steps here.
Run Excel and load your Add-in.

Using cookies with axios and Vue

I have created a Node.js express server that connects to Salesforce.com using the SOAP interface provided by 'jsforce'. It uses session cookies for authorization via the 'express-session' package. So far, it has a POST method for login and a GET to perform a simple query. Testing with Postman has proven that this server is working as expected.
As the browser interface to this server, I have wrttien a Vue application that uses axios to perform the GET and POST. I need to save the session cookie created during login POST then attach attach the cookie to subsequent CRUD operations.
I have tried various methods to handle the cookies. One method I have tried is using axios response interceptors on the POST
axios.interceptors.response.use(response => {
update.update_from_cookies();
return response;
});
The function 'update_from_cookies' attempts to get the cookie named 'js-force' but it does not find it although I know it is being sent
import Cookie from 'js-cookie';
import store from './store';
export function update_from_cookies() {
let logged_in = Cookie.get('js-force');
console.log('cookie ' + logged_in);
if (logged_in && JSON.parse(logged_in)) {
store.commit('logged_in', true);
} else {
store.commit('logged_in', false);
}
}
I have also seen various recommendations to add parameters to the axios calls but these also do not work.
I would appreciate some advice about how to handle cookies using axios or some similar package that works with Vue
Thanks
The problem has been resolved. I was using the wrong syntax for the axios call
The correct syntax has the {withCredentials: true} as the last parameter
this.axios.post(uri, this.sfdata, {withCredentials: true})
.then( () => {
this.$router.push( {name : 'home' });
})
.catch( () => {
});

CORS on Web API and MVC 5 Controller: Upload images with fetch and FormData

I have an application that has the front and back ends running on different .NET projects.
The front end is an Aurelia web application running on ASP.NET 5. This Aurelia app (from now on The FrontEnd) gets all it's data from a Web API 2/MVC 5 application (henceforth, The BackEnd).
Since The FrontEnd and the BackEnd are different applications I have CORS setup, both for the Web API and in the Start.Auth.cs for the token bearer request.
The FronEnd is running on http://localhost:49850.
Now, for some code (this is all in the BackEnd)
Start.Auth.cs
The whole of the application resides behind a log-in form, so inside the Start.Auth.cs file, other than setting up the token-based authentication on the static Startup(), method I have a bit of middleware that adds the Access-Control-Allow-Origin header to the request on the one case where there is no token available yet: when we are requesting one.
public void ConfigureAuth(IAppBuilder app)
{
app.Use(async (context, next) =>
{
if (context.Request.Path.Value.Equals("/token"))
context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost:49850" });
await next();
});
app.UseCors(CorsOptions.AllowAll);
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
WebApiConfig.cs
Here I just added the EnableCorsAttribute so that it is enable globally.
var enableCors = new EnableCorsAttribute("http://localhost:49850", "*", "*");
config.EnableCors(enableCors);
Uploading files
Everything works fine; I can perform GET and POST requests to the Web API without a problem, the problem comes when trying to upload images.
To upload to files I have an action method in an ASP.NET MVC controller called FileControler.
FileController.cs
[HttpPost]
public ActionResult UploadImage(string id, string name = "")
{
var files = (from string fileName in Request.File
select Request.Files[fileName]
into file
where file != null
select DoSomethingWithTheFile(file, id)).ToList();
// ...
return Json(arrayWithFileUrls);
}
Calling the MVC controller
This is already part of The FrontEnd.
To call this method I use Aurelia's Fetch Client:
upload(url, data, files) {
let formData = new FormData();
for (let key of Object.keys(data)) {
formData.append(key, data[key]);
}
for (let i = 0; i < files.length; i++) {
formData.append(`files[${i}]`, files[i]);
}
return this.http.fetch(url, {
method: "POST",
body: formData,
headers: {
cmsDatabase: sessionStorage["cmsDatabase"]
}
}).then(response => {
return response.json();
}).catch(error => {
console.log(error);
});
}
And here's a call to the upload method above:
// files comes from an <input type="file" />
upload("http://localhost:64441/file/uploadImage", { id: id }, files)
.then((uploadedPhotos) => {
// do something with those file urls...
});
The Problem
All this works if I remove all CORS setup from WebApiConfig.cs, and in Startup.Auth.cs I substitute the call to the middleware for app.UseCors(CorsOptions.AllowAll);, so I know my code is ok, but as soon as I use the CORS setup described above, everything works except the call to http://localhost:64441/file/uploadImage, returning even a 404:
Fetch API cannot load http://localhost:64441/file/uploadForSku.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin'
header is present on the requested resource.
Origin 'http://localhost:49850' is therefore not allowed access.
The response had HTTP status code 404. If an opaque response serves your needs,
set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
The "funny" thing is that if I try calling that url with, for intance, REST Console I don't get a 404.
I've tried adding the [HttpOptions] attribute to the action method; I've tried creating ActionFilterAttributes as described here and here, and even setting uip CORS from within the web.config, but to no avail.
I know the problem is that FileController is a regular MVC Controller instead of a Web API controlle, but shouldn't it still be possible to get CORS working?
have you tried this
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
in ApplicationOAuthProvider.cs file

Resources