Node.js SAML implementation with OneLogin - node.js

I am looking to setup our application in the application catalog of OneLogin, thus I need to create a SAML integration, as I understand it. I do see that they have toolkits available for this, but I am working in Node.js and there is no toolkit for that environment.
I have been reading their documentation as well as other posts and think that the process is something like the following:
1) Make a request to OneLogin to create the application and add it to their catalog.
2) My application needs to have a route point that I will provide to OneLogin which will be used as the redirect when someone clicks the icon for our app.
3) A user clicking on the icon for my app in the catalog will tokenize the user and send that to my defined route point with the information passed as a SAML request / XML.
4) My route point will need to consume the SAML request / XML and then will perform my internal login process. The information passed to my route point by OneLogin will include the necessary information for my site, like first name, last name, and email address. I will then do my internal application with that information and if it validates to an existing user, I would count that as a successful login and then let them continue. If they are not an existing user, I would send them through a user creation type form, but could default information from the SAML request / XML from OneLogin, or could just automatically create the user.
Does that seem like I have a high level understanding of the process?
Does anyone have examples in Node.js?
I was going to use the passport-SAML package.

Yes you're on the right track.
Passport-SAML works well for Express apps https://github.com/bergie/passport-saml
Your Passport SAML Strategy configuration should look something like this.
passport.use(new SamlStrategy(
{
path: '/login/callback',
entryPoint: 'https://{SUBDOMAIN}.onelogin.com/trust/saml2/http-redirect/sso/{APP_ID}',
issuer: 'passport-saml'
},
function(profile, done) {
console.log(profile);
return done(null, profile);
})
);
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
Be sure to use the SLO Endpoint that is provided when configuring your app via the OneLogin portal.
Then setup your routes to use Passport
// Initiates an authentication request with OneLogin
// The user will be redirect to OneLogin and once authenticated
// they will be returned to the callback handler below
app.get('/login', passport.authenticate('saml', {
successReturnToOrRedirect: "/"
}));
// Callback handler that OneLogin will redirect back to
// after successfully authenticating the user
app.post('/login/callback', passport.authenticate('saml', {
callback: true,
successReturnToOrRedirect: '/users',
failureRedirect: '/'
}))
You also need to make sure you have set the ACS (Consumer) URL to your apps callback url and that the user you are testing with has access to the app.

Related

Handle Google OAuth with JWT (react + nodejs)

I am working on the authentication system of a web app, using Next.js for the client app and Node.js for the API.
I have my Next.js app on port 3000
I externalized the API of my application, on port 5000
That's why I used JWT for the local signin/signup strategies.
(I'm planning to use the same API for the mobile application later)
I am now wondering what is the best approch for a Google Authentication.
I have set it up, but I don't know how to give the token to the client.
Here is the process:
On Signin page (http://localhost:3000/signin), the user clicks on "Google authentication". It redirects to 'http://localhost:5000/auth/google"
Passport handles it, it redirects to Google OAuth page. User authorize the application.
Google redirects to the callback URL (http://localhost:5000/auth/google/redirect)
In the callback route, I can create a JWT. But how can I give it back to the client ?
I have thought of passing it through URL, but I am wondering if it is safe ?
Is there another way to do it / Am I missing the point ?
router.get('/google/redirect', (req, res, next) => {
return passport.authenticate('google', (err, user) => {
if (err) {
return res.redirect('http://localhost:3000/signin')
}
console.log(user)
// Create JWT and redirect to http://localhost:3000/signin/oauth?token=xxx ?
})(req, res, next)
})
I can show more code if needed, but it works (code is not the blocking point).
Thank you in advance !
all you have to do is setting up cookie session. When google sends responds to /google/redirect, passport.authenticate will call req.login() and this will call the serializeUser
passport.serializeUser(
(user, done ) => {
done(null, user.id); // stores the id<4kb
}
);
this function will create, passport:{user:userId}. this is the unique identifying information about the user. This where you need session. Because passport.js will automatically look for req.session and attaches the passport object to the req.session.
Since we are storing only userId, usually cookie-session package. this package will set req.session object, passport.js will attach the passport object and the cookie-session will store this on the client.

Using the correct oauth2 credentials to connect to AzureDevops through NodeJS using Passport.js

I am having issues to figure out what exact credentials I have to use to connect to Azure Devops through NodeJS using Passport.js.
I am using the strategy as described here: http://www.passportjs.org/packages/passport-azure-oauth2/
In AzureDevops, I have created an Application under the: Authorized OAuth Apps, and then have the following information at my disposal there:
This is the current code I use to create the strategy:
passport.use("azure-devops", new AzureOAuth2Strategy({
clientID: process.env.AZURE_DEVOPS_CLIENT_ID,
clientSecret: process.env.AZURE_DEVOPS_CLIENT_SECRET,
callbackURL: process.env.AZURE_DEVOPS_CALLBACK_URL,
resource: process.env.AZURE_DEVOPS_RESOURCE,
tenant: process.env.AZURE_DEVOPS_TENANT,
prompt: 'consent',
state: true
},
function (accessToken, refreshtoken, params, profile, done) {
var user = jwt.decode(params.id_token, "", true);
done(null, user);
}));
passport.serializeUser(function(user, done) {
done(null, user)
})
passport.deserializeUser(function(obj, done) {
done(null, obj)
});
app.use(passport.initialize())
app.use(passport.session())
Now what I do not understand is which information to fill into the respective credentials fields. Especially confusing to me are the fields "tenant" and the field "resource". Passport.js describes these fields as optional, however, if I leave the "tenant" field out, when I go to the microsoft login page using the /auth/login route, I am getting into a loop. It means that when I enter my email and password, it redirects me back to the login and this is endless. If I add the "tenant" field with an ID (where I am not sure of which ID I need to use).
I receive the following error:
What am I supposed to add to these fields in the Strategy and where do I find this information???
Also confusing to me is that after I have added my application and I go to the dashboard about added applications, it says I have not granted access to any application yet, although I have added that application before ... it looks like this on my side:
What am I doing wrong here overall?
Passport-azure-oauth2 strategy is used to authenticate to access Azure Resources not Azure devops. This strategy is actually the Azure OAuth 2.0 client credentials flow.
If you want to use Oauth2 as authentication for azure devops. You should use OAuth2 authorization code grant flow. See below document for more information.
Authorize access to REST APIs with OAuth 2.0. There is a sample project you can check out.
There are many ways to authenticate your application with Azure DevOps Services. Check out this document for more examples.
As for Passport.js, you can check out OAuth2orize which is a sibling project to Passport, provides a toolkit for implementing OAuth 2.0 authorization servers.

Passing OAuth user data back to React via Node (Passport) authentication

I'm a little confused about the flow of data in a React application that authorizes a user through a third party OAuth provider. Right now I have a login button in React that directs to a Node server.
Log In
This directs to an Express route...
router.get('/auth/google',
passport.authenticate('google', {
session: false,
scope: ['email']
}));
that uses Passport to verify the login request.
passport.use(new GoogleStrategy({
clientID: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_SECRET,
callbackURL: "http://localhost:5000/api/auth/google/callback"
},
function (accessToken, refreshToken, profile, cb) {
return cb(null, profile);
}
));
The callback is set up to redirect the user back to the React application.
router.get('/auth/google/callback',
passport.authenticate('google', {
session: false,
failureRedirect: 'http://localhost:3000'
}),
function (req, res) {
// Successful authentication, redirect home.
res.redirect('http://localhost:3000');
});
This method allows me to either add a user to my DB via my Node app, or confirm the existence of the user already in my DB, and that's as far as I've seen any tutorial take it, but it seems entirely useless without the ability to send this information back to React. The point of logging in, at least in my opinion, is to have my application function specific to a user.
What I'd like to be able to do is send back a signed JWT token. Either one signed manually with my server, or the access token that passport gets from Google, either will do. As well, I'd like to send a user ID. I need this so my app can make API calls with the user ID as part of the request to protected routes, thus the need for the JWT.
It's confusing to me because without this exchange of data, the purpose of OAuth seems essentially useless, so I must be missing a vital step in the process, or I must be looking at the problem from the wrong perspective. Where or how should I be informing my React application of the details of the logged in user, and give them a token for local storage? As far as I can tell, there's no way to send package of data back with the callback URL.
I found a way, you can use eventsource for this. Check this article for more information https://www.blog.tericcabrel.com/implement-server-sent-event-in-node-js/
You can send you token as query param in your frontend app
res.redirect(`http://YOUR_FRONTEND_HOST:3000/?token=` + "Your jwt token")
So in the frontend you can retrieve the token with a useEffect then make it disappear to don't make it avalible to the user.

Getting the domain name making requests to my API endpoints

I'm using Node + express to build an API. The idea is to be able to let other developers login and register their app so that i can authorize access to my API endpoints.
exports = passport.use(new FacebookStrategy({
clientID: '999999999',
clientSecret: '999999999',
callbackURL: "http://localhost:3000/auth/facebook/callback"
},function(accessToken, refreshToken, profile, done) {
profile.access_token = accessToken;
db.mongoClient.connect(db.moments, function(err, db){
var user = db.collection('user');
user.find({'facebook':profile.id}).toArray(function(err, docs){
console.log(docs);
})
})
done(null, profile);
}));
I have set this up using the passport facebook strategy. This allows developers to login to a profile page on my app where they are presented with the access_token i got from facebook. I'm using this access_token to allow a connection between my app and their app using the bearer token strategy.
However, i also want to add another layer of security. They should register the domain name that is going to make API calls to my app. This should protect me from developers passing along the token to other developers (did i got that part right?).
The question: How can i check that they are indeed making the request from the registered domain name?
thx,
After your facebook authentication, you know user and you know his registered domain. Then make call from server to some defined script at registered domain. And if indeed it was call from this user then he should return some response which you agreed with him. And if response is ok, then finish authentication (if I correctly remember some payment systems make this verification for online shops).
Or something more sophisticated, after facebook auth, send to registered domain url some temporary token. Then user must send you in next call this temporary token and you exchange it for final token.
To be secure your users should have https when your server call them. Otherwise it is not reliable.

Unable to sync Gmail contacts with passport-google-oauth in Node.js

I an new for passport.js.
I am using it to authenticate and getting Gmail contacts,for getting the contacts I need to pass scope value this https://www.google.com/m8/feeds.But I didn't get the contact list except profile details.
Here is my code stuff:
//register with google
app.get('/auth/google', passport.authenticate('google', { scope : ['profile', 'email','https://www.google.com/m8/feeds'] }));
// the callback after google has authenticated the user
app.get('/auth/google/callback', passport.authenticate('google', {
successRedirect : '/user/dashboard',
failureRedirect : '/register.html'
}));
And my passport.js code:
passport.use(new GoogleStrategy1({
consumerKey: config.auth.googleAuth.clientID,
consumerSecret: config.auth.googleAuth.clientSecret,
callbackURL: config.auth.googleAuth.callbackURL
},
function(token, tokenSecret, profile, done) {
console.log(profile);
return done(err, user);
}
));
When I print profile I am getting only user details not contact list.
I don't have idea, what I do for getting that.
Any help will be appreciated.
Thank You.
These are the following steps are used to get the Gmail contacts
1- To communicate with any Google API we need to create an account on Google console
2- After that create a project, which we want to communicate with Google API, after creating project Google provides secret key and client key which is used to communicate with Google. These keys are required whenever our application try to communicate with any Google API.
3- To get the Gmail contacts, Google provides the https://www.google.com/m8/feeds/contacts/default/full?alt=json&oauth_token=xxxxxx
4- We only need to call this API for getting the contacts, and this API takes some credential before communicating.
5- The users must have logged in with Google and have the token which used by API to get the user’s contacts.
6- Normally we prefer passport Google strategy to log in with Google. And our half of the things are done by Passport.js like authentication with Google and token.
7- When user login with Google, the Passport.js plays a role as a middleware for the successful login and during that, passport provides a token of current user. And that time we are calling the contacts API https://www.google.com/m8/feeds/contacts/default/full?alt=json&oauth_token=xxxxxx
And easily we can get the token, and the token created by Google expires after one hour, but we should not worry about that since passport internally provides new token provided by Google.
Hope it will work for you.
Update
Let's play with REST API
Get all the contacts by using REST call
Use request module to request the HTTP call
request.get({
url: 'https://www.google.com/m8/feeds/contacts/default/full',
headers: {
'Authorization': 'Bearer <Your access token>',
'Content-Type': 'application/json'
},
qs: qs,//Optional to get limit, max results etc
method: 'GET'
}, function (err, response, body) {
});

Resources