InternalOAuthError when using passport-linkedin strategy - node.js

I'm working with passport-linkedin and I'm getting this error.
InternalOAuthError: failed to obtain request token
at /Users/davey/code/node/appify-frontend/node_modules/passport-linkedin/node_modules/passport-oauth/lib/passport-oauth/strategies/oauth.js:196:36
at /Users/davey/code/node/appify-frontend/node_modules/passport-linkedin/lib/passport-linkedin/strategy.js:80:19
at passBackControl (/Users/davey/code/node/appify-frontend/node_modules/passport-linkedin/node_modules/passport-oauth/node_modules/oauth/lib/oauth.js:397:13)
at IncomingMessage.<anonymous> (/Users/davey/code/node/appify-frontend/node_modules/passport-linkedin/node_modules/passport-oauth/node_modules/oauth/lib/oauth.js:409:9)
at IncomingMessage.emit (events.js:129:20)
at _stream_readable.js:908:16
at process._tickCallback (node.js:355:11)
GET /oauth/linkedin 500 1074ms - 786b
Here's what my setup looks like
exports.setup = function (config) {
var passport = require('passport');
var LinkedinStrategy = require('passport-linkedin').Strategy;
passport.use(new LinkedinStrategy({
consumerKey: config.linkedin.clientID,
consumerSecret: config.linkedin.clientSecret,
callbackURL: config.linkedin.callbackURL
},
function(token, tokenSecret, profile, done) {
console.log(token, tokenSecret, profile);
return done(null, true);
}
));
};
and my routing setup is as follows
router
.get('/',
function(req, res, next) {
console.log("[OAuth2:redirect:query]:", JSON.stringify(req.query));
console.log("[OAuth2:redirect:body]:", JSON.stringify(req.body));
next();
},
passport.authenticate('linkedin', {
failureRedirect: '/settings/connected-accounts',
session: false
}))
.get('/callback', passport.authenticate('linkedin', {
failureRedirect: '/settings/connected-accounts',
session: false
}), function (req, res){
res.redirect('/settings/connected-accounts');
});
In the same app, I've setup twitter and facebook oauth, both of which work very well. I have no idea what's causing this error and have tried everything.
In my linkedin developer account, I've configured everything as should be.
Authorized Redirect URLs:
http://testdomain.ngrok.io/oauth/linkedin/callback
Default "Accept" Redirect URL:
http://testdomain.ngrok.io/settings/connected-accounts
I first used a localhost url running on port 9000, but when that failed, I exposed my app running locally to a live url, but I still had the same error.
Any help is appreciated. Thanks

The InternalOAuthError is related to passport-oauth1 ( https://github.com/jaredhanson/passport-linkedin/blob/master/lib/strategy.js ) , but you use OAuth2 according to your routing setup. So try switching to passport-linkedin-oauth2

Related

How can I test my Backend API against google Oauth2?

My goal is to create an authentication backend and I would like to implement google's Oauth2, and for that I decided to follow the passport documentation. My only issue is, how can I test this on my Postman? I'm just developing a backend and I don't know if it's working, I know it sounds a little silly but for a beginner like me it's a lot. Thanks
const dotenv = require('dotenv').config();
const express = require('express');
const passport = require('passport');
var GoogleStrategy = require('passport-google-oauth20').Strategy;
const app = express();
const port = process.env.PORT;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile'] }));
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
try {
app.listen(port);
console.log(`Server starting on port ${port}`);
} catch (err) {
console.log(err);
}
Whichever OAuth2 service is pretty simply, if you step into it ;)
In first step you send client_id and client_secret to authorization uri.
In second step, the user authenticates with his credentials and you receive the code which you exchange with authorization server for token.
You make request with Bearer token.
Postman can handle some of the things for you. So:
In Google Dev Console create your credentials.
As callback uri fill in:
https://www.getpostman.com/oauth2/callback
https://oauth.pstmn.io/v1/callback
In Postman create new Collection and define following variables:
Define following Authorization:
When you click Get New Access Token you will be asked to login to Google and get back with token.
With this token you can use whichever service you have in scope and your project on Developer Console includes (look for Enabled APIs and services).
Example request:
Create new GET request and save it in your collection, so the variables and tokens are inherited.
URL is: https://www.googleapis.com/oauth2/v1/userinfo?alt=json
Auth in Postman: Inherit auth from parent.
And Send...
Took me months to understand, so hope you'll be swifter :)

Microsoft AD Authentication node js Library

I am developing a nodejs application in which I want to use Microsoft AD oath2 flow. I looked into Microsoft's official documentation here and found that they list two libraries -
msal-node
passport-azure-ad
I need help selecting the library which will best serve my needs. I need to -
perform authentication
protect some apis via token verification
expose all the apis so that they can be tested externally
call external web apis within my app
Also, the documentation says that "Acces web apis" is not possible with passport-azure-ad. What do they mean by this?
I've used passport-azure-ad to handle Azure SSO in a complex login service which involved many different authentication protocols.
It worked perfectly fine to establish the client credential flow, in which the session is managed by the back-end.
The msal-node library is better to implement an implicit flow, in which an SPA gains access to a token which can then be used to obtain data from the identity provider.
Another way around it would be to use passport-azure-ad to handle the login, and then tie that in to a custom JWT generation system.
That would end up looking something like this:
const passport = require('passport');
const OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
const Token = require('Token.js');
settings = {
azureDirectoryId: '',
azureClientId: '',
azureClientSecret: '',
callbackUrl: 'https://example.com/auth/callback',
}
passport.use(new OIDCStrategy(
{
identityMetadata: `https://login.microsoftonline.com/${settings.azureDirectoryId}/.well-known/openid-configuration`,
clientID: settings.azureClientId,
responseType: 'code id_token',
responseMode: 'form_post',
redirectUrl: settings.callbackUrl,
clientSecret: settings.azureClientSecret,
// ...additional settings if needed
},
(req, iss, sub, profile, accessToken, refreshToken, done) => {
process.nextTick(async () => {
profile.accessToken = accessToken;
profile.refreshToken = refreshToken;
profile.token = Token.generate(profile);
done(null, profile);
});
},
));
router.get('/auth/login', (req, res, next) => {
passport.authenticate('azuread-openidconnect', {
response: res,
resourceURL: null,
customState: Math.random().toString(36).substr(2, 10),
failureRedirect: '/failure',
})(req, res, next);
});
router.use('/auth/callback', (req, res, next) => {
passport.authenticate('azuread-openidconnect', {
response: res,
successRedirect: '/success',
failureRedirect: '/failure',
})(req, res, next);
});
router.get('/auth/success', (req, res, next) => {
if (req.user.token) {
res.cookie('token', req.user.token);
return res.redirect('https://example.com');
}
failure(req, res);
});
const failure = (req, res) => {
req.session.destroy(() => {
res.clearCookie('token', cookieSettings);
res.redirect('https://example.com');
});
};
router.get('/auth/failure', failure);
This way, sending the user to https://example.com/auth/login would start off the process of authentication via passport. Once completed, you'd generate a JWT with the information returned from the identity provider (Token.generate(profile);), store that token as a cookie, and redirect the user back to the homepage.

Trying to use Google Oauth2 with Passportjs in Graphql-Yoga Server

I thought I followed all of the docs correctly to implement this, however, I am getting a TokenError: Bad Request that has the error message invalid_grant.
My server is super simple:
require('dotenv').config();
import createServer from './createServer';
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const server = createServer();
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: 'http://localhost:4000/auth/callback',
},
(accessToken, refreshToken, profile, cb) => {
console.log(accessToken);
console.log(refreshToken);
console.log(profile);
cb(null, profile);
},
),
);
server.express.use(
'/auth',
passport.authenticate('google', {
scope: ['email', 'profile'],
session: false,
}),
);
server.express.use(
'/auth/callback',
passport.authenticate('google', {
successRedirect: 'http://localhost:3000/authenticate',
failureRedirect: 'http://localhost:3000/authenticate',
}),
);
server.start(
{
cors: {
credentials: true,
origin: 'http://localhost:3000',
},
},
() => console.log('Server is running on http://localhost:4000'),
);
Is this a problem with the way that I have setup Google in the cloud platform? I can't figure out where I went wrong. My callback is correctly setup. I'm not sure where else to look for a mistake?
Another thing that is confusing is that the GoogleStategy is console logging the user profile and the access token that is returned. I am guessing that the error comes when the callback route tries to verify the code from the URL. Can anyone point me in a direction to look to better troubleshoot this? Thanks in advance.
I found a solution that works for me, but I am still unclear if it is "best-practice" or not. I would love someone with more GraphQL experience to chime in.
I followed the directions in the docs to authenticate in the front-end: https://developers.google.com/identity/sign-in/web/backend-auth
Then I wrote a query called isAuthenticated on the back-end that I can use to verify the token.
async isAuthenticated(_: any, { token }) {
if(!token) {
return null;
}
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.GOOGLE_CLIENT_ID,
});
return payload;
},
I use a React component to check the token in localStorage before rendering any protected routes. This is working for me.

passport google : why sessionID is different in the callback?

Dears,
there are a lot of examples around passport google, but there should be something I missed. I know my question might be marked as "possible duplicate", but sorry I can't find the answer, or none of them are fixing my issue.
the user clicks a href link on the client (running on https://localhost:3000), calling the googleauth route on the server (running on https://localhost).
On server side, passport process is running well, the callbackUrl is on the server side at /googleauth/redirect.
Then in this callback, I can hardcoded the redirect to 'https://localhost:3000'. that works fine.
But I don't want this. I want the server to catch the initial url from the client (so https://localhost:3000), keep it in the session and then use this information in the redirect.
In the google auth route, I set the toRedirect variable into the session thanks a middleware, before calling the passport.authenticate.
googleRouter.get('/', (req, res, next) => {
if (!req.session.toRedirect) req.session.toRedirect = req.socket.remoteAddress;
next();
}, Auth.passport.authenticate('google', {
scope: ['profile', 'email', 'https://mail.google.com/'],
accessType: 'offline',
prompt: 'consent',
session: false // <-- whatever true or false, same issue
})
);
However once the google process is complete (choose the google account and confirm the acces to user data), the sessionID is different in the callback, therefore the url to redirect is undefined... And of course the remote address is no longer the client one, but the google authentification APIs one...
googleRouter.get('/redirect', Auth.passport.authenticate('google', { session: false }), (req, res) => {
console.log(req.session.toRedirect, req.sessionID, req.socket.remoteAddress) // <--- sessionID is different so req.session.toRedirect is undefined
res.redirect('https://localhost:3000'); /<--- hardocded here but I want res.redirect(req.session.toRedirect).
});
So guys, how did you send back the google auth results to your clients ?
Here is the code
apps.js
const passport = Auth.passport;
app.use(passport.initialize());
app.use(passport.session()); <-- whatever set or not, same issue
auth.js
const googleOptions = {
clientID: sds,
clientSecret: dsadd,
callbackURL: '/googleauth/redirect',
passReqToCallback: true
};
passport.use(new GoogleStrategy(googleOptions,
async (req, token, refreshToken, profile, done) => {
data = await Auth.helpers.signIn(req, null, 'google');
if (data && data.erreur == authErrors.NONE) {
// MAJ de la session et cookie
req.session.userId = data.user.id;
done(null, data.user);
};
));
googleroutes
googleRouter.get('/', (req, res, next) => {
if (!req.session.toRedirect) req.session.toRedirect = req.socket.remoteAddress;
next();
}, Auth.passport.authenticate('google', {
scope: ['profile', 'email', 'https://mail.google.com/'],
accessType: 'offline',
prompt: 'consent',
session: false
})
);
// callback route pour redirect google
googleRouter.get('/redirect', Auth.passport.authenticate('google', { session: false }), (req, res) => {
console.log(req.session.toRedirect, req.sessionID, req.socket.remoteAddress)
res.redirect('https://localhost:3000');
});

OAuth2 using passport-google-oauth: there is no body in GET request but I still recieve some token

I'm trying to implement google oAuth2 for android client. I send empty GET request for google login and receive some token. I can't understand why this happening.
I used my web client id and secret from developer console.
Here is my code:
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
passport.use(new GoogleStrategy({
clientID: 'MY CLIENT ID',
clientSecret: 'MY SECRET',
callbackURL: "http://mysite.io/account/googleLogin/callback"
},
function(accessToken, refreshToken, profile, cb) {
console.log("collection findOrCreate");
console.log("accessToken " + accessToken);
console.log("createIndex" + profile.id);
}
app.get('/googleLogin',
passport.authenticate('google', { scope: ['profile'] }));
app.get('/googleLogin/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
console.log("Google Login Success!");
res.redirect('/');
});
Logs:
accessToken ya29.tAI7uCiN-JFKWflq4Wm6xbyQjk1S-qdlB6Ks6GTHnNzzr0N_jz8rUVPZLVlvi4aIkF6SGvw
createIndex 102114909694672049994
When I send with the body, effect is the same.
GET http://mysite.io/account/googleLogin/
{ "idToken": "sometoken" }
I've found different approach. First is this part:
router.get('/googleLogin', passport.authenticate('google', { scope : ['profile', 'email'] }));
redirects user to the google authorization page, after success of which called callback method. It's not what I need, because I already have a token on Android side.
For Android client one of the ways is manually check if the google tokenId is valid or not, sending request to the gooogle API. Implementing this part from official source solved my problem:
https://developers.google.com/identity/sign-in/android/backend-auth#verify-the-integrity-of-the-id-token

Resources