@AlejandroPadilla I am adding my app.js code for your complete reference, here I am trying to build a virtual switch with the cloud connector web hook endpoints, and google oauth2.0 for the authentication, after the user authenticate successfully the app will redirect the user to a page where the switch details and the capability i.e on, off will be available, this part of code I have added in the connector.js file. now the issue is every time I am successfully authenticating with google its redirecting me to the smart things api end point with this message “unauthorized Client”. can you tell me where am I going wrong?
App.js code
“use strict”;
const fs = require(‘fs’)
require(‘dotenv’).config();
const express = require(“express”);
const session = require(“express-session”);
const passport = require(“passport”);
const GoogleStrategy = require(“passport-google-oauth20”).Strategy;
const db = require(‘./db’);
const { StateUpdateRequest, SchemaConnector } = require(‘st-schema’);
const SmartThingsConnector = require(‘./connector’); // SmartThings integration
//const { sessionMiddleware } = require(‘./redis’); // Redis database
const morgan = require(‘morgan’); // Morgan to print logs
const path = require(“path”);
const ejs = require(“ejs”);
const crypto = require(‘crypto’);
const { fetchDeviceCapabilities } = require(‘./smartThingsUtils’); // Import the utility function
const { findVirtualSwitchDeviceId } = require(‘./smartThingsUtils’);
const app = express();
const PORT = process.env.PORT || 3000;
function generateStateParameter() {
return crypto.randomBytes(16).toString(‘hex’);
}
app.set(“view engine”, “ejs”);
app.use(express.json());
app.use(morgan(“HTTP :method :url :res[location] :status :response-time ms”));
// Serve static files from the ‘public’ folder
app.use(express.static(path.join(__dirname, “public”)));
//initialize session
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: { secure: ‘auto’ },
})
);
// Initialize passport
app.use(passport.initialize());
app.use(passport.session());
passport.use(new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_REDIRECT_URI,
},
function (accessToken, refreshToken, profile, cb) {
// Use the profile information to authenticate the user
// …
cb(null, profile);
}
)
);
passport.serializeUser(function (user, cb) {
cb(null, user);
});
passport.deserializeUser(function (obj, cb) {
cb(null, obj);
});
app.get(“/login”, (req, res) => res.render(“login”));
app.get(“/dashboard”, (req, res) => req.isAuthenticated() ? res.render(“dashboard”, { user: req.user }) : res.redirect(“/login”));
app.get(“/auth/google”, passport.authenticate(“google”, { scope: [“profile”, “email”] }));
//app.get(“/auth/google/callback”,passport.authenticate(“google”, { failureRedirect: “/login” }),
//function (req, res) {
//res.redirect(“/dashboard”);
//res.redirect(“/find-virtual-switch”);
//res.redirect(“/initiate-discovery”);
// Here, instead of redirecting to /dashboard, redirect to SmartThings or another desired URL
//res.redirect(“Samsung account”);
//res.redirect(“SmartThings”);
// }
//);
app.get(“/auth/google/callback”, passport.authenticate(“google”, { failureRedirect: “/login” }), (req, res) => {
// Here, check if the user has already linked SmartThings and has a valid token
if (req.session.smartThingsAccessToken) {
res.redirect(“/initiate-discovery”);
} else {
// User is authenticated with Google but hasn’t linked SmartThings yet
res.redirect(“/auth/smartthings”);
}
});
function getUserSmartThingsAccessToken(req) {
// Check if the session exists and contains the SmartThings access token
if (req.session && req.session.smartThingsAccessToken) {
return req.session.smartThingsAccessToken;
} else {
// Log an error or handle the case where the token is not available
console.error(‘SmartThings access token not found in session.’);
return null;
}
}
app.get(‘/auth/smartthings’, (req, res) => {
if (!req.isAuthenticated()) {
return res.redirect(‘/login’);
}
const smartThingsAuthUrl = ‘Samsung account’ + new URLSearchParams({
response_type: ‘code’,
client_id: process.env.ST_CLIENT_ID,
scope: ‘r:devices:*’,
redirect_uri: process.env.ST_REDIRECT_URI,
state: ‘some_random_state’ // Ensure this is securely generated
});
res.redirect(smartThingsAuthUrl);
});
app.get(‘/smartthings/oauth/callback’, async (req, res) => {
const { code } = req.query; // The authorization code from SmartThings
try {
const response = await axios.post(‘https://api.smartthings.com/oauth/token’, {
client_id: process.env.ST_CLIENT_ID,
client_secret: process.env.ST_CLIENT_SECRET,
code: code,
grant_type: ‘authorization_code’,
redirect_uri: process.env.ST_REDIRECT_URI,
});
const { access_token } = response.data;
req.session.smartThingsAccessToken = access_token; // Store the token in the session
res.redirect("/initiate-discovery");
} catch (error) {
console.error(“Failed to exchange SmartThings code for access token:”, error);
res.redirect(“/login”);
}
});
// Assuming ‘app’ is your Express app instance and SmartThingsConnector is properly imported
app.get(‘/initiate-discovery’, async (req, res) => {
if (!req.isAuthenticated()) {
return res.status(401).send(‘Unauthorized’);
}
const accessToken = getUserSmartThingsAccessToken(req);
if (!accessToken) {
return res.status(500).send(‘Failed to retrieve SmartThings access token.’);
}
console.log(“Successfully retrieved SmartThings access token:”, accessToken);
// Assuming you have an endpoint to initiate discovery in SmartThings and expecting a structure for devices
try {
// Replace yourSmartThingsDiscoveryEndpoint
with the actual SmartThings endpoint for device discovery
const discoveryResponse = await axios.get(‘yourSmartThingsDiscoveryEndpoint’, {
headers: { ‘Authorization’: Bearer ${accessToken}
}
});
console.log("accesstoken - ", accessToken);
// Assuming the response body contains an array of devices
const devices = discoveryResponse.data.devices.map(device => {
return {
externalDeviceId: device.externalDeviceId,
deviceType: device.deviceType,
deviceId: device.deviceId,
manufacturerName: device.manufacturerName,
modelName: device.modelName,
components: device.components, // Adjust based on actual response structure
};
});
console.log("[/initiate-discovery] Retrieved devices:", devices);
// Render the discovery view with the devices or send them as a response
res.render("discovery", { devices: devices });
// OR for API-like response:
// res.json({ devices: devices });
} catch (error) {
console.error(“Error during SmartThings device discovery:”, error);
res.status(500).send(“Error initiating device discovery process.”);
}
});
//removed the virtual switch fucntion check sublime
app.get(“/logout”, (req, res) => {req.logout(function (err) {
if (err) {
console.log(err);
} else {
res.redirect(“/login”);
}
});
});
app.post(‘/st/discovery’, (req, res) => {
SmartThingsConnector.discoveryHandler(req.headers.authorization, req.body)
.then((discoveryResponse) => {
res.json(discoveryResponse);
})
.catch((err) => {
console.error(err);
res.status(500).json({ error: ‘Discovery process failed’ });
});
});
// Endpoint for SmartThings to refresh device state
app.post(‘/st/state’, (req, res) => {
SmartThingsConnector.stateRefreshHandler(req.headers.authorization, req.body)
.then((stateRefreshResponse) => {
res.json(stateRefreshResponse);
})
.catch((err) => {
console.error(err);
res.status(500).json({ error: ‘State refresh process failed’ });
});
});
// Endpoint for SmartThings to send commands to devices
app.post(‘/st/command’, (req, res) => {
SmartThingsConnector.commandHandler(req.headers.authorization, req.body)
.then((commandResponse) => {
res.json(commandResponse);
})
.catch((err) => {
console.error(err);
res.status(500).json({ error: ‘Command process failed’ });
});
});
app.listen(PORT, () => console.log(Server listening on http://127.0.0.1:${PORT}
));