"Log In With PayPal" fails with connection reset? - node.js

I have been attempting to use the passport-oauth2 module to use the Log In With Paypal service. At this point, I'm only working with the sandbox and not production (yet).
I am successfully using the passport-google-oauth and passport-amazon to authenticate with each of those services via oauth2. Given this, I'm confused about what I'm doing wrong with PayPal.
With PayPal, I can't get the sandbox to work at all. I get a login screen that takes credentials, but after entering them I am getting a connection reset error every time. I have tried logging out of all PayPal sessions, closing browsers, etc. Despite my client-side fiddling, I never make it back from PayPal to my callback.
So...to the code!
I have a configuration object for development:
"paypal" : {
"clientID": "copied from the developer My REST Apps test credentials",
"clientSecret" : "more copied test credentials",
"callbackURL" : "https://localhost/auth/paypal/cb",
"authorizationURL": "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
"tokenURL": "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice"
}
And I have a little library that does this:
var PayPalStrategy = require('paypal-oauth2').Strategy;
exports.paypalStrategy = function (conf) {
// 'conf' is the configuration from the first code block
return new PaypalStrategy({
clientID: conf.clientID,
clientSecret: conf.clientSecret,
callbackURL: conf.callbackURL,
authorizationURL : conf.authorizationURL,
tokenURL: conf.tokenURL
},
function (accessToken, refreshToken, profile, done) {
// we never get here because our connection is reset.
});
};
And my routes are simple:
app.get('/auth/paypal',
passport.authenticate('oauth2', {scope: 'profile'}),
function (req, res) {}
);
app.get('/auth/paypal/cb',
passport.authenticate('oauth2', { failureRedirect: '/login' }),
function (req, res) {
res.redirect('/profile');
});
My guess is that I'm (a) getting something wrong with the endpoints or (b) I'm missing some step with the test account configuration on PayPal's dev site. Sadly, I can't seem to find what I need in the docs. Any help or guidance is appreciated.

Mine worked with the following:
1) Clear browser cache and history.
2) Delete cookies.
And try again.

After a few hours I mucking around with passport, I ended up moving towards using the node-palpal-sdk instead. I think what makes it confusing is how paypals oauth flows talk about openid on the endpoints urls. Here's a gist of how I got it working, straight forward after you put the pieces together:
https://gist.github.com/jkresner/8a581e50dcac58edbd10

Related

Ouath Social Logins in React JS and Node(Express) with different sub domains

I have created an app in React and Node(Express) which seems to be working fine on local but I am facing issues on hosting them.
I have option to login using OAuth2(Google, LinkedIn, Twitter, GitHub). I have hosted the client on front.domain.io and server on api.domain.io. When I am clicking on login with google it is redirecting me to an invalid URL. Can someone help me to solve this. Below is my code and the expected URL and current redirection happening.
React Code which redirects to google login page :
const googleReqHandler = () => {
window.open(`${APIURL}/auth/google`, "_self");
};
Node Js Code to handle the redirection and callback
router.get(
"/google",
passport.authenticate("google", { scope: ["profile", "email"] })
);
router.get(
"/google/callback",
passport.authenticate("google", {
successRedirect: process.env.CLIENT_URL,
failureRedirect: "/login/failed",
})
);
Passport JS config
passport.use(
new GoogleStrategy(
{
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: `${process.env.APP_URL}/auth/google/callback`,
},
async function (accessToken, refreshToken, profile, done) {
const userProfile = profile;
await usersHelper.createOrUpdate(userProfile);
done(null, userProfile);
}
)
);
URLs that I am using in React And Node
APIURL : https://api.domain.io
CLIENT_URL : https://front.domain.io
APP_URL : https://api.domain.io
The URL that the user is being redirected from when the user clicks Login with Google button
https://api.domain.io/o/oauth2/v2/auth?response_type=code&redirect_uri=https%3A%2F%2Fapi.domain.io%2Fauth%2Fgoogle%2Fcallback&scope=profile%20email&client_id=clientid
The URL it should have been redirected to
https://accounts.google.com/o/oauth2/v2/auth/identifier?response_type=code&redirect_uri=https%3A%2F%2Fapi.domain.io%2Fauth%2Fgoogle%2Fcallback&scope=profile%20email&client_id=clientid&flowName=GeneralOAuthFlow
I have configured the OAuth in all the developer consoles properly, it is working fine for all the logins on local.
I think that is issue is due to requests generation and the redirection from different subdomains where as on local it is same domain just different port(localhost:3000 and localhost:5000). So can someone help me to solve this issue. Thank you in advance.
So after not able to find much on the issue, I hosted the backend(node) using heroku and frontend(react) using netlify, after I found the video mentioned below after going through n number of videos and blogs, and everything worked fine. So I had a talk with the devops so got to know that most probably the issue was due to URL rewrites they had used in the IIS on windows server. So we hosted the application on ubuntu using nginx and then everything is working fine. So if any one faces such an issue do check for such URL rewrites if you are hosting it on windows.
Also make sure that the site is hosted on https, as http can give issues when on public domain(Someone had mentioned that in his video). Do add the following lines to your app.js in backend too or else they won't pass session cookies during login process when hosted on public domain with different domains.
app.use(
session({
secret: "whatever",
resave: true,
saveUninitialized: true,
cookie: {
sameSite: "none",
secure: true,
},
})
);
Here is the link of the video, which helped me to identify this issue and make required changes before hosting such setup as no other video which I referred to showed the hosting process.
Youtube video link

How is passport receiving the profile?

I have implemented passport with GitHub-Strategy. Here's the Glith.
It works wonderfully and I'm receiving the user-profile on redirect from GitHub.
Now, I'm only trying to understand how this actually works 'under the hood'.
I did not find any similar question here on stackoverflow, neither on Passport.
So if I open Chrome Developer Tools during the Auth-Flow, the following seems to be going on when I click on Login with GitHub:
the node route /auth/github is called
node redirects to https://github.com/login/oauth/authorize?response_type=code&redirect_uri=https%3A%2F%2Frightful-exclusive-carriage.glitch.me%2Fauth%2Fgithub%2Fcallback&client_id=ccfcc73fac8223317176
the user is presented with GitHub-Login-Page
User types in GitHub-credentials and clicks 'Login'
GitHub checks the credentials
If valid credentials are provided, user is authenticated and GitHub redirects to the registered callback-endpoint, which is in my case: https://rightful-exclusive-carriage.glitch.me/auth/github/callback
The callback-url has a url-parameter, e.g. ?code=02337a951c242b9202fd. It's interesting to note, that it's a GET-method and nothing else is provided.
On the server, the passport.authenticate('github', ...) method is called inside of the /auth/github/callback-route.
When the GithubStrategy is instanciated, a callback-function is passed with the signature function(accessToken, refreshToken, profile, cb). Somehow magically, the accessToken and profile are fully available here. And I don't understand how this happens.
How is passport receiving the profile?
Is node.js making a server-side call to GitHub? Maybe with the ?code=<id> ?
Yeah that is exactly what NodeJS is doing. This doesn't have anything to do with Passport.JS or Node.JS. It is the OAuth mechanism of how authorizations work.
Whenever a the Identity provider like twitter/facebook calls your /callback with a ?code= query param It then hits another url and gets the AccessToken, RefreshToken and Idtoken(which is basically the user profile).
You can check out in the source code as well:
In this strategy.js#L157 and strategy.js#L173 of passports oauth strategy:
if (req.query && req.query.code) {
....
self._oauth2.getOAuthAccessToken(code, params,
function(err, accessToken, refreshToken, params) {
....
}
....
}
And the oauth2.js#L177 and oauth2.js#L190 of node-oauth package can see that:
exports.OAuth2.prototype.getOAuthAccessToken= function(code, params, callback) {
...
this._request("POST", this._getAccessTokenUrl(), post_headers, post_data, null, function(error, data, response) {
....
})
}
Bascially a POST request is being sent to the accessTokenUrl.

Passport.js Azure AD auth always fails the second time

So i'm using Passport.js in Express to authenticate users in an app with the passport-azure-oauth2 strategy.
The first login works fine, the Microsoft portal takes me back to /cb which is
app.get("/cb", auth.passport.authenticate("provider", {
successRedirect: "/",
failureRedirect: "/login"
}), function (req, res) {
res.redirect("/");
});
And it successfully redirects me to '/'. At this point I can log out 'Logged in' via this
if(req.isAuthenticated()) {
console.log('Logged in');
} else {
console.log('Not logged in');
}
The issue comes when I either log out and try and log back in again, or try and log in with a different browser. The Microsoft portal always takes me back to '/login', leaving me in a loop.
The login script is simply:
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
On Chrome, when I log in (even successfully) it logs an error:
Set-Cookie header is ignored in response from url: https://login.microsoftonline.com/common/login. Cookie length should be less than or equal to 4096 characters.
I'm guessing the issue might be something to do with this? But i've tried everything I can think of to no avail.
it's been a while since you have asked this, but maybe it could be helpful for someone else.
After a day of debugging i found out that the service worker of my react app was messing with the passport authentication strategy.
As you have described, when you load the page for the first time the service worker is not registered yet, but when you get redirected to the homepage the script in the index.jsx enable the worker in the browser (i used it to display the install app button on chrome), so if you logout and try to login again this issue comes along.
If you are in a rush i suggest you to temporary disable the worker if it's not very important for the right functioning of your app and you'll see that everything works fine.
It works on localhost because the script detects your environment and it does not start.
Unfortunately i have not found a permanent solution yet.

Passport.js strategy fails when not using session

I'm trying to figure out how to integrate a Oauth strategy(github) to my application which uses express and websockets.
I'm following this guide which explains how to use JWT tokens instead of using the default passport sessions
https://blog.hyphe.me/token-based-authentication-with-node/
this is the code i have so far
app.use(passport.initialize())
app.get('/auth/github',passport.authenticate('github',{session:false}),serialize, generateToken, respond)
app.get('/auth/github/callback',passport.authenticate('github',{failureRedirect:'/'}),
function(req,res){
res.redirect('/')
}
)
When i try to login via github - i get the below error
Error: Failed to serialize user into session
at pass (/home/avernus/Desktop/experiments/oauth/node_modules/passport/lib/authenticator.js:271:19)
at Authenticator.serializeUser (/home/avernus/Desktop/experiments/oauth/node_modules/passport/lib/authenticator.js:289:5)
at IncomingMessage.req.login.req.logIn (/home/avernus/Desktop/experiments/oauth/node_modules/passport/lib/http/request.js:50:29)
at Strategy.strategy.success (/home/avernus/Desktop/experiments/oauth/node_modules/passport/lib/middleware/authenticate.js:235:13)
at verified (/home/avernus/Desktop/experiments/oauth/node_modules/passport-oauth2/lib/strategy.js:177:20)
at Strategy._verify (/home/avernus/Desktop/experiments/oauth/passport.js:13:12)
at /home/avernus/Desktop/experiments/oauth/node_modules/passport-oauth2/lib/strategy.js:193:24
at /home/avernus/Desktop/experiments/oauth/node_modules/passport-github/lib/strategy.js:174:7
at passBackControl (/home/avernus/Desktop/experiments/oauth/node_modules/oauth/lib/oauth2.js:125:9)
at IncomingMessage.<anonymous> (/home/avernus/Desktop/experiments/oauth/node_modules/oauth/lib/oauth2.js:143:7)
I'm not sure where exactly the problem is
this is my github strategy
passport.use(new githubStrategy({
clientID:'********',
clientSecret:'*******',
callbackURL:'http://localhost:3000/auth/github/callback'
},
function(accessToken,refreshToken,profile,done){
console.log('accessToken: ',accessToken,' refreshToken: ',refreshToken,' profile: ',profile)
return done(null,profile)
}
))
I'm able to successfully get the profile from github
the serialize function
function serialize(req, res, next) {
db.updateOrCreate(req.user, function(err, user){
if(err) {return next(err);}
// we store the updated information in req.user again
req.user = {
id: user.id
};
next();
});
}
from my experience passportjs with oauth always requires sessions to operate, despite the session: false option.
i believe the underlying oauth library dependencies look for sessions no matter what. its quite frustrating.
edit: to add more detail to this, the example you are linking to uses the default strategy, which is not oauth based. in this instance you could opt out of using sessions. you are using the github strategy which uses oauth thus requires sessions
Aren't you missing the {session:false} option in your callback?
app.get('/auth/github/callback',passport.authenticate('github',{failureRedirect:'/', session: false}),
function(req,res){
res.redirect('/')
})
Im guessing right here because I've never worked with Strategies that requires a callback. But i would imagine that passport tries to serialize the user in the callback as thats the point where you receive the profile from Github.

Passport.js: passport-facebook-token strategy, login through JS SDK and THEN authenticate passport?

I was looking for a way to let my client authorize with the facebook JS SDK and then somehow transfer this authorization to my node server (so it can verify requests with the fb graph api)
I stumbled across:
https://github.com/jaredhanson/passport-facebook/issues/26
&
https://github.com/drudge/passport-facebook-token
what seems to be an entirely different strategy from passport-facebook.
Am I correct when assuming that:
One logs in with the fb JS SDK, and then the facebook-token strategy somehow extracts the token and fb id from the document or body object?
Or is there any other decent way to achieve this? I'm namely trying to avoid the redirects enforced by the server SDKs
I've spent a couple of days this week trying to figure out the best way to use Facebook Authentication for a private API, using passport.js — passport-facebook-token is perfect for this.
You are correct in assuming these are two separate authentication strategies. You don't need passport-facebook installed to use passport-facebook-token.
If you have Facebook authentication implemented in the client-side JS (or iOS etc.), and are looking for a way to then authenticate API requests using your user's Facebook authToken, passport-facebook-token is a really elegant solution.
passport-facebook-token works totally independently of passport-facebook, and basically handles the redirects required by Facebook internally, before passing the request along to your controller.
So to authenticate an API route using passport-facebook-token, you'll need to set up a passport strategy like so:
passport.use('facebook-token', new FacebookTokenStrategy({
clientID : "123-your-app-id",
clientSecret : "ssshhhhhhhhh"
},
function(accessToken, refreshToken, profile, done) {
// console.log(profile);
var user = {
'email': profile.emails[0].value,
'name' : profile.name.givenName + ' ' + profile.name.familyName,
'id' : profile.id,
'token': accessToken
}
// You can perform any necessary actions with your user at this point,
// e.g. internal verification against a users table,
// creating new user entries, etc.
return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
}
));
It's worth noting that the User.findOrCreate method used in the passport-facebook-token Readme is not a default mongo/mongoose method, but a plugin that you'll have to install if you want it.
To use this auth strategy as middleware for any of your routes you'll need to pass it an access_token object either as a URL parameter, or as a property of the request body.
app.get('/my/api/:access_token/endpoint',
passport.authenticate(['facebook-token','other-strategies']),
function (req, res) {
if (req.user){
//you're authenticated! return sensitive secret information here.
res.send(200, {'secrets':['array','of','top','secret','information']});
} else {
// not authenticated. go away.
res.send(401)
}
}
NB. the access_token property is case-sensitive and uses an underscore.
The documentation for passport-facebook-token isn't extensive, but the source is really well commented and pretty easy to read, so I'd encourage you to take a look under the hood there. It certainly helped me wrap my head around some of the more general ways that passport works.

Resources