IO-256 Authorization and Basic Calls
This commit is contained in:
@@ -9,34 +9,18 @@ import { useCookies } from "react-cookie";
|
||||
export default function QboAuthorizeComponent() {
|
||||
const location = useLocation();
|
||||
|
||||
const [cookies, setCookie] = useCookies(["access_token", "refresh_token"]);
|
||||
const [, setCookie] = useCookies(["access_token", "refresh_token"]);
|
||||
|
||||
const handleQbSignIn = async () => {
|
||||
const result = await Axios.post("/qbo/authorize");
|
||||
console.log("pushing to history", result.data);
|
||||
window.location.href = result.data;
|
||||
};
|
||||
const qs = queryString.parse(location.search);
|
||||
|
||||
const { error } = qs;
|
||||
|
||||
useEffect(() => {
|
||||
const ExchangeForAccessToken = async () => {
|
||||
const response = await Axios.get(`/qbo/callback${location.search}`);
|
||||
|
||||
let expires = new Date();
|
||||
expires.setTime(expires.getTime() + response.data.expires_in * 1000);
|
||||
setCookie("qbo_access_token", response.data.access_token, {
|
||||
path: "/",
|
||||
expires,
|
||||
});
|
||||
expires = new Date();
|
||||
expires.setTime(
|
||||
expires.getTime() + response.data.x_refresh_token_expires_in * 1000
|
||||
);
|
||||
setCookie("qbo_refresh_token", response.data.refresh_token, {
|
||||
path: "/",
|
||||
expires,
|
||||
});
|
||||
};
|
||||
const qs = queryString.parse(location.search);
|
||||
const { code, state, realmId } = qs;
|
||||
const hasBeenCalledBack = code && realmId && state;
|
||||
|
||||
@@ -51,19 +35,19 @@ export default function QboAuthorizeComponent() {
|
||||
path: "/",
|
||||
expires,
|
||||
});
|
||||
ExchangeForAccessToken();
|
||||
}
|
||||
}, [location, setCookie]);
|
||||
}, [qs, location, setCookie]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Space>
|
||||
<Space wrap>
|
||||
<Button onClick={handleQbSignIn}>
|
||||
<img
|
||||
{/* <img
|
||||
src={QboImg}
|
||||
alt="Sign in with Intuit"
|
||||
onClick={handleQbSignIn}
|
||||
/>
|
||||
/> */}
|
||||
auth
|
||||
</Button>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
@@ -75,7 +59,18 @@ export default function QboAuthorizeComponent() {
|
||||
>
|
||||
Refresh Token
|
||||
</Button>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const response = await Axios.post(`/qbo/receivables`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
console.log(response);
|
||||
}}
|
||||
>
|
||||
REC
|
||||
</Button>
|
||||
</Space>
|
||||
{error && JSON.parse(decodeURIComponent(error)).error_description}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import axios from "axios";
|
||||
import { auth } from "../firebase/firebase.utils";
|
||||
|
||||
import { Cookies } from "react-cookie";
|
||||
|
||||
const cookies = new Cookies();
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
axios.defaults.baseURL =
|
||||
process.env.REACT_APP_AXIOS_BASE_API_URL || "https://api.imex.online/";
|
||||
@@ -18,38 +15,6 @@ export const axiosAuthInterceptorId = axios.interceptors.request.use(
|
||||
}
|
||||
}
|
||||
|
||||
//Check if Qbo Cookies need to be refreshed.
|
||||
const qbo_access_token = cookies.get("qbo_access_token");
|
||||
const qbo_refresh_token = cookies.get("qbo_refresh_token");
|
||||
|
||||
if (!qbo_refresh_token) {
|
||||
//Kill both values just in case.
|
||||
cookies.remove("qbo_access_token");
|
||||
cookies.remove("qbo_refresh_token");
|
||||
return config;
|
||||
}
|
||||
//Are they expired?
|
||||
if (!qbo_access_token) {
|
||||
//Refresh it first.
|
||||
const response = await axios.get(`/qbo/refresh`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
let expires = new Date();
|
||||
expires.setTime(expires.getTime() + response.data.expires_in * 1000);
|
||||
cookies.set("qbo_access_token", response.data.access_token, {
|
||||
path: "/",
|
||||
expires,
|
||||
});
|
||||
expires = new Date();
|
||||
expires.setTime(
|
||||
expires.getTime() + response.data.x_refresh_token_expires_in * 1000
|
||||
);
|
||||
cookies.set("qbo_refresh_token", response.data.refresh_token, {
|
||||
path: "/",
|
||||
expires,
|
||||
});
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."associations" DROP COLUMN "qbo_auth";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,5 @@
|
||||
- args:
|
||||
cascade: false
|
||||
read_only: false
|
||||
sql: ALTER TABLE "public"."associations" ADD COLUMN "qbo_auth" jsonb NULL;
|
||||
type: run_sql
|
||||
File diff suppressed because one or more lines are too long
@@ -41,6 +41,7 @@
|
||||
"node-quickbooks": "^2.0.39",
|
||||
"nodemailer": "^6.6.3",
|
||||
"phone": "^3.1.2",
|
||||
"query-string": "^7.0.1",
|
||||
"soap": "^0.40.0",
|
||||
"socket.io": "^4.1.3",
|
||||
"ssh2-sftp-client": "^7.0.0",
|
||||
|
||||
@@ -148,7 +148,6 @@ app.post("/utils/time", utils.servertime);
|
||||
var qbo = require("./server/accounting/qbo/qbo");
|
||||
app.post("/qbo/authorize", fb.validateFirebaseIdToken, qbo.authorize);
|
||||
app.get("/qbo/callback", qbo.callback);
|
||||
app.get("/qbo/refresh", fb.validateFirebaseIdToken, qbo.refresh);
|
||||
app.post("/qbo/receivables", fb.validateFirebaseIdToken, qbo.receivables);
|
||||
|
||||
var data = require("./server/data/data");
|
||||
|
||||
@@ -6,6 +6,7 @@ require("dotenv").config({
|
||||
),
|
||||
});
|
||||
const OAuthClient = require("intuit-oauth");
|
||||
const logger = require("../../utils/logger");
|
||||
|
||||
const oauthClient = new OAuthClient({
|
||||
clientId: process.env.QBO_CLIENT_ID,
|
||||
@@ -16,15 +17,17 @@ const oauthClient = new OAuthClient({
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
try {
|
||||
logger.log("qbo-auth-uri", "DEBUG", req.user.email, null, null);
|
||||
const authUri = oauthClient.authorizeUri({
|
||||
scope: [OAuthClient.scopes.Accounting, OAuthClient.scopes.OpenId],
|
||||
state: req.user.uid,
|
||||
state: req.user.email,
|
||||
}); // can be an array of multiple scopes ex : {scope:[OAuthClient.scopes.Accounting,OAuthClient.scopes.OpenId]}
|
||||
// Redirect the authUri
|
||||
|
||||
res.send(authUri);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.sendStatus(500);
|
||||
logger.log("qbo-auth-uri-error", "ERROR", req.user.email, null, { error });
|
||||
|
||||
res.status(500).json(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@ require("dotenv").config({
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const logger = require("../../utils/logger");
|
||||
const OAuthClient = require("intuit-oauth");
|
||||
|
||||
var QuickBooks = require("node-quickbooks");
|
||||
const Promise = require("bluebird");
|
||||
const QuickBooksPromise = Promise.promisifyAll(QuickBooks.prototype);
|
||||
|
||||
const client = require("../../graphql-client/graphql-client").client;
|
||||
const queries = require("../../graphql-client/queries");
|
||||
const queryString = require("query-string");
|
||||
const oauthClient = new OAuthClient({
|
||||
clientId: process.env.QBO_CLIENT_ID,
|
||||
clientSecret: process.env.QBO_SECRET,
|
||||
@@ -20,50 +22,62 @@ const oauthClient = new OAuthClient({
|
||||
});
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
// Parse the redirect URL for authCode and exchange them for tokens
|
||||
|
||||
const params = queryString.parse(req.url.split("?").reverse()[0]);
|
||||
try {
|
||||
// Exchange the auth code retrieved from the **req.url** on the redirectUri
|
||||
logger.log("qbo-callback-create-token", "DEBUG", params.state, null, null);
|
||||
const authResponse = await oauthClient.createToken(req.url);
|
||||
if (authResponse.json.error) {
|
||||
logger.log("qbo-callback-error", "ERROR", params.state, null, {
|
||||
error: authResponse.json,
|
||||
});
|
||||
res.redirect(
|
||||
`http://localhost:3000/manage/accounting/qbo?error=${encodeURIComponent(
|
||||
JSON.stringify(authResponse.json)
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
await client.request(queries.SET_QBO_AUTH, {
|
||||
email: params.state,
|
||||
qbo_auth: { ...authResponse.json, createdAt: Date.now() },
|
||||
});
|
||||
logger.log(
|
||||
"qbo-callback-create-token-success",
|
||||
"DEBUG",
|
||||
params.state,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
const { access_token, refresh_token } = authResponse.json;
|
||||
|
||||
//store this information against the assocation record.
|
||||
|
||||
//Send a redirect back to the imex online application
|
||||
res.json(authResponse.json);
|
||||
|
||||
// var qbo = new QuickBooks(
|
||||
// process.env.QBO_CLIENT_ID,
|
||||
// process.env.QBO_SECRET,
|
||||
// access_token,
|
||||
// false, // no token secret for oAuth 2.0
|
||||
// realmId,
|
||||
// process.env.NODE_ENV !== "production", // use the sandbox?, // use the sandbox?
|
||||
// true, // enable debugging?
|
||||
// null, // set minorversion, or null for the latest version
|
||||
// "2.0", //oAuth version
|
||||
// refresh_token
|
||||
// );
|
||||
|
||||
// qbo.findInvoices({ fetchAll: true }, (errors, invoices) =>
|
||||
// console.log(errors, invoices)
|
||||
// );
|
||||
res.redirect(`http://localhost:3000/manage/accounting/qbo?`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("The error message is :" + JSON.stringify(e, null, 2));
|
||||
console.error(e.intuit_tid);
|
||||
res.status(500).json(e);
|
||||
logger.log("qbo-callback-error", "ERROR", params.state, null, {
|
||||
error: e,
|
||||
});
|
||||
res.status(400).json(e);
|
||||
}
|
||||
};
|
||||
|
||||
exports.refresh = async (req, res) => {
|
||||
exports.refresh = async (oauthClient, req) => {
|
||||
try {
|
||||
oauthClient.setToken(req.cookies.qbo_access_token);
|
||||
const authResponse = oauthClient.refreshUsingToken(
|
||||
req.cookies.qbo_refresh_token
|
||||
);
|
||||
res.json(authResponse.json);
|
||||
logger.log("qbo-token-refresh", "DEBUG", req.user.email, null, null);
|
||||
const authResponse = await oauthClient.refresh();
|
||||
await client.request(queries.SET_QBO_AUTH, {
|
||||
email: req.user.email,
|
||||
qbo_auth: { ...authResponse.json, createdAt: Date.now() },
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json(error);
|
||||
logger.log("qbo-token-refresh-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.setNewRefreshToken = async (email, apiResponse) => {
|
||||
logger.log("qbo-token-updated", "DEBUG", email, null, null);
|
||||
|
||||
await client.request(queries.SET_QBO_AUTH, {
|
||||
email,
|
||||
qbo_auth: { ...apiResponse.token, createdAt: Date.now() },
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const urlBuilder = require("./qbo").urlBuilder;
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
@@ -5,39 +6,102 @@ require("dotenv").config({
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const client = require("../../graphql-client/graphql-client").client;
|
||||
const queries = require("../../graphql-client/queries");
|
||||
const {
|
||||
refresh: refreshOauthToken,
|
||||
setNewRefreshToken,
|
||||
} = require("./qbo-callback");
|
||||
const OAuthClient = require("intuit-oauth");
|
||||
var QuickBooks = require("node-quickbooks");
|
||||
|
||||
const oauthClient = new OAuthClient({
|
||||
clientId: process.env.QBO_CLIENT_ID,
|
||||
clientSecret: process.env.QBO_SECRET,
|
||||
environment: process.env.NODE_ENV === "production" ? "production" : "sandbox",
|
||||
redirectUri: process.env.QBO_REDIRECT_URI,
|
||||
logging: true,
|
||||
});
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
try {
|
||||
oauthClient.setToken(req.cookies.qbo_access_token);
|
||||
const response = await client.request(queries.GET_QBO_AUTH, {
|
||||
email: req.user.email,
|
||||
});
|
||||
response.associations[0].qbo_auth;
|
||||
|
||||
var qbo = new QuickBooks(
|
||||
process.env.QBO_CLIENT_ID,
|
||||
process.env.QBO_SECRET,
|
||||
req.cookies.qbo_access_token,
|
||||
false, // no token secret for oAuth 2.0
|
||||
req.cookies.qbo_realmId,
|
||||
process.env.NODE_ENV !== "production", // use the sandbox?, // use the sandbox?
|
||||
true, // enable debugging?
|
||||
null, // set minorversion, or null for the latest version
|
||||
"2.0", //oAuth version
|
||||
req.cookies.qbo_refresh_token
|
||||
);
|
||||
oauthClient.setToken(response.associations[0].qbo_auth);
|
||||
|
||||
qbo.findInvoices({ fetchAll: true }, (errors, invoices) =>
|
||||
console.log(errors, invoices)
|
||||
);
|
||||
if (!oauthClient.token.isAccessTokenValid()) {
|
||||
await refreshOauthToken(oauthClient, req);
|
||||
if (!oauthClient.token.isAccessTokenValid()) {
|
||||
res.sendStatus(401);
|
||||
}
|
||||
}
|
||||
|
||||
res.send({});
|
||||
const customerCreate = {
|
||||
FullyQualifiedName: "A Test Customer",
|
||||
DisplayName: "A test Customer",
|
||||
};
|
||||
|
||||
// const ret = await oauthClient.makeApiCall({
|
||||
// url: urlBuilder(req.cookies.qbo_realmId, "customer"),
|
||||
// method: "POST",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// body: JSON.stringify(customerCreate),
|
||||
// });
|
||||
|
||||
// const invoice = {
|
||||
// Line: [
|
||||
// {
|
||||
// DetailType: "SalesItemLineDetail",
|
||||
// Amount: 100,
|
||||
// SalesItemLineDetail: {
|
||||
// ItemRef: {
|
||||
// name: "Services",
|
||||
// value: "1",
|
||||
// },
|
||||
// TaxCodeRef: {
|
||||
// value: "2",
|
||||
// },
|
||||
// Qty: 1,
|
||||
// UnitPrice: 100,
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// CustomerRef: {
|
||||
// name: "A test Customer",
|
||||
// },
|
||||
// };
|
||||
|
||||
// const ret2 = await oauthClient.makeApiCall({
|
||||
// url: urlBuilder(req.cookies.qbo_realmId, "invoice"),
|
||||
// method: "POST",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// body: JSON.stringify(invoice),
|
||||
// });
|
||||
|
||||
const ret2 = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(
|
||||
req.cookies.qbo_realmId,
|
||||
"query",
|
||||
`select * From TaxCode where Active = true`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
// body: JSON.stringify(invoice),
|
||||
});
|
||||
setNewRefreshToken(req.user.email, ret2);
|
||||
console.log(ret2);
|
||||
res.send(ret2);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.sendStatus(500);
|
||||
res.status(400).json(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
exports.callback = require("./qbo-callback").default;
|
||||
exports.authorize = require("./qbo-authorize").default;
|
||||
exports.refresh = require("./qbo-callback").refresh;
|
||||
exports.receivables = require("./qbo-receivables");
|
||||
|
||||
const OAuthClient = require("intuit-oauth");
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
@@ -11,3 +6,19 @@ require("dotenv").config({
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
|
||||
function urlBuilder(realmId, object, query = null) {
|
||||
return `https://${
|
||||
process.env.NODE_ENV === "development" || !process.env.NODE_ENV
|
||||
? "sandbox-"
|
||||
: ""
|
||||
}quickbooks.api.intuit.com/v3/company/${realmId}/${object}${
|
||||
query ? `?query=${encodeURIComponent(query)}` : ""
|
||||
}`;
|
||||
}
|
||||
|
||||
exports.urlBuilder = urlBuilder;
|
||||
exports.callback = require("./qbo-callback").default;
|
||||
exports.authorize = require("./qbo-authorize").default;
|
||||
exports.refresh = require("./qbo-callback").refresh;
|
||||
exports.receivables = require("./qbo-receivables").default;
|
||||
|
||||
@@ -1049,3 +1049,17 @@ exports.GET_CDK_ALLOCATIONS = `
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
exports.GET_QBO_AUTH = `query GET_QBO_AUTH($email: String!) {
|
||||
associations(where: {_and: {active: {_eq: true}, useremail: {_eq: $email}}}){
|
||||
id
|
||||
qbo_auth
|
||||
}
|
||||
}`;
|
||||
|
||||
exports.SET_QBO_AUTH = `mutation SET_QBO_AUTH($email: String!, $qbo_auth: jsonb!) {
|
||||
update_associations(_set: {qbo_auth: $qbo_auth}, where: {_and: {active: {_eq: true}, useremail: {_eq: $email}}}){
|
||||
affected_rows
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -3172,6 +3172,16 @@ query-string@^6.12.1:
|
||||
split-on-first "^1.0.0"
|
||||
strict-uri-encode "^2.0.0"
|
||||
|
||||
query-string@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.0.1.tgz#45bd149cf586aaa582dffc7ec7a8ad97dd02f75d"
|
||||
integrity sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==
|
||||
dependencies:
|
||||
decode-uri-component "^0.2.0"
|
||||
filter-obj "^1.1.0"
|
||||
split-on-first "^1.0.0"
|
||||
strict-uri-encode "^2.0.0"
|
||||
|
||||
querystring@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
|
||||
Reference in New Issue
Block a user