Closed wh1t3cAt1k closed 4 years ago
I can confirm that mocking the fs
and solving the request
library problem gets rid of all the webpack errors.
Most of the above problems are caused by dependency on request
which is only available server-side.
This can most likely be mitigated by using https://www.npmjs.com/package/cross-fetch instead: this package works across the platform table.
As a temporary work-around, I was able to monkey-patch the HttpClient to use browser-request
instead of just request
, where I stripped all headers that the browser considers unsafe, e.g. "User-Agent" and "Accept-Encoding".
I also mocked the fs
module to use an in-memory "filesystem" provided by browserify-fs
:
resolve: {
alias: {
'fs': 'browserify-fs',
'./HttpClientHandler$': path.resolve(__dirname, 'src/.../polyfills/http-client-handler-polyfill')
},
}
(in webpack.config.js)
import { Response, RequestCallback } from 'request';
import _ from 'lodash';
import { falsyToUndefined } from '../../value-decorators/falsy-to-undefined';
const client: typeof import('request') = require('browser-request');
class HttpClientHandler
{
private readonly _options: any;
public constructor(options: any)
{
this._options = options;
}
public postAsync = async (): Promise<Response> => {
const promise = new Promise<Response>((resolve, reject) => {
const requestCallback: RequestCallback = (error: any, response: Response, body: any) => {
if (falsyToUndefined(error) !== undefined) {
reject(error);
return;
}
if (falsyToUndefined(body) === undefined) {
return;
}
if (falsyToUndefined(response.headers) === undefined) {
response.headers = {
"content-type": 'application/xml',
}
}
resolve(response);
}
const browserSafeOptions = _.cloneDeep(this._options);
this._removeUnsafeHeader(browserSafeOptions, "User-Agent");
this._removeUnsafeHeader(browserSafeOptions, "Accept-Encoding");
client(browserSafeOptions, requestCallback);
});
const result = await promise;
return result;
}
private readonly _removeUnsafeHeader = (browserSafeOptions: any, headerName: string): void => {
if (browserSafeOptions?.headers?.[headerName] !== undefined) {
browserSafeOptions.headers[headerName] = undefined;
}
}
}
export default HttpClientHandler;
(polyfill file)
However, I would be glad if this could work out-of-the-box in an isomorphic way.
Something like this will not be considered until Sage Intacct Web Services supports public client types via OAuth2. Today, only confidential client types are supported since in a public client like the browser would be exposing the Web Services sender+user passwords.
@jimmymcpeter sorry, I do not completely understand you. Where would it be exposing the password?
As far as I understand, interacting with the web services API does not require an OAuth authentication, we use session-based authentication.
If the OAuth is an additional option, it can simply be not supported in client-side environments.
I considered forking your SDK to do this manually, but I figured supporting isomorphic JS was not a very strict requirement, so I decided to file it here first.
To use web services, you must have a sender ID and sender password. The sender credentials are independent of a user's credentials (session ID or company ID, user ID, and user password).
All web services requests have to have a senderid and password, you'd have to hard code this into the code running in the browser. This means it's visible to anyone using your app and goes against the web services developer terms. If you're a marketplace partner this would cause it to also fail the app review.
<request>
<control>
<senderid>testsenderid</senderid>
<password>pass123!</password> <!-- this would be visible -->
<controlid>unittest</controlid>
<uniqueid>false</uniqueid>
<dtdversion>3.0</dtdversion>
<includewhitespace>false</includewhitespace>
</control>
<operation transaction="false">
<authentication>
<sessionid>testsession..</sessionid>
</authentication>
<content/>
</operation>
</request>
@jimmymcpeter I understand.
No, of course it won't be hard-coded into the code in any case.
Consider two options:
For a number of important reasons, we wish to make our application a fat-client, and there certainly seems to be a way to make it secure...
I think the proxy is probably your best choice at the moment. This also gives you control on any user licensing since, if I'm not mistaken, Microsoft has deprecated paid office add-ins in favor of free add-ins+paid SaaS offers.
Current intacct-sdk-js has a dependency on a number of Node.js-specific core modules such as
net
,tls
,fs
, and others.My app intentionally wants to do a fat-client implementation, so it needs to work in the browser. Unfortunately currently it is not possible.
If possible, the current SDK needs to be isomorphic and work in both client-side and server-side JS.
Example errors are attached below:
As we can see, the only directly offending file is
ProfileCredentialProvider.js
andAttachmentFile.js
, otherwise those are in the librariesrequest-promise-native
library.I am not sure if after fixing these errors no new errors would appear, but I am really looking forward to developing my web app without resorting to server-side logic!