Google OAuth client authorization error on all devices except my local development machine - node.js

I am developing a full stack Node.js web application (MERN Stack based) which has a Google OAuth login system. I successfully deployed the application to Heroku and it is working completely fine on my development Windows Laptop. Google's choose account window successfully pops up, I select an account and get redirected back to dashboard (not completed yet but it is successfully logging in a user with all the data successfully getting updated to MongoDB as well. I even checked on different browsers and tried incognito mode too, the OAuth flow is working correctly as it should but the problem is that I tried opening the website on my mobile device and on clicking the login to google button (I'll add the app link at bottom) it says Authorization Error, Error 400: redirect_uri_mismatch. Now I know that my redirect uri should be updated in developer console dashboard but I have triple checked it is what my instructor told me, otherwise why would it work on my local machine as well?? I'm definitely missing something here that the OAuth fails on all devices except my laptop. Any help would be really appreciated.
App link: https://dry-springs-04824.herokuapp.com
Passport.js file:
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const mongoose = require('mongoose');
const keys = require('../config/keys');
const User = mongoose.model('users');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
passport.use(new GoogleStrategy({
clientID: keys.googleClientID,
clientSecret: keys.googleClientSecret,
callbackURL: '/auth/google/callback',
proxy: true
}, async (accessToken, refreshToken, profile, done) => {
const existingUser = await User.findOne({ googleId: profile.id });
if(existingUser) {
done(null, existingUser);
} else {
const user = await new User({ googleId: profile.id }).save()
done(null, user);
}
})
);
I am still really confused why it works on my local machine, if it has to fail, it should not work anywhere right?
P.S. I am not testing the website on development mode everything is done from the production Heroku hosted app considering the redirect uri mismatch issue.
Thanks again for any help.

Related

Linkedin Login API returning Status 500: Internal Server Error

been stuck for a while trying to implement LinkedIn Login using the passport framework on nodejs with passport-linkedin-oauth2 strategy.
These are my configurations for the strategy on nodejs
const strategy = new LinkedInStrategy(
{
clientID: LINKEDIN_KEY,
clientSecret: LINKEDIN_SECRET,
callbackURL: BASE_URL + "/auth/linkedin/callback",
scope: ["r_emailaddress", "r_liteprofile"],
},
(
accessToken,
refreshToken,
profile,
done
) => {
process.nextTick(() => {
return done(null, profile);
});
}
)
The callback route defined so:
router.get(
"/auth/linkedin/callback",
passport.authenticate("linkedin",{
successRedirect: "/",
failureRedirect: "/auth/linkedin",
})
);
In the dev environment on http://localhost:3000, this works perfectly fine - image showing redirection success. After a login success, I get redirected to localhost:3000.
In production however, hosted at https://www.(example_website).com, the redirection throws a code 500 internal server error - image showing redirection failure.
I'm lost as to why the issue is happening for the production site. Really appreciate any clues or help to debug this!

How to manage OAuth in Node-Express / Vue web app?

I'm a bit confused as to how to manage the OAuth flow in my application. I can make it work, but I'm unsure of the best practices, and would like to find some good articles/tutorials/documentation on that topic.
My app is structured as follows:
A Vue front-end that makes HTTP requests via axios to a back-end
A Node.js / Express back-end that uses Passport.js allowing local, Google and Facebook strategies
Passport is configured like this:
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/api/login/google/callback'
},
async (accesToken, refreshToken, profile, done) => {
try {
let user = await User.findOne({ googleId: profile.id });
if (user) {
if (
user.email != profile.emails[0].value ||
user.pic != profile.photos[0].value
) {
user.email = profile.emails[0].value;
user.pic = profile.photos[0].value;
user.save();
}
return done(null, user);
} else {
user = await User.findOne({
email: profile.emails[0].value
});
if (user) done(new UserError('existing-user'));
const newUser = await new User({
googleId: profile.id,
email: profile.emails[0].value,
name: profile.displayName,
pic: profile.photos[0].value
}).save();
return done(null, newUser);
}
} catch (e) {
return done(e);
}
}
)
);
And these are the auth methods in my login component:
methods: {
async login() {
await authClient.login(this.credentials);
},
async googleLogin() {
window.open('/api/login/google', 'loginPopup', 'menubar=on');
// window.location.href = '/api/login/google';
},
async facebookLogin() {
window.location.href = '/api/login/facebook';
},
async requestResetToken() {
await userClient.requestResetToken({
email: this.emailReset
});
}
}
My confusion comes from the fact that in order to start the OAuth flow, I need to actually leave my Vue app by linking to /api/login/google, which redirects to the Google OAuth page. Once the OAuth is completed, I'm not redirected to my Vue app but to the Node back-end (via the callback setup in Passport config).
One way to make it work is to open that OAuth flow in a popup window, track through my Vue app the content of that window, and as soon as I get the user object I close the window and login the user in the front-end. But somehow that doesn't seem quite right.
Should I find a way to actually have that callback function redirect to my Vue app and get the Vue app to deal with it? Any good resource online with examples to understand that clearly?
Thanks for your help!
:) Hello,
For an one page web app you should use Authorization Code Flow and with Passport after an oauth attempt you should have a middleware that will redirect you in case of failure or success : Passport Documentation:
And a good exemple for me was this Traversy Media - backend app with oAuth
I don't think that passport is the problem, the flow of the token is in your case...

OAuth Flow redirecting to "Unauthorized" page

In my web app, I use Spotify's OAuth flow to authenticate a user. I store the user's information in my database like this.
passport.use(
new SpotifyStrategy({
clientID,
clientSecret,
callbackURL,
proxy: true
}, async (accessToken, refreshToken, profile, done) => {
const user = await User.findOne({ spotifyId: profile.id });
if (user) {
user.accessToken = accessToken;
user.refreshToken = refreshToken;
const replace = await user.save();
return done(null, replace);
}
const newUser = await new User({spotifyId: profile.id, accessToken, refreshToken}).save();
return done(null, user);
})
In other words, if my user already exists in my database, then all I want to do is update the access/refresh token. If the user does not exist, then I want to create a new user document.
My problem occurs when a first-time user logs into my web app. I noticed that because this first-time user is not in my database, the redirect-url will go to a page that simply says unauthorized. However, this login-attempt will put the user's information into the database. So, even though the user initially went to the unauthorized page, if he or she tries to login again, it will work successfully. Likewise, any user that has already visited my website before will have no trouble logging in again.
So, if a user's information is not already in the database, they will be redirected to the unauthorized page when they go to the route '/auth/spotify/callback', and if they are already in, then the website will work as normal.
I can't figure out why this is happening. I initially thought that there might be a page that requires the authorization token, but because my user might not have one yet, it says unauthorized. But, I did some testing and found out that this is not the reason. I also tried redirecting to a page where the authorization token is not needed, and it still messes up. I also thought maybe I spelled the auth callback route incorrectly in the app, but I'm sure I spelled it correctly.
Here are my auth routes
app.get('/auth/spotify',
passport.authenticate('spotify', {
scope: ['playlist-read-collaborative', 'playlist-read-private',
'user-read-playback-state', 'user-modify-playback-state',
'user-read-currently-playing', 'streaming']
}),
(req, res) => {
}
)
app.get('/auth/spotify/callback',
passport.authenticate('spotify'),
(req, res) => {
res.redirect('/');
});
If anyone has an idea of how to fix this bug, I will greatly appreciate it. Thank you!
I'm an idiot. At the bottom, I should be returning newUser, not user.

Handling authentication in Nodejs with passport-facebook-token, request coming from frontend Facebook SDK

I am working on a Unity App. For login, there are two methods, one using Email and another using Facebook. In case of login separately, I do not have any problem. Registration and Login with Email works perfectly. And Login with Facebook works perfectly as well. Here's the workflow, I created just to make you clear.
tl;dr [read update]
There's another schema for account, which is used for login.
var Account = new Schema({
email: String,
password: String,
facebookId: String
});
Things to know about the backend API.
Passport is used for Authentication
Successful login returns email and token to the client through API.
On client, token is most to play game and use the overall features.
As I said, I have already covered the part when if a client registers and login using email, then client can use the app. But my confusion is handling the logins with Facebook. Facebook SDK is already integrated with the Unity App, and Login is success.
Now, how can I use the Facebook login information that is generated by the Facebook SDK onto my back end, so that I can authorize the user throughout the system, as done in email login.
Going through other questions in SO and Google, I came across passport-facebook-token, I also tried using the plugin but could not came up with the logic and flow for handling the data from SDK into the Nodejs API. Can someone me help understand how it is done?
Update 1: Using passport-facebook-token
Strategy on index.js
passport.use(new FacebookTokenStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET
}, function(accessToken, refreshToken, profile, done) {
Account.findOrCreate({facebookId: profile.id}, function (error, user) {
return done(error, user);
});
}
));
Controller API
api.post('/auth/facebook/token',
passport.authenticate('facebook-token'),
function (req, res) {
console.log(req.user);
// do something with req.user
res.sendStatus(req.user? 200 : 401);
}
);
Now, there is no error shown, but the data is not inserted into Account Schema, I have this findOrCreate() function in Model.
Account.statics.findOrCreate = function findOrCreate(profile, cb){
var userObj = new this();
this.findOne({facebookId : profile.id},function(err,result){
if(!result){
userObj.facebookId = profile.id;
//....
userObj.save(cb);
}else{
cb(err,result);
}
});
};
you can use facebook-passport for that, you can check the documentation here: https://github.com/jaredhanson/passport-facebook but basically, after you have already set up your developer account and got your keys from the developer site of facebook you can implement a FacebookStrategy object like following where you have to specify your credential and also a callback that in the documentation example is an http request to another resource of an express server where you can then save the data to mongo
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ facebookId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));

Using PassportJS google authentication with the nodejs google api library

I'm currently looking at implementing a google api, using the nodejs client:
https://github.com/google/google-api-nodejs-client/
I'm trying to use passport in order to authenticate, which seems to be working#
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/callback"
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
var user = {
id: profile.id,
email: profile.email,
firstName: profile.given_name,
lastName: profile.family_name,
accessToken: accessToken
};
return done(null, user);
});
}
));
In my google auth callback:
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
//do something here with the google api
});
I can get hold of req.user, and this has the accessToken
However, the docs for the Google api nodejs client aren't clear on how to use an accessToken.
The example included shows the OAauth2Client retrieving a token, but I guess that part has already been covered using Passport?
I'm not familiar with google-api-nodejs-client, but this is in their documentation:
oauth2Client.credentials = {
access_token: 'ACCESS TOKEN HERE',
refresh_token: 'REFRESH TOKEN HERE'
};
client
.plus.people.get({ userId: 'me' })
.withAuthClient(oauth2Client)
.execute(callback);
I assume you can just set the credentials to those provided by Passport, and things will work fine.
Honestly, though, I find these API wrappers really contrived, and recommend just using request. That way you can access any API service from any provider using a familiar module.

Resources