I've been working on my test project to create node.js 2-legged OAuth Forge Hubs Browser with BIM 360 Docs. I first developed the 3-legged OAuth app along with the tutorial document published by Autodesk(https://tutorials.autodesk.io/tutorials/hubs-browser/).
What changes do I need to do to make 3-legged OAuth Hubs into 2-legged OAuth Hubs?
I made this changes.
1: deleted routes/auth.js
2: modified routes/hubs.js
/** @format */
const express = require("express");
const {
authRefreshMiddleware,
getHubs,
getProjects,
getProjectContents,
getItemVersions,
} = require("../services/aps.js");
let router = express.Router();
// router.use("/api/hubs", authRefreshMiddleware);
router.get("/api/hubs", async function(req, res, next) {
try {
const hubs = await getHubs( /*req.internalOAuthToken*/ );
res.json(hubs);
} catch (err) {
next(err);
}
});
router.get("/api/hubs/:hub_id/projects", async function(req, res, next) {
try {
const projects = await getProjects();
/*
req.params.hub_id,
req.internalOAuthToken
*/
res.json(projects);
} catch (err) {
next(err);
}
});
router.get(
"/api/hubs/:hub_id/projects/:project_id/contents",
async function(req, res, next) {
try {
const contents = await getProjectContents();
/*
req.params.hub_id,
req.params.project_id,
req.query.folder_id,
req.internalOAuthToken
*/
res.json(contents);
} catch (err) {
next(err);
}
}
);
router.get(
"/api/hubs/:hub_id/projects/:project_id/contents/:item_id/versions",
async function(req, res, next) {
try {
const versions = await getItemVersions();
/*
req.params.project_id,
req.params.item_id,
req.internalOAuthToken
*/
res.json(versions);
} catch (err) {
next(err);
}
}
);
module.exports = router;
3: modified services/aps.js
/** @format */
const APS = require("forge-apis");
const {
APS_CLIENT_ID,
APS_CLIENT_SECRET,
APS_TOKEN_SCOPES,
APS_CALLBACK_URL,
INTERNAL_TOKEN_SCOPES,
PUBLIC_TOKEN_SCOPES,
} = require("../config.js");
/*
const internalAuthClient = new APS.AuthClientThreeLegged(
APS_CLIENT_ID,
APS_CLIENT_SECRET,
APS_CALLBACK_URL,
INTERNAL_TOKEN_SCOPES
);
*/
const internalAuthClient = new APS.AuthClientTwoLegged(
APS_CLIENT_ID,
APS_CLIENT_SECRET,
APS_CALLBACK_URL,
APS_TOKEN_SCOPES
);
const service = (module.exports = {});
service.initialize = async function() {
try {
internalCredentials = await internalAuthClient.authenticate();
} catch (error) {
console.error("Failed to authenticate:", error);
}
};
service.authRefreshMiddleware = async(req, res, next) => {
if (!internalCredentials || !internalCredentials.access_token) {
try {
internalCredentials = await internalAuthClient.authenticate();
} catch (error) {
console.error("Failed to authenticate:", error);
return res
.status(500)
.send("Failed to authenticate with Autodesk Forge");
}
}
next();
};
/*
const publicAuthClient = new APS.AuthClientThreeLegged(
APS_CLIENT_ID,
APS_CLIENT_SECRET,
APS_CALLBACK_URL,
PUBLIC_TOKEN_SCOPES
);
*/
//service.getAuthorizationUrl = () => internalAuthClient.generateAuthUrl();
/*
service.authCallbackMiddleware = async(req, res, next) => {
const internalCredentials = await internalAuthClient.getToken(
req.query.code
);
const publicCredentials = await publicAuthClient.refreshToken(
internalCredentials
);
req.session.public_token = publicCredentials.access_token;
req.session.internal_token = internalCredentials.access_token;
req.session.refresh_token = publicCredentials.refresh_token;
req.session.expires_at = Date.now() + internalCredentials.expires_in * 1000;
next();
};
service.authRefreshMiddleware = async(req, res, next) => {
const { refresh_token, expires_at } = req.session;
if (!refresh_token) {
res.status(401).end();
return;
}
if (expires_at < Date.now()) {
const internalCredentials = await internalAuthClient.refreshToken({
refresh_token,
});
const publicCredentials = await publicAuthClient.refreshToken(
internalCredentials
);
req.session.public_token = publicCredentials.access_token;
req.session.internal_token = internalCredentials.access_token;
req.session.refresh_token = publicCredentials.refresh_token;
req.session.expires_at =
Date.now() + internalCredentials.expires_in * 1000;
}
req.internalOAuthToken = {
access_token: req.session.internal_token,
expires_in: Math.round((req.session.expires_at - Date.now()) / 1000),
};
req.publicOAuthToken = {
access_token: req.session.public_token,
expires_in: Math.round((req.session.expires_at - Date.now()) / 1000),
};
next();
};
service.getUserProfile = async(token) => {
const resp = await new APS.UserProfileApi().getUserProfile(
internalAuthClient,
token
);
return resp.body;
};
*/
service.getHubs = async(token) => {
const resp = await new APS.HubsApi().getHubs(
null,
internalAuthClient,
internalCredentials.access_token
);
return resp.body.data;
};
service.getProjects = async(hubId, token) => {
const resp = await new APS.ProjectsApi().getHubProjects(
hubId,
null,
internalAuthClient,
internalCredentials.access_token
);
return resp.body.data;
};
service.getProjectContents = async(hubId, projectId, folderId, token) => {
if (!folderId) {
const resp = await new APS.ProjectsApi().getProjectTopFolders(
hubId,
projectId,
internalAuthClient,
internalCredentials.access_token
);
return resp.body.data;
} else {
const resp = await new APS.FoldersApi().getFolderContents(
projectId,
folderId,
null,
internalAuthClient,
internalCredentials.access_token
);
return resp.body.data;
}
};
service.getItemVersions = async(projectId, itemId, token) => {
const resp = await new APS.ItemsApi().getItemVersions(
projectId,
itemId,
null,
internalAuthClient,
internalCredentials.access_token
);
return resp.body.data;
};
4: deleted login buttons in wwwroot/index.html
5: modified wwwroot/main.js
/** @format */
let {
APS_CLIENT_ID,
APS_CLIENT_SECRET,
APS_CALLBACK_URL,
SERVER_SESSION_SECRET,
APS_TOKEN_SCOPES,
PORT,
} = process.env;
if (!APS_CLIENT_ID ||
!APS_CLIENT_SECRET ||
!APS_CALLBACK_URL ||
!SERVER_SESSION_SECRET
) {
console.warn("Missing some of the environment variables.");
process.exit(1);
}
/*
const INTERNAL_TOKEN_SCOPES = ["data:read"];
const PUBLIC_TOKEN_SCOPES = ["viewables:read"];
*/
PORT = PORT || 8080;
module.exports = {
APS_CLIENT_ID,
APS_CLIENT_SECRET,
APS_CALLBACK_URL,
SERVER_SESSION_SECRET,
APS_TOKEN_SCOPES,
PORT,
};
6: modified config.js
/** @format */
let {
APS_CLIENT_ID,
APS_CLIENT_SECRET,
APS_CALLBACK_URL,
SERVER_SESSION_SECRET,
APS_TOKEN_SCOPES,
PORT,
} = process.env;
if (!APS_CLIENT_ID ||
!APS_CLIENT_SECRET ||
!APS_CALLBACK_URL ||
!SERVER_SESSION_SECRET
) {
console.warn("Missing some of the environment variables.");
process.exit(1);
}
/*
const INTERNAL_TOKEN_SCOPES = ["data:read"];
const PUBLIC_TOKEN_SCOPES = ["viewables:read"];
*/
PORT = PORT || 8080;
module.exports = {
APS_CLIENT_ID,
APS_CLIENT_SECRET,
APS_CALLBACK_URL,
SERVER_SESSION_SECRET,
APS_TOKEN_SCOPES,
PORT,
};
7: modified server.js:
/** @format */
const express = require("express");
const session = require("cookie-session");
require("dotenv").config();
const { PORT, SERVER_SESSION_SECRET } = require("./config.js");
let app = express();
app.use(express.static("wwwroot"));
app.use(
session({ secret: SERVER_SESSION_SECRET, maxAge: 24 * 60 * 60 * 1000 })
);
const apsService = require("./services/aps.js");
const { authRefreshMiddleware } = require("./services/aps.js");
app.get("/api/auth/token", authRefreshMiddleware, async(req, res) => {
res.json(internalCredentials);
});
app.use(authRefreshMiddleware);
app.use(require("./routes/hubs.js"));
(async() => {
await apsService.initialize();
app.listen(PORT, () => console.log(`Server listening on port ${PORT}...`));
})();
This is my .env file (I hide some variables):
APS_CLIENT_ID=
APS_CLIENT_SECRET=
APS_CALLBACK_URL=http://localhost:8080/api/auth/callback
SERVER_SESSION_SECRET=
APS_TOKEN_SCOPES=[
"data:read",
"data:create",
"data:write",
"bucket:create",
"bucket:read",
"viewables:read",
"viewables:write",
"account:read",
"data:search",
]
This is my error messages:
Uncaught Error h scope is not allowed
at validateScope (/Users/ayumuNew/Desktop/RCI Dev/forge-hub/node_modules/forge-apis/src/auth/OAuth2.js:45:7)
at OAuth2 (/Users/ayumuNew/Desktop/RCI Dev/forge-hub/node_modules/forge-apis/src/auth/OAuth2.js:146:20)
at OAuth2TwoLegged (/Users/ayumuNew/Desktop/RCI Dev/forge-hub/node_modules/forge-apis/src/auth/OAuth2TwoLegged.js:64:10)
at <anonymous> (/Users/ayumuNew/Desktop/RCI Dev/forge-hub/services/aps.js:20:28)
at Module._compile (node:internal/modules/cjs/loader:1254:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
at Module.load (node:internal/modules/cjs/loader:1117:32)
at Module._load (node:internal/modules/cjs/loader:958:12)
at Module.require (node:internal/modules/cjs/loader:1141:19)
at require (node:internal/modules/cjs/helpers:110:18)
at <anonymous> (/Users/ayumuNew/Desktop/RCI Dev/forge-hub/server.js:13:20)
at Module._compile (node:internal/modules/cjs/loader:1254:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
at Module.load (node:internal/modules/cjs/loader:1117:32)
at Module._load (node:internal/modules/cjs/loader:958:12)
at executeUserEntryPoint (node:internal/modules/run_main:81:12)
at <anonymous> (node:internal/main/run_main_module:23:47)
I assume that I have to specify what BIM 360 account exactly I want to login as but I don't know how to do so.
I remember when creating a Forge app custom Integration on BIM 360 admin, it also has my BIM360 account ID of a developer. I wonder if it automatically grant the app this BIM 360 id or do I need to specify x-user-id?