I've built a web app that has a backend express server which handles oauth authentication via passport.
I'm now trying to build an accompanying browser extension that will share a session with the web app via the express server.
There is one way I've done it that seems to work, which is actually opening the web app's oauth flow in a new window. The authentication technically gets done through the web app, but the extension ends up being able to get the auth status from the server so this "works".
const signIn= () => {
chrome.windows.create({
url: "http://localhost:3000/auth/google-ext",
});
};
What I don't like about this solution is that it minimizes the extension.
I've tried doing the following using chrome.identity.launchWebAuthFlow and calling my same oauth endpoint I would use from the webapp.
chrome.identity.launchWebAuthFlow(
{
url: "http://localhost:5000/auth/google-ext",
interactive: true,
},
(responseUrl) => {
fetchUser();
}
);
This opens up the oauth sign in page, but after attempting to sign in, I get this error on the extension side:
Unchecked runtime.lastError: Authorization page could not be loaded.
And on my server I am getting a
TokenError: Unauthorized
Here are my express routes and passport strategies:
NOTE: when using the windows.create method, I would use the google client ID for my web app. For the launchWebAuthFlow method, I use my google extension client ID (both the web app and the extension are under the same google project).
// Extension specific routes
router.get(
"auth/google-ext",
passport.authenticate("google-ext", {
scope: ["profile", "email"],
})
);
router.get(
"auth/google-ext/callback",
passport.authenticate("google-ext", {
failureFlash: true,
}),
(req, res) => {
res.redirect("https://<app-id>.chromiumapp.org/");
}
);
// passport.js
passport.use(
"google-ext",
new GoogleStrategy(
{
clientID: googleExtensionClientID,
clientSecret: googleClientSecret,
callbackURL: "/auth/google-ext/callback",
proxy: true,
},
async (accessToken, refreshToken, profile, done) => {
const existingUser = await User.findOne({ googleId: profile.id });
if (existingUser) {
return done(null, existingUser);
}
const user = await new User({
name: profile.name,
email: profile.emails[0].value,
googleId: profile.id,
}).save();
done(null, user);
}
)
);
I've been trying to figure this out for awhile but have been unable to figure it out. Is what I am trying to do even possible? Do I have use google's getAuthToken instead?
EDIT:
It appears if I use my web app client ID for the passport strategy, the oauth flow works correctly, and my /auth/google-ext/callback is receiving a req.user. However, the session does not appear to be getting saved anywhere, ie, a follow up request to retrieve the req.user results in a null/undefined.
Here is my server index.js
const app = express();
app.use(
cors({ origin: "chrome-extension://<app-id>" })
);
app.use(express.json());
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [cookieKey],
})
);
app.use(passport.initialize());
app.use(passport.session());