Access token revocation implementation in OAuth 2 (Node js) - 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.

Related

Node.js Express Spotify API save in session

Question appeared while integrating Spotify API into Nodejs Express web application using spotify-web-api-node. How multiple simultaneous user requests should be handled? After passing the authentication step, user receives access_token, which is different for each user. Each request can have a session, for example using express-session since access_token is unique for each authenticated user. The weird thing is that I can't find an example with proper session usage in the description and samples https://www.npmjs.com/package/spotify-web-api-node where spotify-web-api-node is used. How is that possible to use global variable without session? Would it make full mess among separate user requests or I'm missing something? I guess that the access_token would be always replaced with latest authenticated user. Another usage example is here https://github.com/thelinmichael/spotify-web-api-node, though it also suggests to use one global instance.
the solution is to store the access_token and refresh_token after successful authentication in the session storage, than before calling Spotify API endpoints set both tokens for the current user from the present session:
saving tokens in the session after successful authentication:
app.get('/login', (req,res) => {
var scopes = [ ... ]
var authUrl = spotifyApi.createAuthorizeURL(scopes)
res.redirect(authUrl+"&show_dialog=true")
})
app.get('/callback', async (req, res) => {
const { code } = req.query
try {
var data = await spotifyApi.authorizationCodeGrant(code)
const { access_token, refresh_token } = data.body
spotifyApi.setAccessToken(access_token)
spotifyApi.setRefreshToken(refresh_token)
req.session.spotifyAccount = { access_token, refresh_token }
res.redirect('...')
} catch(err) {
res.send(`error ${err}`)
}
});
app.get('/userinfo', async (req,res) => {
try {
spotifyApi.setAccessToken(req.session.spotifyAccount["access_token"])
spotifyApi.setRefreshToken(req.session.spotifyAccount["refresh_token"])
var result = await spotifyApi.getMe()
console.log(result.body);
res.status(200).send(result.body)
} catch (err) {
res.status(400).send(err)
}
});
since access_token is only identification key which identifies any API request, that ensures that API endpoints are called for the current user. This technique prevents mess and confusion, so that each user can see and manipulate his data only.

How to send Bearer token to client and then call token from client

I have done a tutorial trying to get my head around JWT tokens. I seem to have got my head around the token creation as well as using the token to allow or disallow access to a route.
This all works great using postman, but in postman I enter the token under authorization. My question is:
1. how do I send the token to the client so it is saved on that client side.
2. How does the client return the token when they try to access a route?
I need to understand how this happens when NOT using postman. I am sure its pretty simple.
Do I just send
`res.header('Authorization', 'Bearer', + token);`
`res.header('Authorization', 'Bearer' + token);`
But can I send this with other stuff like a message / data etc?
Then when the user tries to access a protected route later, How do I access this header. IOW how is it stored client-side?
This is what I have thus far:
`//login route`
`app.post('/login', async function(req, res, next) {
const { name, password } = req.body;
if (name && password) {
let user = await getUser({ name: name });
if (!user) {
res.status(401).json({ message: 'No such user found' });
}
if (user.password === password) {
// from now on we'll identify the user by the id and the id is the
// only personalized value that goes into our token
let payload = { id: user.id };
let token = jwt.sign(payload, jwtOptions.secretOrKey);
res.json({ msg: 'ok', token: token });
} else {
res.status(401).json({ msg: 'Password is incorrect' });
}
}
});`
`// protected route
app.get('/protected', passport.authenticate('jwt', { session: false }), function(req, res) {
console.log('REQUEST HEADERS ON PROTECTED::',req.headers.authorization)
res.json('Success! You can now see this without a token.');
});`
The console.log under protected route gives me:
"REQUEST HEADERS ON PROTECTED:: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTU2NjI3NTczfQ.gAU2VzpUpXHpcgM6_n8gf7D-xLCS59tK6K2RIlIk-L4" but I gather this is because I used the authorization in postman.
I recently worked with jwt auth using react as my front end and hapi.js as backend. To save the token on the client side, you can use localstorage like this:
You have to save this on the user login component.
localStorage.setItem('token', res.data.token);
And then, to access this token on the protected router, use this :
let token = localStorage.getItem('token');
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
I hope this may help you to solve your problem on the client side.

How can you verify if a JWT is still valid?

I want to make a call every X amount of minutes from the client side to see if the JWT is still valid. I'm not sure how to do this in nodeJS. If I'm already authorized, how can i check if I'm still authorized.
An elegant solution to handle token expiration is when you set the token(in LocalStorage or store(redux), or both) is also to have an Async function that runs exactly when the token expires. Something like this:
const logUserOut = token =>{
setTimeout(()=> MyLogoutFunction(), token.expiresIn)
}
This way you make sure that the user won't be logged when the token is no longer valid.
You can have your client side decode the JWT and check an expiry field and compare it with system time.
eg.
isExpired: (token) => {
if (token && jwt.decode(token)) {
const expiry = jwt.decode(token).exp;
const now = new Date();
return now.getTime() > expiry * 1000;
}
return false;
you can use npm install jsonwebtoken or some other npm package on the client side to do this
Create and endpoint that verifies the token is valid. You can use the the jsonwebtoken package.
import jwt from 'jsonwebtoken';
const verifyToken = (req, res) => {
const token = req.headers.authorization;
jwt.verify(token, SECRET_KEY, (err, decoded) => {
if (err) {
return res.status(401).send();
}
// can do something with the decoded data
})
}
router.post('/verify-token', verifyToken);
I found a better option with promise to check if my token is valid
jwt.verify(token,key,(err,result)=>{
if(err){
if(err.name == "TokenExpiredError"){
console.log("Expired") //This case is when token expired
}
else{
console.log(err.name) //Any other case
}
}
else{
//Here code for your promise using 'result' when token is Valid
}
})
EDIT: this code is OK if you don't use the JWT for security reason, only if you use it to public stuff.
since this request is show to user if it valid or not, and if not why.
I'm using it only to understand if object is still available for other reason. (and it's OK to be public)

Firebase 3.0 Tokens : [Error: Invalid claim 'kid' in auth header.]

I'm trying to create JWT tokens in node.js for use with the REST api in firebase, but when I try to use them, I get the error "Error: Invalid claim 'kid' in auth header."
This is my code
http.createServer(function (req, res) {
var payload = {
uid: "bruh"
};
var token = jwt.sign(payload, sact["private_key"], {
algorithm: 'RS256',
issuer: sact["client_email"],
subject: sact["client_email"],
audience: 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
expiresIn: '3600s',
jwtid: sact["private_key_id"],
header: {
"kid": sact["private_key_id"]
}
});
res.writeHead(200);
res.end("It worked. (" + token + ")");
}).listen(port);
These are my requires
var http = require('http');
var jwt = require('jsonwebtoken');
Please use returnSecureToken: true, with correct Spellings
I hope it will solve the problem of Invalid claim 'kid' in the auth header.
This is an issue because you're generating a Firebase ID token, not an access token for the Firebase REST API.
To generate a REST API token I would use the legacy Firebase Token Generator library which still works perfectly well (but only generates REST tokens, not general purpose access tokens).
Note that your Firebase Database secret is now located under the gear icon in the top left of the console.
So I had this error and I've fixed it. Now here is the solution:
You'll need to retrieve the ID-token using an additional function. Here is the function you can use:
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
I implemented it somewhat like this:
//google OAuth login handler
const googleLoginHandler = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
.signInWithPopup(provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
setgoogleAuthStatus(true)
// The signed-in user info.
const userId = result.user.uid;
const displayName = result.user.displayName;
const email = result.user.email;
//This is the function for getting the ID-Token
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then((idToken) => {
// Send token to your backend via HTTPS
console.log(idToken)
}).catch((error) => {
// Handle error
console.log(error.message)
alert(error.message)
});
console.log(result)
}).catch((error) => {
console.log(error)
// Handle Errors here.
alert(error.message)
})
}
The id token you get by this method can be used to access the firebase real-time database and other firebase services.
check out these links for more details:
https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients
https://firebase.google.com/docs/database/rest/auth#firebase_id_tokens

Nodejs google oAuth2 invalid credentials

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.

Resources