Microsoft AD Authentication node js Library - node.js

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.

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 :)

How to do Single Sign-Out SAML using passport-saml npm package?

The application using single sign on flow in Azure AD using the npm package passport-saml.
Application built in nodejs express framework.
passport saml Configuration looks like this snippet:
filename - config.js
passport: {
strategy: 'saml',
saml: {
path: process.env.SAML_PATH ,
entryPoint: process.env.SAML_ENTRY_POINT || 'https://login.microsoftonline.com/tenant/saml2',
issuer: 'app id',
cert: process.env.SAML_CERT,
callbackUrl: "https://application_url/login/callback",
logoutUrl: 'https://login.microsoftonline.com/tenant/saml2',
}
}
In the above config entry point & logoutUrl is same.
Code snippet for express app which consumes passport SAML strategy to connect Azure AD.
filename - connect.js:
const SamlStrategy = require('passport-saml').Strategy;
const config = require('./config.js');
app.use(passport.initialize())
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new SamlStrategy(config.passport.saml,
function (req, token, refreshToken, profile, done) {
}
))
/*express app router*/
app.get("/login", (req, res, next) => {
passport.authenticate(config.passport.strategy, { failureRedirect: "/" })(req, res, next);
})
app.post('/login/callback', (req, res, next)=> {
/*processing logic after the successful auth from Azure AD SAML*/
})
Up to this point it's possible to do SAML auth in Azure AD and received the login callback as well. Note: Login callback properly configured in Redirect URI's of Azure AD application.
Moving on, having a problem in performing logout on an express app router.
LogoutUrl is configured in Azure AD application settings
Whenever app hits movelogout route and it needs to logout Azure Ad session. How it's possible to issue a logout request to Azure AD inside this route using passport-saml strategy?
Code continues
filename: connect.js
app.get('/movelogout', (req, res, next)=> {
//How to issue logout request ?
})
/*Callback for successful logout in Azure AD*/
app.post('/logout', (req, res, next) => {
//Do post logout operation
})
In the nutshell, I have been trying to accomplish Azure Single sign out SAML protocol using passport-saml. The link having SAML logout request and it's not having explanation in javascript way of issuing SAML request.
I am not quite sure with relation between logoutUrl in config & front-end logout Url in Azure setting.
Any suggestions or solutions to perform Azure AD session logout manually are much appreciated!
//How to issue logout request ?
Passport manifest a logout() function on req , that can be called from any route handler which needs to terminate a login session. Invoking logout() will also remove the req.user property and clear the login session (if any).
You can try adding following cmd to Logout in your connect.js file
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
Or,
app.get('/logout', function (req, res) {
req.session.destroy();
req.logout();
res.redirect('/');
});
relation between logoutUrl in config & front-end logout Url in Azure
setting.
Based on this MS DOC No front channel Logout URL is needed while registering the application in Node.js.
For more information please refer the below links:-
.Microsoft Azure Active Directory Passport.js Plug-In | Git-Hub .
.Logging out of Azure Passport authentication Node js | SO THREAD .

Google APi + Passport + React : Authentication flow

I'm using passport to authenticate using Google API, I'm sending a token by URL to the client (React app) which saves it in the localStorage.
I want to use that token : With every API call (get, post, put) I want to send that token to the server , but I didn't know how to verify that token on the server side.
Passport Startegy :
app.use(passport.initialize()); // Used to initialize passport
app.use(passport.session()); // Used to persist login sessions
passport.use(new GoogleStrategy({
clientID: 'IDxxxxx',
clientSecret: 'SecreXXX',
callbackURL: 'http://localhost:3000/callback'
},
(accessToken, refreshToken, profile, done) => {
// Directory API here
var userData = {
name: profile.displayName,
token: accessToken
};
done(null, userData);
Authentication :
app.get('/auth/google', passport.authenticate('google', {
scope: ['profile'] // Used to specify the required data
}));
// The middleware receives the data from Google and runs the function on Strategy config
app.get('/callback', passport.authenticate('google'), (req, res) => {
var token = req.user.token;
res.redirect("http://localhost:8000?token=" + token);
});
API in express (which contains CRUD methods) :
app.use('/api', movieRouter)
In react side : Getting the token
componentWillMount() {
var query = queryString.parse(this.props.location.search);
if (query.token) {
window.localStorage.setItem("jwt", query.token);
// appel a directory api (avec token) puis sauvergarder dans redux puis redirection vers liste demandes
this.props.history.push("/");
}
}
Doing API calls :
import axios from 'axios'
const api = axios.create({
baseURL: 'http://localhost:3000/api',
})
export const insertMovie = payload => api.post(`/movie`, payload)
I just need to send the token in every call and check it in the server side.
Thanks
You want to set the token in a header most likely, try changing your axios client to something like
const api = axios.create({
baseURL: 'http://localhost:3000/api',
headers: {
Authorization: `Bearer ${your_token_here}`
}
})
I'm not 100% sure if this is the correct header form that passport will be expecting, but it's the general idea you need to do.
If the token is correctly set in the header, session, or cookie by the client as noted by Bill Metcalf, then express is able to authenticate a route/endpoint by adding the passport.authenticate middleware function to the route, like so
app.use('/api', passport.authenticate('google', {failureRedirect:'/login'}), movieRouter)
Refer to http://www.passportjs.org/docs/google/ for more information
For every API that you want to verify the token, you can pass a verify token function (which I call 'isCorrectToken') before taking action like this:
router.get("/api", isCorrectToken, (req, res) => {
// your api content })
And then, this is our isCorrectToken function:
const isCorrectToken = (req, res, next) => {
const token = req.headers.authorization;
if(token){
const onlyToken = token.slice(7, token.length);
jwt.verify(onlyToken, accessToken, (err, decode) => {
if(err) return res.status(401).send({ msg: 'Invalid Token' });
req.user = decode;
next();
return;
});
}
else return res.status(401).send({ msg: 'Token is not supplied'});}
The number 7 is the length of 'Bearer ' (from Bill Metcalf's answer above).

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');
});

Is it possible to use passport.js to authenticate wep apis in node?

I am currently using Passport.js as a middleware authentication for my Node.js website. However, I want to use the same authentication services for a react-native application.
Is there a way to expose the generated token by Passport.Js and return it using Json to my react-native app? I know, so far, that it is possible to customize the return of an authentication call such as in
app.post('/api/login',
function(req, res, next) {
app.passport.authenticate('login', function(err, user, info) {
if (err) {
res.json(200, app.infra.errorReturn('Server error', err, null));
} else if (user === false) {
res.json(200, app.infra.errorReturn('Invalid Login', '', null));
} else {
res.json(200, app.infra.successReturn('', '', user));
}
})(req, res, next);
});
As for the strategy, I am using the LocalStrategy and it is configured like this:
const LocalStrategy = require('passport-local').Strategy;
app.passport.use('login',
new LocalStrategy({ passReqToCallback: true },
function(req, username, password, done) { ....
Is this in the "info" parameter? Can it be extracted in some way?
So... to make a long story short, I discovered that I could use two (or more) authentication strategies for the same application. One for the routes to pages and another one for the routes comprising the api.
Therefore I managed to solve my own problem by implementing passport-jwt and then using jsonwebtoken in order to generate an "internal" webtoken to be used by the website when accessing the api services.

Resources