oauth-jsclient
oauth-jsclient copied to clipboard
Abandoned?
No security package upgrades, no updates in several years.
They updated PHP out of all things and not node.js... the #1 developer rated backend framework..
Seriously
After 18 hours of understanding their very weirdly designed documentation, I just ended up creating a custom class to just do what I want it to do. Sad considering I'm also using azure api to process invoices and read text from them and their documentation is 100x better and their packages are actually kept up to date
@m1daz Any chance I could snag that from you?
import { GetPrisma } from "@/lib/database";
export class Quickbooks {
private redirectUrl: string;
private scope: string;
private clientId: string;
private clientSecret: string | undefined;
private readonly bearerUrl =
"https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
constructor(
redirectUrl: string,
scope: string,
clientId: string,
clientSectet?: string
) {
this.redirectUrl = redirectUrl;
this.scope = scope;
this.clientId = clientId;
this.clientSecret = clientSectet;
}
get OAUTH_URL(): string {
return `https://appcenter.intuit.com/app/connect/oauth2?client_id=${this.clientId}&scope=${this.scope}&redirect_uri=${this.redirectUrl}&response_type=code&state=PlaygroundAuth`;
}
private chunkBase64(b64: string): string {
// Split b64 string with /r/n to avoid exceeding max line size (1000)
// So for each 900 char for example, /r/n
// dont add /r/n to last item
const chunkSize = 72;
const chunks = [];
for (let i = 0; i < b64.length; i += chunkSize) {
chunks.push(b64.slice(i, i + chunkSize));
}
return chunks.join("\r\n");
}
async createInvoice(
realmId: string,
token: string,
customerId: string,
loadInformation: {
amount: number;
number: number;
}
): Promise<boolean> {
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/most-commonly-used/invoice#create-an-invoice
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities//attachable#upload-attachments
const resp = await fetch(
`https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
Line: [
{
DetailType: "SalesItemLineDetail",
Amount: loadInformation.amount,
SalesItemLineDetail: {
ItemRef: {
name: "Services",
value: "1",
},
},
},
],
CustomerRef: {
value: customerId,
},
}),
}
);
const data = await resp.json();
if (data && data.Invoice) {
const invoiceId: string = data.Invoice.Id;
return await this.uploadAttachment(
realmId,
token,
invoiceId,
loadInformation.number
);
} else {
return false;
}
}
async uploadAttachment(
realmId: string,
token: string,
invoiceId: string,
loadId: number
): Promise<boolean> {
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities//attachable#upload-attachments
const clientHandle = await GetPrisma();
if (!clientHandle.success) {
return false;
}
const client = clientHandle.prismaHandle!;
const invoiceFile = await client.invoice.findFirst({
where: {
load: {
id: loadId,
},
},
});
if (!invoiceFile) {
return false;
}
let pdfData = Buffer.from(invoiceFile.data ?? "", "base64");
if (pdfData.toString().split(";base64,").length > 1) {
pdfData = Buffer.from(pdfData.toString().split(";base64,")[1], "base64");
}
const body = `--dEneMo239
Content-Disposition: form-data; name="file_metadata_01"; filename="attachment.json"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit
{
"AttachableRef": [
{"EntityRef": {
"type": "Invoice",
"value": "${invoiceId}"
}
}
],
"FileName": "invoice.pdf",
"ContentType": "application/pdf"
}
--dEneMo239
Content-Disposition: form-data; name="file_content_01"; filename="invoice.pdf"
Content-Type: application/pdf
Content-Transfer-Encoding: base64
${this.chunkBase64(pdfData.toString("base64"))}
--dEneMo239--`;
// calculate body size in mb
const bodySize = Buffer.byteLength(body, "utf8") / 1024 / 1024;
const resp = await fetch(
`https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/upload`,
{
method: "POST",
headers: {
"Content-Type": "multipart/form-data; boundary=dEneMo239",
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: body,
}
);
const data = await resp.json();
if (data && data.AttachableResponse) {
return true;
} else {
return false;
}
}
async createCustomer(
realmId: string,
token: string,
customerInformation: {
Name: string;
EmailAddress?: string;
Phone?: string;
Address: {
Street: string;
City: string;
Zip: string;
State: string;
};
}
): Promise<string | undefined> {
// https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/customer#create-a-customer
const resp = await fetch(
`https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/customer`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
FullyQualifiedName: customerInformation.Name,
PrimaryEmailAddr: {
Address: customerInformation.EmailAddress ?? "",
},
DisplayName: customerInformation.Name,
PrimaryPhone: {
FreeFormNumber: customerInformation.Phone ?? "",
},
CompanyName: customerInformation.Name,
BillAddr: {
CountrySubDivisionCode: customerInformation.Address.State,
City: customerInformation.Address.City,
PostalCode: customerInformation.Address.Zip,
Line1: customerInformation.Address.Street,
Country: "USA",
},
}),
}
);
const data = await resp.json();
if (
data.responseHeader === undefined ||
data.responseHeader.status === 200
) {
return data.Customer.Id;
}
return undefined;
}
async getAccessToken(code: string): Promise<{
access_token: string;
expires_in: number;
refresh_token: string;
x_refresh_token_expires_in: number;
token_type: string;
}> {
// convert clientId & secret to base64 (nodejs)
const bearer = Buffer.from(
`${this.clientId}:${this.clientSecret}`
).toString("base64");
const resp = await fetch(this.bearerUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
Authorization: `Basic ${bearer}`,
},
body: `grant_type=authorization_code&code=${code}&redirect_uri=${this.redirectUrl}`,
});
const data: any = await resp.json();
return data;
}
async refreshAccessToken(refreshToken: string): Promise<{
access_token: string;
expires_in: number;
refresh_token: string;
x_refresh_token_expires_in: number;
token_type: string;
}> {
const bearer = Buffer.from(
`${this.clientId}:${this.clientSecret}`
).toString("base64");
const resp = await fetch(this.bearerUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
Authorization: `Basic ${bearer}`,
},
body: `grant_type=refresh_token&refresh_token=${refreshToken}`,
});
const data: any = await resp.json();
return data;
}
}
I made it work for me, you might need to modify it for you.
@m1daz Cool ty. I'm still trying to get everything authenticated but a few of those methods will be helpful for sure. Cheers
Hi @geoffcorey, @m1daz, @WillsWebsites - Robert with the Intuit team here. Thank you for your comments and contributions. You're absolutely right, we haven't done a great job of keeping this OAuth Client up to date. I'm happy to share, however, that this is changing. Over the next few months, we'll be conducting a full review (including actioning all open PRs + issues) alongside other Intuit SDKs/OAuth Clients. From there, expect to see regular updates across the board. Outside of this thread, if you have any additional suggestions, please send them our way!
Hi @geoffcorey, @m1daz, @WillsWebsites - Robert with the Intuit team here. Thank you for your comments and contributions. You're absolutely right, we haven't done a great job of keeping this OAuth Client up to date. I'm happy to share, however, that this is changing. Over the next few months, we'll be conducting a full review (including actioning all open PRs + issues) alongside other Intuit SDKs/OAuth Clients. From there, expect to see regular updates across the board.
Outside of this thread, if you have any additional suggestions, please send them our way!
Thank you so much. I am very interested in working with intuit for our software, but I need to have the confidence in there being support.
@robert-mings That'd be great to see. The main things with this specific package that would be useful to start are:
- Typescript with all defined types and responses
- Updating packages that have security flaws
Otherwise with intuit api's in general:
- Being able to get the payment invoice link as you're able to in the software itself (maybe it can be included in the invoice query response)
- Maybe the ability to not have to set up a full Oauth2 system to work with your companies own data if possible?
- An official API SDK instead of relying on the community one.
I can post these elsewhere if needed just let me know
Hi @geoffcorey, @m1daz, @WillsWebsites - Robert with the Intuit team here. Thank you for your comments and contributions. You're absolutely right, we haven't done a great job of keeping this OAuth Client up to date. I'm happy to share, however, that this is changing. Over the next few months, we'll be conducting a full review (including actioning all open PRs + issues) alongside other Intuit SDKs/OAuth Clients. From there, expect to see regular updates across the board. Outside of this thread, if you have any additional suggestions, please send them our way!
Thank you so much. I am very interested in working with intuit for our software, but I need to have the confidence in there being support.
@m1daz You bet! Check out the docs if you haven't already. Lots of great info there.
@robert-mings That'd be great to see. The main things with this specific package that would be useful to start are:
* Typescript with all defined types and responses * Updating packages that have security flaws
Otherwise with intuit api's in general:
* Being able to get the payment invoice link as you're able to in the software itself (maybe it can be included in the invoice query response) * Maybe the ability to not have to set up a full Oauth2 system to work with your companies own data if possible? * An official API SDK instead of relying on the community one.
I can post these elsewhere if needed just let me know
@WillsWebsites Fantastic suggestions, thank you!
@robert-mings any update on when security patches and types will be pushed out for this sdk?
Would love to see some updates on security patches and/or typescript support.
Thank you all for your patience, much appreciated! Ensuring you that this SDK is still active and being maintained. We have plans to release the security patches asap, before we get too busy with the upcoming holiday season. Stay tuned!
Created a branch called 'hotfix-4.0.1' and ran npm audit for security fixes. Feel free to test the branch, or raise PR on it and provide any comment or suggestions you may have. Thanks
I moved on and use @apigrate/quickbooks now. Years without security patches, being maintained by hacktoberfest and interns is not acceptable for a company dealing with finances.
Hi @geoffcorey, @m1daz, @WillsWebsites - Robert with the Intuit team here. Thank you for your comments and contributions. You're absolutely right, we haven't done a great job of keeping this OAuth Client up to date. I'm happy to share, however, that this is changing. Over the next few months, we'll be conducting a full review (including actioning all open PRs + issues) alongside other Intuit SDKs/OAuth Clients. From there, expect to see regular updates across the board. Outside of this thread, if you have any additional suggestions, please send them our way!
Thank you so much. I am very interested in working with intuit for our software, but I need to have the confidence in there being support.
@m1daz You bet! Check out the docs if you haven't already. Lots of great info there.
@robert-mings That'd be great to see. The main things with this specific package that would be useful to start are:
* Typescript with all defined types and responses * Updating packages that have security flaws
Otherwise with intuit api's in general:
* Being able to get the payment invoice link as you're able to in the software itself (maybe it can be included in the invoice query response) * Maybe the ability to not have to set up a full Oauth2 system to work with your companies own data if possible? * An official API SDK instead of relying on the community one.
I can post these elsewhere if needed just let me know
@WillsWebsites Fantastic suggestions, thank you!
Robert, today I had to modify some code for the quickbooks API and I decided to install this package instead of using my class that I made by reading the conversation. I was really happy seeing all the other changes, however, there's still one really big downfall for me right now. There's no TypeScript support whatsoever. This was promised previously, is it still coming?