Sails REST API with simple AUTH - web-services

I'm pretty new to sails, but after read the doc and followed some examples at the Internet, I decided to give it a shot ;)
I have made an APP that depend on a REST webservice that I want to build in Sails Framework - but after a lots of research I haven't found the right solutions in sails yet.
I think I want to pass a (username, password) or a api_key in each webservice call made from the app?
All the examples that i found was only with a session login method - not with an API key in each call.
I used this tutorial - http://jethrokuan.github.io/2013/12/19/Using-Passport-With-Sails-JS.html
But only logins at post to login page - I want it to login in every call and still want to use the build in REST API blueprints.
The problem in my solution is that a call to like this - will not give me all the users as expected because of the default REST method - I want it to auth the user and give me the result ..
http://example.com:1337/user/?username=test&password=xxx
What is the "best practises" for building a APP with a REST webservice backend? - "with sails"
Some of my auth code:
// policies/authentication.js
if(req.param('username') && req.param('password')) {
UserAuth.auth(req, res, function(err, user) {
if (err) return res.forbidden('You are not permitted to perform this action.');
if(user) {
return next();
}
});
}else{
return res.forbidden('You are not permitted to perform this action.');
}
// services/UserAuth.js
module.exports = {
auth : function(req, res, cb) {
var bcrypt = require('bcrypt');
var passport = require("passport");
passport.authenticate('local', function(err, user, info){
if (err) return cb({ error: 'auth error!', status: 400 });
if(user) {
cb(null, user);
}
})(req, res);
}
}
// config/policies.js
module.exports.policies = {
'*': "authentication"
};

First off, it's bad practice to continuously expose usernames and passwords in the wild like this. At the very least, you should consider issuing access_tokens that expire after some time, and need to be re-issued via a login system.
Second, if you want to authenticate on every request (instead of using sessions), it's better to do so using a request header, rather than putting the credentials in the query string. This is especially true when using Sails blueprints; otherwise you'll have to do extra work to keep the blueprints from using your credentials as search criteria.
When using a header, per-request authorization becomes simple with Sails. Set up a policy in api/policies called (for example) auth.js:
module.exports = function (req, res, next) {
// Find an access header
var accessToken = req.header('my-auth-header');
// No header, no access
if (!accessToken) {return res.forbidden();}
// Find the user with that token
User.findOne({accessToken: accessToken})
.exec(function(err, user) {
// Handle error
if (err) {return next(err);}
// Handle bad access token
if (!user) {return res.forbidden();}
// Handle success
return next();
});
}
Then you can set any controller actions that need authentication using the config/policies.js file:
module.exports = {
SomeController: {
'*': 'auth'
},
...etc...
}

Related

How to create a NodeJS Authorization middleware on a serverless framework?

I'd like to create a middleware that checks the authorization header, decodes the token and sends the decoded data to the actual function, just like you would by adding userData to the request and using next() on an Express server, so the actual function gets back the decoded data on the req and it can then check what content to display to the user (if any).
I'm using Lambda functions on a serverless framework.
This was the function on my Express NodeJS local server:
const authorizerFunc = async (req, res, next) => {
let token;
try {
if (
req.headers.authorization &&
req.headers.authorization.split(" ")[0] === "Bearer"
) {
token = req.headers.authorization.split(" ")[1];
}
if (!token) {
req.userData = { userId: "", username: "" };
next();
return;
}
const decodedToken = jwt.verify(token, process.env.JWT_SECRET_KEY);
console.log("DECODED TOKEN", decodedToken);
req.userData = {
userId: decodedToken.userId,
username: decodedToken.username,
email: decodedToken.email,
};
next();
} catch (err) {
req.userData = { userId: "", username: "" };
next();
return;
}
};
The question is, how do I create a Lambda function that does this and sends the decoded data to the real function?
Edit: is it bad if I decode the auth token directly in the functions at the very beginning? I don't think it would add huge complexity to them.
Well, I don't have an actuall example for the serverless framework, but i can tell what you should do.
Create an Lambda Function to act as a Amazon API Gateway Lambda authorizer - you can see the documentation here - https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html
make sure you do the validation logic what you have defined, and also return the context object in the response - which you can define your user data
add the Amazon API Gateway Lambda authorizer to the API Gateway - https://docs.aws.amazon.com/apigateway/latest/developerguide/configure-api-gateway-lambda-authorization-with-console.html
If the authorization successful your rest api lambda can access the context object with the user data, which you customize in step 2

Nuxt Vuex Helper not sending Client Cookies to API

Okay, I have the bad feeling that I'm missing a key concept in what I'm doing. Hope someone can help me out with a hint.
I'm using Nuxt and Vuex Store Modules. Every fetch a Module Action does is wrapped in a helper Function (saveFetch) that I imported to decrease repetitive code, like this:
export const actions = {
async sampleAction(context, data){
...
await saveFetch(context, 'POST', '/pages', data)
...
}
}
The helper simple checks if the users accessToken is still valid, refreshes it if not and then sends the request:
export const saveFetch = async (context, method = 'POST', path, data) => {
const accessTokenExpiry = context.rootGetters['auth/getAccessTokenExpiry']
let accessToken = context.rootGetters['auth/getAccessToken']
// If the client provides an accessToken and the accessToken is expired,
// refresh the token before making the "real" fetch.
if (accessToken && accessTokenExpiry < new Date() && path !== '/auth/refresh-token') {
if (process.client) {
// Works fine
await window.$nuxt.$store.dispatch('auth/refreshToken')
} else {
// This is where the trouble starts
await context.dispatch('auth/refreshToken', null, { root: true })
}
accessToken = rootGetters['auth/getAccessToken']
}
return fetch(path, {
method,
headers: { ... },
body: JSON.stringify(data),
}
}
If the accessToken is expired the helper function dispatches a Vuex Action to refresh it. This works well on the client side, but not if that process happens on the server side.
The Problem that's coming up on the server side is, that the user has to provide a refreshToken to get a refreshed accessToken from the API. This refreshToken is stored as a HttpOnly Cookie in the Client. When logging the Nuxt request details on the API side of things I noticed, that Nuxt is not sending that cookie.
My current workaround looks like this:
export const actions = {
async refreshToken(context){
...
let refreshToken
if (process?.server && this?.app?.context?.req?.headers?.cookie) {
const parsedCookies = cookie.parse(
this.app.context.req.headers.cookie
)
refreshToken = parsedCookies?.refreshToken
}
const response = await saveFetch(context, 'POST', '/auth/refresh-token', {
refreshToken,
})
...
}
...
}
If on server side, access the req object, get the cookies from it and send the refreshToken Cookie Content in the requests body.
This looks clearly bad to me and I would love to get some feedback on how to do this better. Did I maybe miss some key concepts that would help me not get into this problem in the first place?

how to use session in loopback using middlewre

I m new to loopback and don't know how to do following things in loopback
I want to set access token and other value in a session using middleware for that I found this thing in server folder of loopback
"session": {},
in middleware.json but don't know how to use this because there is not much documentation
I want to condition in session middleware like if I has session value then continue else throw to login page
note i already install this npm install express-session
Could you be a little more specific about what you want? but I'll explain a little bit about how authentification sessions are handled, there are two native ways you treat it all; The first one would be using a more raw reading pulling for modeling of your api and the second would be to use the JWT in aligned with accessToken and Passport.JS.
There are two examples available today with Loopback 3.x
loopback-example-user-management
loopback-example-passport
Basically using the raw reading with app.post('/login', function(req, res) then if your client is successfully authenticated you generate a cookie using your client's accessToken, example res.cookie('access_token', token.id, { signed: true , maxAge: 300000 }); res.set('X-Access-Token', token.id); and finally if you want you can transport the generated token to your pages:
res.render('home', {
email: req.body.email,
accessToken: token.id
});
Now with Passport.JS a middleware is used to secure all your connection and authentication:
app.middleware('session:before', cookieParser(app.get('cookieSecret')));
app.middleware('session', session({
secret: 'Seal Playing Saxophone',
saveUninitialized: true,
resave: true,
}));
passportConfigurator.init();
One of the authenticated page rendering pillar is var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn; you can use this ensureLoggedIn('/login') to free up your routes:
app.get('/auth/account', ensureLoggedIn('/login'), function(req, res, next) {
res.render('pages/loginProfiles', {
user: req.user,
url: req.url,
});
});
Now if you just want to skip this all and already have your environment set up and just want to create a route to get the accessToken of the logged in client use the template below;
app.get('/session-details', function (req, res) {
var AccessToken = app.models.AccessToken;
AccessToken.findForRequest(req, {}, function (aux, accesstoken) {
// console.log(aux, accesstoken);
if (accesstoken == undefined) {
res.status(401);
res.send({
'Error': 'Unauthorized',
'Message': 'You need to be authenticated to access this endpoint'
});
} else {
var UserModel = app.models.user;
UserModel.findById(accesstoken.userId, function (err, user) {
// show current user logged in your console
console.log(user);
// setup http response
res.status(200);
// if you want to check the json in real time in the browser
res.json(user);
});
}
});
});
I hope I have illuminated your ideas :] I am here to answer your questions.

ember-simple-auth is not saving auth response to session

I'm using ember-simple-auth and I can see that the authentication token is returned and that my custom authenticator is putting it (code-wise) into the session.
I can use wireshark to see that the token comes back but I can't debug into the authenticator code - I can't even use debug statements as the redirect wipes the network session on google chrome.
However, it isn't in the session (google chrome tools) and the session isn't considered authenticated.
Why is the lock code correctly popping up the auth0 dialog but not saving the session? I don't really think it is something with this code but rather a config or initialization setting I'm unaware of.
I expect the session to be saved in the _setupFutureEvents as the first thing it does.
Any ideas what I can try to get it to work?
(/app/authenticators/lock.js) authenticate (options) {
return new Ember.RSVP.Promise((res) => {
this.get('lock').show(options, (err, profile, jwt, accessToken, state, refreshToken) => {
if (err) {
this.onAuthError(err);
} else {
var sessionData = { profile, jwt, accessToken, refreshToken };
// pass the NEW auth0 session data into future events
this.afterAuth(sessionData).then(response => res(this._setupFutureEvents(response)));
}
});
});
},
afterAuth (data) {
return Ember.RSVP.resolve(data);
},
_setupFutureEvents (data) {
// set the session info here
this.get('sessionData').setProperties(data);
this._clearJobs();
this._scheduleExpire();
if (this.get('hasRefreshToken')) {
this._scheduleRefresh();
}
return this.get('sessionData');
},

Redux: What is the correct place to save cookie after login request?

I have the following situation: The user enters his credentials and clicks a Login button. An API call is done in the action creator via redux-thunk. When the API call was successful, another action is dispatched containing the response from the server. After the (successful) login I want to store the users session id in a cookie (via react-cookie).
Action creator
export function initiateLoginRequest(username, password) {
return function(dispatch) {
dispatch(loginRequestStarting())
return fetch('http://path.to/api/v1/login',
{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
password: password
})
})
.then(checkStatus)
.then(parseJSON)
.then(function(data) {
dispatch(loginRequestSuccess(data))
})
.catch(function(error) {
dispatch(loginRequestError(error))
})
}
}
export function loginRequestSuccess(user) {
return {
type: ActionTypes.LOGIN_REQUEST_SUCCESS,
user
}
}
Reducer
export default function user(state = initialState, action) {
switch (action.type) {
case ActionTypes.LOGIN_REQUEST_SUCCESS:
cookie.save('sessionId', action.user.sid, { path: '/' })
return merge({}, state, {
sessionId: action.user.sid,
id: action.user.id,
firstName: action.user.first_name,
lastName: action.user.last_name,
isAuthenticated: true
})
default:
return state
}
}
Right now the reducer responsible for LOGIN_REQUEST_SUCCESS saves the cookie. I know the reducer has to be a pure function.
Is saving a cookie in the reducer violating this principle? Would it be better to save the cookie inside the action creator?
Have a look at redux-persist.
You can persist/save your reducers (or parts of them) in LocalStorage.
Concept
Initiate login.
Receive cookie from server.
Dispatch login success.
Reducer stores cookie in memory.
Persist middleware stores reducer state in LocalStorage.
Example
Install
npm install --save-dev redux-persist
Example Usage
Create a component that wraps the persistence/rehydration logic.
AppProvider.js
import React, { Component, PropTypes } from 'react';
import { Provider } from 'react-redux';
import { persistStore } from 'redux-persist';
class AppProvider extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
children: PropTypes.node
}
constructor(props) {
super(props);
this.state = { rehydrated: false };
}
componentWillMount() {
const opts = {
whitelist: ['user'] // <-- Your auth/user reducer storing the cookie
};
persistStore(this.props.store, opts, () => {
this.setState({ rehydrated: true });
});
}
render() {
if (!this.state.rehydrated) {
return null;
}
return (
<Provider store={this.props.store}>
{this.props.children}
</Provider>
);
}
}
AppProvider.propTypes = {
store: PropTypes.object.isRequired,
children: PropTypes.node
}
export default AppProvider;
Then, in your index.js or file in which you set up the store, wrap the rendered components in your new AppProvider.
index.js
...
import AppProvider from 'containers/AppProvider.jsx';
...
render((
<AppProvider store={store}>
...
</AppProvider>
), document.getElementById('App'));
This will serialize your user reducer state to LocalStorage on each update of the store/state. You can open your dev tools (Chrome) and look at Resources => Local Storage.
I'm not sure if this is the "right" way, but that's how my team is persisting the logged user in the Redux app we built:
We have a very default architecture, an API ready to receive requests in one side, and a React/Redux/Single Page app that consumes this API endpoints in the other side.
When the user credentials are valid, the API's endpoint responsible for the login respond to the app with the user object, including an access token. The access token is latter used in every request made by the app to validate the user against the API.
When the app receives this user information from the API two things happen: 1) an action is dispatched to the users reducer, something like ADD_USER, to include this user in the users store and 2) the user's access token is persisted in the localStorage.
After this, any component can connect to the users reducer and use the persisted access token to know who is the logged user, and of course if you have no access token in your localStorage it means the user is not logged in.
In the top of our components hierarchy, we have one component responsible to connect to the users reducer, get the current user based on the access token persisted in the localStorage, and pass this current user in the React's context. So we avoid every component that depends on the current user to have to connect to the users reducer and read from the localStorage, we assume that this components will always receive the current user from the app's context.
There are some challenges like token expiration that adds more complexity to the solution, but basically this is how we are doing it and it's working pretty well.
I'd probably have the server-side set the cookie, personally, and make it transparent to JavaScript. But if you really want to do it client-side, I'd do it in an action helper. Something like this:
// Using redux-thunk
function login(user, password) {
return dispatch => api.auth.login(user, password)
.then(result => setCookie())
.then(() => dispatch({type: 'USER_LOGGED_IN'}))
}
Or something like that.
Action helpers don't need to be pure, but reducers should be. So, if I'm doing side-effects, I put them into action helpers.