I'm currently working on a angular + sails project. I'm using json web tokens for auth. It works fine but I wanna set a new token for every validated request that my angular app does.
This is my auth policy
passport.authenticate('jwt', function (error, user, info) {
if (error) return res.serverError(error);
if (!user)
return res.send({
message: info.message,
code: info.code,
tokenError: info.name
});
// The token is ok past this line
// I check the user again
User.findOne({ email: user.email }, function (err, thisUser) {
if (err) { return res.send(err); }
if (!thisUser) {
// send a bad response
}
req.user = user;
// This is the new token that I wanna send to the frontend
var newToken = AuthService.createToken(thisUser);
next();
});
})(req, res);
With this policy I can create the new token, but then I would need a way to include this token in every response, this Is the point where I'm stuck.
I gues I could do it manually in every controller action, but this is want I want to avoid
The best way to standardize your responses in Sails is to use the custom responses feature. In short, instead of calling res.send() or res.json() in your controller actions, call res.ok() instead, and then customize the api/responses/ok.js file that is generated with every new Sails app. This is the same response that Sails blueprints use as well!
In your case, you'd want to save the token onto the request object (e.g. req.token) in your policy code, then use that property in your logic inside of ok.js.
Related
I am new to Auth0 and trying to implement it in my regular express web application. I need to protect/validate the user before they access some of my endpoints. My understanding is that i can do this with the JWT that is returned from the login callback. I have gotten that far, but when I login, it redirects, and I'm unsure of how to pass in the access token/store it securely on the client side.
this is what my callback endpoint looks like after logging in. It returns the authorization code but I am lost from here.
https://auth0.com/docs/api-auth/tutorials/authorization-code-grant
I return this on login:
/callback?code=oi9-ZTieXo0hYL6A&state=sMJAUK4QVs7jziJ7lXvwmGKF
// Perform the final stage of authentication and redirect to previously requested URL or '/user'
router.get('/callback', function (req, res, next) {
passport.authenticate('auth0', function (err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function (err) {
if (err) { return next(err); }
const returnTo = req.session.returnTo;
delete req.session.returnTo;
res.redirect('/user);
});
})(req, res, next);
});
where do i go from here?
Auth0 does not recommend storing tokens in browser storage (session/local storage). For client side applications, tokens should be short lived and renewed when necessary via silent authentication (renewed via a cookie session with the auth server in a hidded iframe).
This is outlined here:
https://auth0.com/docs/security/store-tokens
If you have a backend, then handle the tokens there, if you are using a SPA + API then use the strategy outlined in the link.
I am playing around with this library and I am experiencing an annoying scenario which I believe comes from some sort of conflict in cookies or headers authentication.
When I login to one account everything works great. But then when trying to login to another account, it simply ignore the new data provided and move through the authentication with the old data and connecting to the old account. No matter if the email or the password even exist. (Tried also with fake data).
The library doesn't have proper logout method which make sense, you dont really need one because when you run it simply using node on your machine without any server involved and there is no cookies or any kind of data in memory, everything work great. I can login to as many account as I want.
The problem is when running it on an Express server.
CODE:
// api.js
const OKCupid = require("./okc_lib");
const Promise = require("bluebird");
const okc = Promise.promisifyAll(new OKCupid());
async function start(req, res, next) {
const {
body: {
username,
password
}
} = req;
try {
await okc.loginAsync(username, password);
okc.search(
searchOpt,
(err, resp, body) => {
if (err) return console.log({ err });
const results = body.data;
// do dsomething with results
return res.status(200).json({ message: "OK" });
});
}
);
} catch (error) {
res.status(400).json({ message: "Something went wrong", error });
}
}
module.exports = { start };
// routes.js
const express = require("express");
const router = express.Router();
const { start, login } = require("../actions/okc");
router.post("/login", login);
router.post("/start", start);
module.exports = router;
So when trying first to post to url/login it works fine. But when you try to do it again with different username and password it simply go through and ignore the new data and connect to the old one.
As part of my investigation I looked at the source code of the library and found a method clearOAuthToken which clear the token from the header. However it didn't really do anything. So I tried to remove the jar initialisation from the requester helper and it was the only thing that helped me to move on and login to another account. BUT it was only for experimenting and cant be a solution as you do need those cookies for other parts of the library. It was only a proof the problem isn't in the headers but in the cookies.
Any idea how can I "reset" state of server between each call?
"when trying to login to another account, it simply ignore the new data provided and move through the authentication with the old data and connecting to the old account."
As OP mentioned in the comment, this is not an authorization header issue, but a cookie issue.
To implement the logout interface, you can manually clear the cookies:
OKCupid.prototype.logout = function(){
request = request.defaults({jar: request.jar()}) // reset the cookie jar
headers.clearOAuthToken(); // just in case
}
I have some routes in my Node.js API sending data from a MongoDB database to an Angular 4 frontend.
Example:
Node.js route:
router.get('/api/articles', (req, res) => {
Article.find({}, (err, articles) => {
if(err) return res.status(500).send("Something went wrong");
res.status(200).send(articles);
});
});
Angular 4 service function:
getArticles() {
return this.http.get('http://localhost:3000/api/articles')
.map(res => res.json()).subscribe(res => this.articles = res);
}
The question is, how do I protect my Node.js API routes from browser access? When I go to http://localhost:3000/api/articles I can see all my articles in json format.
This is not a security measure, just a way to filter the request. For security use other mechanisms like JWT or similar.
If the angular app is controlled by you then send a special header like X-Requested-With:XMLHttpRequest (chrome sends it by default for AJAX calls) and before responding check for the presence of this header.
If you are really particular about exposing the endpoint to a special case use a unique header may be X-Request-App: MyNgApp and filter for it.
You can't really unless you are willing to implement some sort of authentication — i.e your angular user will need to sign into the api.
You can make it less convenient. For example, simply switching your route to accept POST request instead of GET requests will stop browsers from seeing it easily. It will still be visible in dev tool or curl.
Alternatively you can set a header with your angular request that you look for in your express handler, but that seems like a lot of work for only the appearance of security.
Best method is to implement an authentication token system. You can start with a static token(Later you can implement dynamic token with authorisation).
Token is just a string to ensure the request is authenticated.
Node.js route:
router.get('/api/articles', (req, res) => {
let token = url.parse(req.url,true).query.token; //Parse GET param from URL
if("mytoken" == token){ // Validate Token
Article.find({}, (err, articles) => {
if(err) return res.status(500).send("Something went wrong");
res.status(200).send(articles);
});
}else {
res.status(401).send("Error:Invalid Token"); //Send Error message
}
});
Angular 4 service function:
getArticles() {
return this.http.get('http://localhost:3000/api/articles?token=mytoken') // Add token when making call
.map(res => res.json()).subscribe(res => this.articles = res);
}
With Express, you can use route handlers to allow or deny access to your endpoints. This method is used by Passport authentication middleware (which you can use for this, by the way).
function isAccessGranted (req, res, next) {
// Here your authorization logic (jwt, OAuth, custom connection logic...)
if (!isGranted) return res.status(401).end()
next()
}
router.get('/api/articles', isAccessGranted, (req, res) => {
//...
})
Or make it more generic for all your routes:
app.use('*', isAccessGranted)
I have a REST endpoint that is being submitted with a JSON object (User) and I just set the corresponding mongo record to that JSON object. This saves me the trouble of updating schema changes in the service method and the endpoint leaving just the Mongoose model to update.
What would be a more secure way of doing this, if any?
Example User JSON
{
'fname': 'Bill',
'lname': 'Williams',
'email': 'bill#billwilliams.com',
'settings': {
'strokeColor': '#FF0000'
}
}
From my Angular service
Update: function(my_user) {
return $http.put('http://api.domain.com/v1/api/users/' + _user.id, {
user: my_user,
token: window.localStorage['token']
});
}
My REST endpoint in Node
api.route('/users/:user_id')
.put(function(req, res) {
User.findById(req.params.user_id, function(err, user) {
userData = req.body.user;
if (user) {
//-- This is potential trouble area?
User.update({'_id': user._id}, {$set: userData});
user.save(function(err) {
res.json({
success: true,
message: 'User updated'
});
}); //-- end findById()
}); //-- end /users/:user_id put() route
Have a look at Jsonwebtoken.
It basically works like this:
Create a REST endpoint that lets users aquire a token with a certain
payload (id for example)
Secure the relevant part of your api with the Jsonwebtoken middleware (if using express as the webserver)
User adds the token to every request header (by using $httpInterceptor)
Token is checked on the server side before the request reaches your API
Tokens may expire after a certain time (useful when users needs to register first) which adds additional security.
I'm trying to build a simple application with parse.com as my user manager.
I would like to make a login call to parse.com from my client side, and call my node.js server with the user's session token (I'll add it as a cookie). In the server side, I'll validate the session (using https://parse.com/docs/rest#users-validating) and allow access only if the session is valid.
For example (in my server):
app.get('/api', function(req, res, next) {
var token = getTokenFromRequest(req);
if(tokenIsValid(token)) {
next();
} else { // Redirect... }
});
app.get('/api/doSomething', function(req, res) {
// Do something....
});
the tokenIsValid(token) function should be implemented using https://parse.com/docs/rest#users-validating.
However, it seems that the REST API user validation returns the user even if the user is logged out (expected to return 'invalid session').
Is this a bug in the REST API user validation? What am I doing wrong? Is there a better way for doing that?
Thanks!
Via REST there's no concept of sessions really. REST calls are meant to be stateless meaning that the (current) user at /me will be serialized from the token provided. If the token is associated to a user it will return the JSON representation of that user otherwise in returns an error.
One way or another that call is asynchronous so you can't really use it in and if statement.
You can do:
app.get('/api', function(req, res, next) {
var token = getTokenFromRequest(req);
serializeUserFromToken(token,function(err,parseResponse) {
if(err) return next(err)
if(parseResponse.code && parseResponse.code === 101){
// called to parse succedded but the token is not valid
return next(parseResponse);
}
// parseResponse is the current User.
next();
});
});
Where serializeUserFromToken makes a request to Parse with the token in the X-Parse-Session-Token header field.