Create vs Login when using Google oauth for access - node.js

I am currently trying to setup my server to allow users to login with google oauth 2.0.
I am using passport and passport-google-oauth.
Normal set up is something like:
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/google/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return done(err, user);
});
}
));
However what I really want is to still control access to my server after accounts are approved.
Meaning a user would first 'create' and account using google, then be able to signin once there account is approved.
I would really like there to be a signup route and login route:
app.get('/auth/google/signup',
passport.authenticate('google', { scope: ['profile', 'email'] }));
app.get('/auth/google',
passport.authenticate('google', { scope: 'https://www.googleapis.com/auth/plus.login' }));
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
My problem is that when I get to the GoogleStrategy setup I don't really know which route they initially hit. IE if they hit the login route but had not created an account I do not want to create an account I want to warn them that they did not yet create an account. Had they hit the signup route and already had an account I would not want to create another account I would just tell them they already have an account.
Is there anyway in the GoogleStrategy that I can tell which route the user initially hit on my server?

In your user model create the "approved" field, with default False (Boolean)
And you can check this field on the GoogleStrategy to restrict the access.
If you want to apply this on all Strategies you can filter on the serialization method in passport.
Hope it helps.

You can pass a 'state' query parameter in your initial request that will be round-tripped back to your callback.
Documented here:
https://developers.google.com/identity/protocols/OAuth2WebServer
state
Any string Provides any state that might be useful to your
application upon receipt of the response. The Google Authorization
Server roundtrips this parameter, so your application receives the
same value it sent. To mitigate against cross-site request forgery
(CSRF), it is strongly recommended to include an anti-forgery token in
the state, and confirm it in the response. See OpenID Connect for an
example of how to do this.

Related

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.

Invitation system using Passport JS

I'm rewriting an authentication system to be OAuth only with Passport JS. I've designed a user flow as described below, but I can't see where the best point to get Passport to interact with information from the original request is.
The flow will be:
Authenticated user sends invitation to a new user's email address
New user clicks on link and lands on invitation page (/invitation/SOMECODE)
Invitation code is verified and, if still valid, allows user to auth via Google/Slack
New profile is created in the Strategy, but associated with the existing company (instead of creating a new one)
I'm looking to get access to the req.params inside of the Google Strategy, because this is the point I would typically create a new profile, and company for first time users. But if there's an invitation code, I want to do lookups on that info at this point.
I can't see any documentation that supports this approach, other than Node Passport invitation strategy which uses a password after initial sign up.
Can you get access to req object inside the strategy or is there a better way to approach this with another middleware?
I think what you're looking for is passReqToCallback
Example from docs:
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: "http://www.example.com/auth/twitter/callback",
passReqToCallback: true
},
function(req, token, tokenSecret, profile, done) {
if (!req.user) {
// Not logged-in. Authenticate based on Twitter account.
} else {
// Logged in. Associate Twitter account with user. Preserve the login
// state by supplying the existing user after association.
// return done(null, req.user);
}
}
));
See passReqToCallback in the docs: http://passportjs.org/docs/authorize

Node.js SAML implementation with OneLogin

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.

Google oauth & apis how to do it

I can't find any documentation on using google oauth and google apis which I understand. At the moment, I am doing this, but it is not a complete guide: https://developers.google.com/identity/sign-in/web/server-side-flow
What I am trying to do:
I have a node based app, with a simple JS frontend.
It needs a google login.
When a user logs in, it creates a username for them on my server.
It stores some google token somehow under this user on my server.
Somehow give a service level access to my backend.
This backend can then make changes and read calendar info whenever needed, without user interaction.
At the moment I have a google login which returns an auth code like this: localhost:8000/?code=123. I don't know what to do with that auth code. Documentation is only provided for Java and Python unfortunately.
The next thing is to get the auth tokens (somehow) then figure out how to give my service account access.
Anyone know how?
Have you had a look at PassportJS? It has a very solid implementation for Google
I have done something similar for Facebook and will paste it here in case it helps:
So firstly, I added the below into my node app:
var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
Then, the below:
app.use(passport.initialize());
app.use(passport.session());
Then, the below:
passport.serializeUser(function(user, done){
done(null, user.id);
});
passport.deserializeUser(function(id, done){
userModel.findById(id, function(err, user){
done(err, user);
});
});
passport.use(new FacebookStrategy({
clientID: config.facebook.appID,
clientSecret: config.facebook.appSecret,
callbackURL: config.facebook.callbackURL,
profileFields: ['id', 'displayName', 'photos', 'emails', 'birthday', 'location', 'bio', 'likes.limit(100)']
},
function(accessToken, refreshToken, profile, done){
//Do whatever I want
}
)
);
I won't explain all this but - see if it helps you with your Google apps integration. Good luck!

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