Nodejs google oAuth2 invalid credentials - node.js

I have the following piece of code that allows a user to create google calendar events. Initially it will check if the user has an access token set, if not it will initiate the regular oAuth2 authorisation process and get + save the access token and refresh token inside the user profile. The subsequent request will then allow the user to create calendar events via the access token set in his profile.
Here is the code:
function getNewToken() {
const authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
return authUrl;
}
module.exports = {
insert(req, res, next) {
User.findById(req.user._id, (err, user) => {
if (user.accessToken) {
oauth2Client.setCredentials({
refresh_token: user.refreshToken
});
const calendar = google.calendar('v3');
calendar.events.quickAdd({ auth: oauth2Client, calendarId: 'primary', text: 'Test Calendar' }, (err, data) => {
if (err) {
next(err);
}
res.status(200).send('Successfully added calendar event');
});
} else {
const authUrl = getNewToken();
res.redirect(authUrl);
}
});
},
googleOauth2Callback(req, res, next) {
const code = req.query.code;
oauth2Client.getToken(code, (err, token) => {
if (err) {
next(err);
}
User.findByIdAndUpdate({ _id: req.user._id }, { accessToken: token.access_token, refreshToken: token.refresh_token }, (err) => {
if (err) {
next(err);
}
});
res.send({ token });
});
}
};
However, I've noticed that after a certain time passes, I get an "401 Invalid Credentials" error. I've noticed that if I omit the access_token and just set the refresh token in the oAuth2Client then everything works as expected.
Is this the right way to handle oAuth2 tokens? Can I just continue using only the refresh token in my API requests? What happens when the refresh token expires? Any help would be appreciated.

Is this the right way to handle oAuth2 tokens?
From what I understand, yes. the tokens are short-lived and will be replaced with a new one once access has been made.
Access tokens have limited lifetimes. If your application needs access to a Google API beyond the lifetime of a single access token, it can obtain a refresh token. A refresh token allows your application to obtain new access tokens.
Can I just continue using only the refresh token in my API requests?
You can continue to use the tokens if its part of the scope.
Access tokens are valid only for the set of operations and resources described in the scope of the token request. For example, if an access token is issued for the Google+ API, it does not grant access to the Google Contacts API. You can, however, send that access token to the Google+ API multiple times for similar operations.
What happens when the refresh token expires?
Once the access tokens expire, the application uses refresh token to obtain a new one.
In general, this diagram from the Identity documentation illustrates the process fully.
For more information about oAuth2 in client-side application, check out Web Apps section of the documentation.

Related

Access token revocation implementation in OAuth 2 (Node js)

I am using oAuth 2 package modified by him -> https://github.com/pedroetb/node-oauth2-server-mongo-example
I am stuck in the token revocation part. How to logout a user by calling revoke token?
var revokeToken = function(token, callback) {
tokenModel.deleteOne({
refreshToken: token.refreshToken
}).exec((function(callback, err, results) {
var deleteSuccess = results && results.deletedCount === 1;
if (!deleteSuccess) {
console.error('Token not deleted');
}
callback(err, deleteSuccess);
}).bind(null, callback));
};
The revoke token is done but I am not understanding how to call this one? do I have to do something like this?
app.post('/logout', revokeToken);
Postman screenshot:
From the node-oauth2-server library, only three APIs call available.
it are oauth.authorize(), oauth.authenticate() and oauth.token(), there are no revoke_token() API but we can workaround to revoke token by refresh token().
This is my approaches by refresh token method.
Adding this code in app.js of example
app.post('/logout', revokeToken, function(req, res) {
res.send('Congratulations, you are logged out!');
});
function revokeToken(req, res) {
var request = new Request(req);
var response = new Response(res);
return app.oauth.token(request, response)
.then(function(token) {
token.accessToken = '';
token.accessTokenExpiresAt = '';
token.refreshToken = '';
token.refreshTokenExpiresAt = '';
res.json(token);
}).catch(function(err) {
res.status(err.code || 500).json(err);
});
}
Demo
1. Run example
npm start
2. Ready to assign to Postman variable
3. Get Access token and Refresh Token
4. Check Valid access token
5. Logout - using refresh token and revoke the access token
6. Check valid access token - access-token no more valid due to revoked by refresh token.

How to use azure AD authentication from node.js server side non interactively

I have configured azure AD service and an user in it and used react-adal to get the token, which worked fine.But now i need to change this flow and instead have my own login form and send the credentials to node.js express server and verify these from azure ad without a login popup from azure and store the returned token in the passport session.I have tried using node-adal but not sure how this can be achieved ,Can this be done? Are there any examples for this.Thanks
Agree with #juunas, here is the non-interactive method(login with username and password) for your reference. This sample is used to manage MicrosoftGraph Resources.
var msRestAzure = require('ms-rest-azure');
var graphRbacManagementClient = require('azure-graph');
var tenantId='abcd-efgh-ijk-lmno-12345';
// Enter your tenant ID here which can be found from your Azure AD URL
// Eg. https://manage.windowsazure.com/example.com#Workspaces/ActiveDirectoryExtension/Directory/<TenantId>/users
msRestAzure.loginWithUsernamePassword('username#contosocorp.onmicrosoft.com', 'your-password', { tokenAudience: 'graph', domain: tenantId }, function (err, credentials, subscriptions) {
if (err) console.log(err);
var client = new graphRbacManagementClient(credentials, tenantId);
var userParams = {
accountEnabled: true,
userPrincipalName: 'OfficialStark#<yourdomain.com>', //please add your domain over here
displayName: 'Jon Snow',
mailNickname: 'OfficialStark',
passwordProfile: {
password: 'WinterisComing!',
forceChangePasswordNextLogin: false
}
};
client.users.create(userParams, function (err, user, request, response) {
if (err) return console.log(err);
console.log(user);
var userObjectId = user.objectId;
client.users.list(function (err, result, request, response) {
if (err) return console.log(err);
console.log(result);
client.users.deleteMethod(userObjectId, function (err, result, request, response) {
if (err) return console.log(err);
console.log(result);
});
});
});
});
There is a way, but you should not use it.
When using federated authentication, you should not be handling passwords.
The one way that works is a legacy migration path, named the ROPC flow.
However, none of these will work:
User with MFA
User synced from on-prem AD
User with expired password
If you want to do something as the user in the background, have them login and store their refresh token securely.
You'd need to exchange the front-end access token for a back-end access token + refresh token.
Then you can use the refresh token to get new tokens for the user whenever.
Alternatively you can require application permissions to APIs and access them using client credentials from your API (client id + secret/certificate).

Save jwt to local storage

I'm currently developing a node express postgresql application, and I'm trying to implement Jsonwebtokens as authentication. I've seen multiple tutorials on how to implement it and I get how to do it on the backend part, but the frontend is usually skipped and apparently everyone just tests their code with Postman.
I have also read online that the recommended way to implement jwt authentication is to store the generated token in localstorage, and, when needed, to send it on the header. But I wasn't able to find how this is done...
Thus, my questions are:
How do you store the token on the front-end once it's generated by the backend? (an example would help a lot, because I don't really get how am I supposed to get the token on a front-end javascript program)
How do you send the token on the headers when making an http request that needs it once you have it stored?
On the server side, once you have created the token and logged the user in, you send the token via res.send(), example below, note that you may have different approach to functions findByCredentials ad genereateAuthToken, they are custom:
app.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateAuthToken();
res.send({ token: user.tasks });
} catch (e) {
res.status(400).send();
}
});
On the frontend you can use html5's fetch() to send the token in the header. For example, if you would like to access '/users/me' that needs authentication you follow the steps below (make sure you however you save the token to localstorage first so you can access that via getItem:
localStorage.setItem('userInfo', JSON.stringify(userInfo));
document.getElementById("my-profile").addEventListener("click", getMe);
then:
function getMe(e) {
e.preventDefault();
var token = JSON.parse(localStorage.getItem('token'));
console.log(`Authorization=Bearer ${token}`)
fetch('/users/me', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token
}
})
.then(res => res.json())
.then(data => {
console.log(data)
// window.location.href = 'http://localhost:3000/dashboard';
})
.catch(err => { console.log(err) })
}
As you said, usually the token is store in localStorage.
localStorage is similar to sessionStorage, except that while data
stored in localStorage has no expiration time, data stored in
sessionStorage gets cleared when the page session ends — that is, when
the page is closed.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
For getting the token in front-end you send to a URL the email & password of the user in order to exchange it with a token (you have to be in https). After that you store it with localStorage.setItem('key', value)
Short example:
$.post("/authenticate", {email: userEmail, password: userPassword}, function(data) {
localStorage.setItem('token', data.token)
});
For get back the token, after a refresh for example, you have to use : localStorage.getItem('key').
And finally, in order to be authenticate with this token, you can send it in bearer headers in Authorization headers property.
Why bearer ? => https://security.stackexchange.com/questions/108662/why-is-bearer-required-before-the-token-in-authorization-header-in-a-http-re
Example:
$.ajax({
type: 'GET',
url: '/account,
headers: {
"Authorization": "Bearer " + token
}
}, function(data) {
// Authenticated data
});
May this can help : https://github.com/auth0-blog/angularjs-jwt-authentication-tutorial/blob/master/frontend/login/login.js

How to refresh access token for Google Analytics Reporting API v3

So I am working on a Node.js app that will poll Google Analytics api ever x amount of seconds. I have setup a "Service Account", and converted the p12 key to a .pem file. Initial setup looks something like this:
var authClient = new google.auth.JWT(authData.serviceEmail, authData.keyFile, null, authData.scope, '');
authClient.authorize(function(err, tokens) {
if (err) {
winston.log('error', 'Error authorizing with GA', {error: err});
return;
}
setInterval(function() {
analytics.data.realtime.get({
'auth': authClient,
...
}, function (err, body) {
// getting 401 error here
})
}, 20000);
});
I had not realized that the initial tokens have an expiration date of 1 hour; however the tokens I receive look like this:
{
access_token: ...,
token_type: 'Bearer',
expiry_date: NaN,
refresh_token: 'jwt-placeholder
}
My question is, once I get that 401 invalidCredentials error, do I simply just re-authorize to get a new access token to be able to poll from Google Analytics? I am new to JWT, and this seems like it will be authorizing way too many times. Is there a limit to this?
For reference, I am using the Google API Node.js Client Library
Yes, just rebuild authClient as you did the first time. Service Accounts don't have refresh tokens like other OAuth flows, you just rebuild the authentication when the current access token expires.

how to use access token to authenticate user after login through twitter in node.js passport

I need to build a token based authentication on my node.js app , that the user can use the his facebook or twitter credential to login my app, and use access token to get to resource. this post is suggesting to once authenticated through facebook or twitter or other, use access token on every request, and Session is NOT needed at all
For example
GET /api/v1/somefunction?token='abcedf'
The client gets the access token from the response.
The client calls some server api with the token argument.
so the following code, is to authorize user through twitter, if my app doesn't find my user information, then store user information into the database.
passport.use(new TwitterStrategy({
consumerKey: config.twitter.clientID,
consumerSecret: config.twitter.clientSecret,
callbackURL: config.twitter.callbackURL
},
function(token, tokenSecret, profile, done) {
console.log('TwitterStrategy /auth/twitter.............',profile.id, profile.displayName, profile.username, profile.emails[0], profile._json.avatar_url);
userModel.findUserByQuery({ 'social.twitter.id': profile.id }, function (err, user) {
if (!user) {
console.log('twitter user not found'.red);
userModel.createNewUser( { username:profile.username,
email:profile.emails[0].value,
img:profile._json.avatar_url,
fullname:profile.displayName,
password:profile._json.avatar_url,
social:{twitter:{id:profile.id,avatar:profile._json.avatar_url, name:profile.username,token:accessToken} }},
function(err,data){
if(err) return done(err);
else if(!data) return done(null, false, { message: 'can not create your profile in database' });
else {
console.log('save the new twitter user into database'.green, data._id);
return done(err, user);
}
})
} else {
console.log('twitter user found'.green);
return done(err, user);
}
})
}
))
However, I have two questions,
1. how to send the access token to the client for the following requests
in the code, after authenticated from twitter, I get the access token and send this token to the client on the browser, since the token is embedded in the url parameter, I tried the code
res.redirect ('/users/profile ? token = blabla '), but in the client browser, the url is still shown as '/users/profile' rather than '/users/profile ? token=blabla'
2. once authenticated from twitter, the following request with token is going through my app locally( which I store the token in database, and compare the following token to verify) or still to twitter API to authenticate?
if in the first situation, so I should store the token into the database, in order to compare the following request in token in the following requests to my app? is that right
I'm also trying hard to get this, and just found this rather relevant answer: Call Bitbucket REST API using 2leg oauth token
I just can't get how to do this with passport? Particularly, how to get ouath instance from passport authenticated session, to do oauth.get(...), as described here: https://github.com/ciaranj/node-oauth
UPDATE: Jared (author of passportjs) has explained this is wrong approach in the google groups thread below, and recommends to use https://github.com/mikeal/request.
This is how it works for me:
var oauth = {
consumer_key: process.env.BB_CONSUMER_KEY,
consumer_secret: process.env.BB_CONSUMER_SECRET
};
passport.use(new BitbucketStrategy({
...
function(token, tokenSecret, profile, done) {
oauth = extend(oauth, {
token: token,
token_secret: tokenSecret
});
}
Note, tokens above may need be persisted per user in clustered environment.
Later, to access api, do:
request.get({url: 'protected end point', oauth: oauth})
Hope it will help someone!

Resources