0

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?

1 Answers1

0

Just use the simple viewer authentication

varunpatil
  • 428
  • 1
  • 3
  • 6