Using Google function with Node-express-passport - node.js

I have used passport-twitter strategy and it so happens that I am unable to see passport-login window on my client side when I execute my stratergy
So this is what I am doing, initially
class Auth extends ParentClass {
constructor(context, options) {
super()
this.app.get('/callback/:service', (req, res) =>
this.callbackEndpoint(req,res))
this.app.get('/', (req, res, next) => this.loginEndpoint(req, res, next))
}
async loginEndpoint (req, res, next) {
if (req.query.Twitter) {
console.log(`Inside Twitter Authentication`)
passport.authenticate('twitter', { scope : ['email'] })(req,res,next);
}
}
where in my ParentClass I am more or less initialising stuff
class ParentClass {
this.use(corsMiddleware(options.CORS_URLS))
this.use(bodyParser.json())
this.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
this.use(passport.initialize())
this.use((passport.session()))
}
use (middleware) {
this.app.use(middleware)
}
}
And finally, this is my passport stratergy
passport.use(new TwitterStrategy({
consumerKey: functions.config().twitterCredentials.apikey,
consumerSecret: functions.config().twitterCredentials.apiSecretKey,
callbackURL: redirect_uri,
passReqToCallback: true
}, async (req, accessToken, refreshToken, params, profile, done) => {
console.log(`logging from Passport Twitter ${req.workspace, req.accessToken}`)
done()
}))
From the first code, snippet I see the log Inside Twitter Authentication but thereafter nothing happens.
What I was expecting? A window to show up for me to log into twitter but nothing shows up and after sometime I get this in my console info: Execution took 569847 ms, finished with status: 'timeout'
So here are my Questions:
1. Can we use passport and normal oauth flow with google-functions? (just comment if not answer)
2. If yes, can we use spot the error in my above code.
Update note: While Googling for my problem, I stumbled upon this old question and realised that while it was somewhat similar but the description for the previous one was very vague. I asked a similar question later today but decided to merge it with this one.

In the first part of your code, you need to pass passport.authenticate directly to the get handler. Like this:
class Auth extends ParentClass {
constructor(context, options) {
super()
this.app.get('/callback/:service', (req, res) =>
this.callbackEndpoint(req,res))
this.app.get('/', passport.authenticate('twitter', { scope : ['email'] })(req,res,next))
}
}
I assume this "App" is mounted at some path like /auth/twitter
In your front end, you open a popup window with URL pointing to /auth/twitter. Something like this:
<button onclick="oauthPrompt('twitter')">Login with Twitter</button>
<script>
function oauthPrompt(service) {
const url = '/auth/' + service
var newWindow = window.open(url, 'name', 'height=600,width=450')
if (window.focus) {
newWindow.focus();
}
}
</script>

Related

Authentication using linkedin in a mean stack application

so I've been trying to implement login with linkedin in my application, and I couldn't find anything online that could show me the steps from A to Z
I implemented the backend and the frontend separateley, however, I don't know how to link them together
In the backend, I'm using passportjs
So here's what I've done so far:
FRONTEND
app.component.html
<button click="loginWithLinkedin()">Linkedin</button>
app.component.ts
window.location.href = `https://www.linkedin.com/uas/oauth2/authorization?response_type=code&state=true&client_id=${environment.LINKEDIN_API_KEY}&redirect_uri=${environment.LINKEDIN_REDIRECT_URL}&scope=${environment.LINKEDIN_SCOPE}`;
redirect.component.ts
const linkedInToken = this.route.snapshot.queryParams["code"];
this.http.get('http://localhost:3000/user/auth/linkedin',
{ params: { token: linkedinToken }}).subscribe(res => {
console.log(res);
});
BACKEND
passport.use(new LinkedInStrategy({
clientID: LINKEDIN_CLIENT_ID,
clientSecret: LINKEDIN_CLIENT_SECRET,
callbackURL: "http://127.0.0.1:8000/user/auth/linkedin/callback",
scope: ['r_emailaddress', 'r_basicprofile'],
passReqToCallback: true
},
function (req, accessToken, refreshToken, profile, done) {
req.session.accessToken = accessToken;
process.nextTick(function () {
return done(null, profile);
});
}));
linkedinRouter.route('/auth/linkedin')
.get(passport.authenticate('linkedin', { state: 'SOME STATE' }),
function(req, res){
// The request will be redirected to LinkedIn for authentication, so this
// function will not be called.
});
linkedinRouter.route('/auth/linkedin/callback')
.get( passport.authenticate('linkedin', { failureRedirect: '/' }),
function (req, res) {
return res.send('hello');
});
I don't understand how would passport work and I don't understand how to link backend and frontend.
I don't know if this is the correct way to implement linkedin authentication
If you have any articles that could guide, or if you can correct that would really help, I've been stuck for a couple of days now.
Thank you very much
I apologize if I formulated the question in a bad way or I didn't know how to ask the question. However, I managed to make it work and I wrote a medium article about it.
here's the link:
https://nour-karoui.medium.com/linkedin-authentication-for-a-mean-stack-application-bd8542b2dc7f
I hope it can help somebody out there !

Passport authenticate access to req

I have set up azureAD login with passport in my Express-server. I would like to access the req object in the passport.authenticate middleware, is this possible somehow?
this.router.post(
'login/azure/return',
passport.authenticate('azuread-openidconnect', {
session: false,
failureRedirect: '' // I would like access to the req object HERE
}),
(req, res) => {
...
}
You can't use the req object because these are default options provided based on the authentication or the configuration you want to provide to a user. For sample you can check this:
passport.authenticate('azuread-openidconnect', { failureRedirect: '/', session: false, customState: 'my_state', resourceURL: 'https://graph.microsoft.com/mail.send'}, (req, res, next) => {
log.info('Login was called in the Sample');
res.redirect('/');
});
For more information, you can check the details here.
Yes, it is possible.
Look at your code, at some point you have to define your OIDCStrategy, which looks something like this:
var strategy = new OIDCStrategy({
"clientID": "clientId goes here",
// a bunch of attributes will be listed here
"passReqToCallback": true // <--- this will do the trick
},
function authenticationCallback(req, iss, sub, profile, accessToken, refreshToken, done){
// you will get "req" in this call back
}
);
When defining the options of your strategy make sure you use the attribute "passReqToCallback": true (as shown in the example), and use the function signature also provided in the example, the first parameter will be your "req" object.

Next.js implementing google signin with /api routes and passport

I'm trying to implement Google signin in my serverless (/api routes) next.js app.
I'm using #passport-next/passport-google-oauth2, next-connect and passport packages.
I searched a lot and found a few helpful links online but I couldn't make it work, and I'm not sure about the whole flow that should happen here.
For example, I found those:
https://github.com/andycmaj/nextjs-passport-session-auth
https://todayilearned.io/til/nextjs-with-passport-oauth-cookie-sessions
I have /api/auth/login route for regular login. If login was successful, I'm setting JWT cookie on user response.
For Google login, I added /api/auth/social/google route, with the following code:
import passport from 'passport';
import { Strategy as GoogleStrategy } from '#passport-next/passport-google-oauth2';
import nextConnect from 'next-connect';
passport.use(new GoogleStrategy({
clientID: process.env.OAUTH_GOOGLE_CLIENT_ID,
clientSecret: process.env.OAUTH_GOOGLE_CLIENT_SECRET,
callbackURL: process.env.OAUTH_GOOGLE_CALLBACK_URL,
scope: "https://www.googleapis.com/auth/plus.login",
},
(accessToken, refreshToken, googleUserInfo, cb) => {
console.log('accessToken, refreshToken, googleUserInfo');
cb(null, googleUserInfo);
}
));
export default nextConnect()
.use(passport.initialize())
.get(async (req, res) => {
passport.authenticate('google')(req, res, (...args) => {
console.log('passport authenticated', args)
})
})
and /api/auth/social/callback/google route, with the following code:
import passport from 'passport';
import nextConnect from 'next-connect';
passport.serializeUser((user, done) => {
console.log('serialize')
done(null, user);
});
export default nextConnect()
.use(passport.initialize())
.get(async (req, res) => {
passport.authenticate('google', {
failureRedirect: '/failure/success',
successRedirect: '/auth/success',
})(req, res, (...args) => {
console.log('auth callback')
return true;
})
})
So what happens is that the user is redirected to /auth/success after signin to his google account, and console logs are:
accessToken, refreshToken, googleUserInfo
serialize
So my questions are:
When and how can I set the JWT cookie on the response to "login" the user?
Why the line console.log('auth callback') never runs? when it should run?
The same for console.log('passport authenticated', args)
How is a complete flow should look like in my app?
Thanks !
I had the same problem. I'm still working on the full implementation, but according to this thread it looks like the problem might be that passport.authenticate is supposed to be used as a middleware:
// api/auth/social/callback/google
export default nextConnect()
.use(passport.initialize())
.get(passport.authenticate("google"), (req, res) => {
console.log('auth callback')
res.writeHead(302, {
'Location': '/auth/success'
});
res.end();
})
Ok, I'm not a Next.js expert but I had a similar "problem", only using other authentication method.
You see, the framework has a method that is really important in the authentication process, they are called: getInitialProps, which is an async function that can be added to any page as a static method to trigger before the initial render;
it looks like this:
static async getInitialProps(ctx) {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const json = await res.json()
return { stars: json.stargazers_count }
}
It takes this ctx prop, which is a object with {req, res} that you can use to make your request to authenticate the user and, then, you may use req with the lib next-cookies to get the JWT token and check if it is valid and, then, you may return an object, which will be used as a prop of your page (or all the pages, if you wrap your _app.js around a Context of Authentication.
Also, your 'auth callback' is not being called because you redirect the user before it triggers, which is not happening. The same for 'passport authenticated'
This article may also help you.
https://dev.to/chrsgrrtt/easy-user-authentication-with-next-js-18oe

Azure AD open BearerStrategy "TypeError: self.success is not a function"

I asked this at https://github.com/AzureAD/passport-azure-ad/issues/427 but have had no response. I feel it is a bug that is preventing me from completing my work so I am reaching farther and wider to get an answer. Is it something I'm doing or a bug?
I am writing the question different to I had before since I have done some more investigation in to the problem (I will indicate where the differences start).
Passport-Azure-AD version 4.1.0 - https://www.npmjs.com/package/passport-azure-ad#52-bearerstrategy
I have set this up from the documentation:
setup() {
const findById = (id, fn) => {
for (let i = 0, len = this.users.length; i < len; i++) {
const user = this.users[i];
if (user.sub === id) {
logger.info('Found user: ', user);
return fn(null, user);
}
}
return fn(null, null);
};
this.bearerStrategy = new BearerStrategy(jwtOptions,
(token: ITokenPayload, done: VerifyCallback) => {
findById(token.oid, (err, user) => {
if (err) {
return done(err);
}
if (!user) {
// 'Auto-registration'
logger.info('User was added automatically as they were new. Their oid is: ', token.oid);
this.users.push(token);
this.owner = token.oid;
return done(null, token);
}
this.owner = token.oid;
return done(null, user, token);
});
}
);
console.log(`setup bearerStrategy`);
}
The jwtOptions I use are:
The options are like this:
const jwtOptions = {
identityMetadata: 'https://login.microsoftonline.com/xyz/v2.0/.well-known/openid-configuration',
clientID: '0123456789',
loggingLevel: 'info',
loggingNoPII: false,
passReqToCallback: false
};
And run authentication (from middlewhere) using the following:
authenticate(request: express.Request) {
this.bearerStrategy.authenticate(request, {session: false});
}
NOTE That this is different to the doco since what they had doesn't work.
It fails on the line:
return done(null, token);
With:
[2019-05-29T13:49:33.479] [INFO ] [AUTHSERVICE_LOGGER] - User was added automatically as they were new. Their oid is: 123
.../translateboard/node_modules/passport-azure-ad/lib/bearerstrategy.js:565
return self.success(user, info);
^
TypeError: self.success is not a function
at verified (/Users/bbos/dev/dhs/translate/translateboard/node_modules/passport-azure-ad/lib/bearerstrategy.js:565:21)
at findById (/Users/bbos/dev/dhs/translate/translateboard/server/src/services/AuthService.ts:106:32)
at findById (/Users/bbos/dev/dhs/translate/translateboard/server/src/services/AuthService.ts:87:20)
at Strategy.bearerStrategy.passport_azure_ad_1.BearerStrategy [as _verify] (/Users/bbos/dev/dhs/translate/translateboard/server/src/services/AuthService.ts:97:17)
at jwt.verify (/Users/bbos/dev/dhs/translate/translateboard/node_modules/passport-azure-ad/lib/bearerstrategy.js:363:19)
at /Users/bbos/dev/dhs/translate/translateboard/node_modules/passport-azure-ad/lib/jsonWebToken.js:80:16
at process._tickCallback (internal/process/next_tick.js:61:11)
From here is different compared to original post
If I put a breakpoint in the code, the self Object in BearerStrategy.js where the error is:
{
"name": "oauth-bearer",
"_options": {
"identityMetadata": "https://login.microsoftonline.com/xyz/v2.0/.well-known/openid-configuration",
"clientID": "0123456789",
"loggingLevel": "info",
"loggingNoPII": false,
"passReqToCallback": false,
"clockSkew": 300,
"validateIssuer": true,
"allowMultiAudiencesInToken": false,
"audience": [
"1234",
"spn:1234"
],
"isB2C": false,
"_isCommonEndpoint": false,
"_verify" = (token, done) => {...},
"__proto__" = Strategy(...,
}
}
And under __proto__ are:
authenticate = function authenticateStrategy(req, options) {
constructor = function Strategy(options, verifyFn) {
failWithLog = function(message) {
jwtVerify = function jwtVerifyFunc(req, token, metadata, optionsToValidate, done) {
loadMetadata = function(params, next) {
You can see there is no success in Passport-Azure-Ad. It does define a failWithLog https://github.com/AzureAD/passport-azure-ad/blob/e9684341920ac8ac41c55a1e7150d1765dced809/lib/bearerstrategy.js#L600 - did they forget to add the others?
Passport defines these others (https://github.com/jaredhanson/passport/blob/1c8ede35a334d672024e14234f023a87bdccaac2/lib/middleware/authenticate.js#L230) however they are in a closure and never exposed. Nor is the parent Strategy object that they are defined on. The only connection with the outside is through the exposed authenticate method https://github.com/jaredhanson/passport/blob/1c8ede35a334d672024e14234f023a87bdccaac2/lib/middleware/authenticate.js#L70
However as seen, Passport-Azure-Ad defines it's own authenticate method (https://github.com/AzureAD/passport-azure-ad/blob/e9684341920ac8ac41c55a1e7150d1765dced809/lib/bearerstrategy.js#L372) and never calls the passsport one.
To me it looks like it never worked.
Can anyone confirm or disagree?
I will update the post at https://github.com/AzureAD/passport-azure-ad/issues/427 to refer to this.
Next I am going to git bisect the repository to see if I can find a change where those missing methods used to be defined or something else that stands out.
I can confirm that as my code was written it would never work. There were two main issues:
Pass parameters
As per my comment against the question, i neglected to provide information in the question since I didn't think it was relevant. But it is.
I am using TSED - TypeScript Express Decorators (https://tsed.io) and it replaces express middleware code like:
server.get('/api/tasks', passport.authenticate('oauth-bearer', { session: false }), listTasks);
With an annotated middleware class - https://tsed.io/docs/middlewares.html
So now the call to passport.authenticate() is in the use() method like this as I showed before (THIS IS INCORRECT):
#OverrideMiddleware(AuthenticatedMiddleware)
export class UserAuthMiddleware implements IMiddleware {
constructor(#Inject() private authService: AuthService) {
}
public use(
#EndpointInfo() endpoint: EndpointMetadata,
#Request() request: express.Request,
#Response() response: express.Response,
#Next() next: express.NextFunction
) {
const options = endpoint.get(AuthenticatedMiddleware) || {};
Passport.authenticate('oauth-bearer', {session: false}); // <-- WRONG
if (!request.isAuthenticated()) {
throw new Forbidden('Forbidden');
}
next();
}
}
What I neglected to consider is that express middleware is passed the request object. So what I actually needed to have was:
Passport.authenticate('oauth-bearer', {session: false})(request, response, next); // <-- CORRECT
Must use Passport.use()
The documentation is misleading. Given I'm not overly savy with Passport I didn't think much about this.
The doco (http://www.passportjs.org/packages/passport-azure-ad/) (at 5.2.1.1 Sample using the BearerStrategy) says to use:
var bearerStrategy = new BearerStrategy(options,
function(token, done) {
log.info('verifying the user');
log.info(token, 'was the token retreived');
findById(token.oid, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
// "Auto-registration"
log.info('User was added automatically as they were new. Their oid is: ', token.oid);
users.push(token);
owner = token.oid;
return done(null, token);
}
owner = token.oid;
return done(null, user, token);
});
}
);
I am aware that when other strategies are described (such as the 5.1 OIDCStrategy on the same page):
passport.use(new OIDCStrategy({
identityMetadata: config.creds.identityMetadata,
clientID: config.creds.clientID,
...
},
function(iss, sub, profile, accessToken, refreshToken, done) {
...
}
));
They use passport.use. I thought about the difference (when i first saw it) for 1/2 a second and concluded that the AAD BearerStrategy handles things differently given the login is done by Azure using their msal.js library. And I didn't revisit this until Fix # 1 above didn't fix the problem.
I conclude that the TSED project needs to update their documentation / samples (I will do this for them); and the Passport Azure AD project needs to update their doco.
There are still some issues and I don't know who is at fault. I wrote about these at Passport-Azure-Ad in TSED framework seems to run asynchronously.

PassportJS openid-client How to use without a session, save token/user in normal cookie

We are currently using the https://github.com/panva/node-openid-client strategy for passportJS, along with cookie-session.
However we would like to try and move away from sessions and just store either a simple cookie with a token, or attach the token to a header on each request which we then introspect to see whether the token is valid.
I can't figure it out, maybe it's not possible, I just simply don't know where or how I can retrieve the token from the openid-client library, and when and how I should save it in a cookie. Maybe it is only built to use a session.
currently we have:
passport.use(
`oidc.${site}`,
new Strategy(
{
client,
params: getParamsForSite(site),
passReqToCallback,
usePKCE,
},
(tokenset, done) => {
const user = {
token: tokenset,
name: tokenset.claims.sub,
};
return done(null, user);
}
)
);
for the login
app.get(['/login', '/login/:site'], (req, res, next) => {
if (req.params.site) {
passport.authenticate(`oidc.${req.params.site}`)(req, res, next);
} else {
res.end(loginFrontend.success());
}
});
and for the callback
app.get('/auth_callback', (req, res, next) => {
passport.authenticate(`oidc.${req.query.state}`, {
callback: true,
successReturnToOrRedirect: process.env.BASE_URI,
})(req, res, next);
});
We would like to continue using this library as the authentication service we call has a discovery endpoint etc. and wouldn't want to implement all of the features ourselves. If I set session to false, how do I retrieve the token and where for this strategy, can someone help me?

Resources