REST API Security and CrossPlatform - node.js

I'm working on rest api with node.js. I create jwt-token in /signup endpoint. Then send this token every ios-app,windowsphone-app requests in Authoriziton header. So api try find user which contain this token. If its have then return responses.
I'm worry about security. Everybody will monitoring api addresses for create user and get token then use this token for all endpoints. So will post data an infinate number to other schemas and application be garbage, dies.
How can prevent this situation ? Native iOS,Windows Phone apps and Web Site used to this api. Whats the best way handling security and user management somelike cross platform application api's ? Is there good documentation for this sturucture ?
There is user scheme.
var userSchema = mongoose.Schema({
token:String,
favorites:[favorites],
local : {
username:String,
email: String,
password: String,
sex:Number
},
facebook: {
id:String,
token:String,
email:String,
name:String
},
lists:[{listid:String,insertdate:Date}]
});
Every endpoint control authorization like this
function ensureAuthorized(req, res, next) {
var bearerToken;
console.log(req.headers);
var bearerHeader = req.headers["authorization"];
if (typeof bearerHeader !== 'undefined') {
req.token = bearerHeader;
User.findOne({'token':bearerHeader},function(err,currentUser){
if(err)
res.json({success:false,description:err});
console.log("current" + currentUser);
if(currentUser)
{
req.user = currentUser;
next();
}
else
{
res.status(403).end();
}
});
} else {
res.status(403).end();
}
}
Thank you all.

Related

Nextjs api and authentication

I'm in the process of building an application for stripe payments. This application generates a form that passes the data to the Stripe api via nextjs api. I just need to build in some basic authentication so only those submitting their payments via my form have access to the api. How would I go about adding some basic auth to my api without requiring users to login? Would I do this via env variable? I'm fairly new to the nextjs/vercel world and come from the python/django/react realm, so maybe my thoughts on this are backwards... I'm planning on hosting the api on vercel and the react form on a php site. So the react form will essentially push data to the vercel app api.
(The reason I'm not building the api in php is because I don't know php and because I'm attempting to build something with as little footprint in the current php site as possible.) Any help or guidance on this would be much appreciated!
My pages/api/customers.js file
import Stripe from 'stripe'
const stripe = new Stripe(process.env.SECRET_KEY)
export default async (req, res) => {
if (req.method === 'POST') {
try {
const { email, name, address, phone, source } = req.body
// Check for customer
const customerExist = await stripe.customers.list(
{
email: email,
limit: 0
})
// console.log('customerExist', customerExist.data[0])
if (customerExist.data.length < 1) {
const customer = await stripe.customers.create({
email,
name,
address,
phone,
source
})
res.status(200).send(customer.id)
} else {
res.status(200).send(customerExist.data[0].id)
}
} catch (err) {
res.status(500).json({ statusCode: 500, message: err.message })
}
} else {
res.setHeader('Allow', 'POST')
res.status(405).end('Method Not Allowed')
}
}
Part of my checkout form
// Function to check/create a user account via api
const checkUserAccount = async (billingDetails, source) => {
try {
const customer = await axios.post('/api/customers', {
email: billingDetails.email,
name: billingDetails.name,
phone: billingDetails.phone,
address: billingDetails.address,
source: source
})
return customer.data
} catch (err) {
console.log(err)
}
}
UPDATE:
Alright, so I added a "TOKEN" to my .env file and now require my api to receive that specific token.
I added this to my checkout form:
...
axios.defaults.headers.common.Authorization = process.env.AUTH_TOKEN
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
...
and then added this to the api:
if (req.method === 'POST' && req.headers.authorization === process.env.AUTH_TOKEN)...
Since I'm not using a login/logout system, I'm hoping this is enough. Thoughts or feedback are more than welcome.

Authentication service with Apollo Gateway

I am developing a microservices-based application using Apollo Gateway. Each service is written in Node.js and uses Graphql to build a federated schema. I am also using Mongoose to interact with a MongoDB database shared between the services. The main goal of the development of this app is to learn and gain experience using tools and technologies new to me as Graphql, microservices, and Node.js.
I have a question regarding authentication. I decided to use JWT based authentication with additional database-stored sessions for each user. In this way, I can monitor active sessions for each user and revoke access by disabling the session associated with the token. All of this is managed by the Auth service which is responsible for authenticating, creating new users and login/logout functions.
The Auth service exposes one REST endpoint to verify the jwt token as follows.
...
app.post('verify', async (req, res, next) => {
const token = req.body.jwt;
if(!token) {
res.status(403).send({ error: 'No token provided.' });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findOne({ _id: decoded.sub});
if (!user) {
res.status(401).send({ error: 'No user found.' });
}
const session = await Session.findOne({
'_id': {
$in: user.sessions
}
});
if(!session) {
res.status(401).send({ error: 'Session not found or expired.' });
}
if(!session.valid) {
res.status(401).send({ error: 'Session not valid.' });
}
res.send({
userId: user.id,
scopes: user.scopes
});
}
const server = new ApolloServer({
schema: buildFederatedSchema([
{
typeDefs,
resolvers
}
]),
context: ({ req }) => {
return {
// headers
userId: req.get['user-id'] || 0,
scopes: req.get['user-scopes'] ? req.get['user-scopes'].split(',') : [],
// Mongoose models
models: {
User,
Session
}
}
}
});
server.applyMiddleware({ app, cors: false });
...
My API gateway is based on Apollo Gateway to build the federated schema. Authentication is verified by the Auth service and shared with every other service via request headers set by the gateway.
...
// Set authenticated user id in request for other services
class AuthenticatedDataSource extends RemoteGraphQLDataSource {
willSendRequest({ request, context }) {
// pass the user's id from the context to underlying services
// as a header called `user-id`
request.http.headers.set('user-id', context.userId);
request.http.headers.set('user-scopes', context.scopes.join(','));
}
}
const gateway = new ApolloGateway({
serviceList: [
{ name: 'auth', url: 'https://auth:4000' }
],
buildService: ({ name, url }) => {
return AuthenticatedDataSource({ url });
}
});
// Apollo server middleware - last applied
const server = new ApolloServer({
gateway,
// not supported
subscriptions: false,
context: async ({ req }) => {
try {
// Send auth query to Auth service REST api
const response = await axios.post('https://auth:4000/verify', {
jwt: req.cookies['plottwist_login']
});
// save auth data in context
return {
userId: response.data.userId,
scopes: response.data.scopes
}
} catch(e) {
// deal with error
}
}
});
server.applyMiddleware({ app, path, cors: false });
...
This way the flow is the following:
API gateway receives the Graphql query request from the client.
API gateway queries the Auth service to authenticate the user using the only REST endpoint offered by the auth service (copying the token cookie from the received request).
Auth service authenticates the user and sends back data.
Gateway receives response, creates additional request headers and proceeds by managing the original Graphql query.
This comes with the cost of an extra call that the gateway makes before managing each Graphql query coming from the client. I wonder if this is a viable option or there is some major flaws in my reasoning.

Meteor client login using LDAP and JWT

I have a big CMS built with Meteor that until now used basic-auth to login as we didn't have more than one editor. However, more people will start working with it, so I am trying to create a login functionality through LDAP so any authorised employee can login with their username/password
I tried to do my best given poor documentation of WebApp.connectHandlers and the lack of content integrating passport with Meteor. I based my LDAP login code on express examples (assuming WebApp.connectHandlers.use() was the Meteor equivalent of Express' app.use())
The login part works, that is to query and verify the user through LDAP.
However I cannot figure how to identify the logged-in user when they make a Meteor.call( ).
What I have in mind at the moment is to send a JWT token back to the authenticated user, store it in a cookie or something, and whenever a Meteor.call( ) is made, the client passes the token as a parameter. Then I would be able to identify the caller and can for example store the username in the database as the person who made a certain change.
Good to mention that I am trying to avoid using Meteor's accounts system as the requirement is to use LDAP without creating any extra tables (that's why the complication of using those several packages together).
Here's my code, with working login but no idea how to pass back the token to the user.
Maybe my whole JWT logic is wrong, I would appreciate any help/suggestions.
var basicAuth = require("basic-auth");
var passport = require("passport");
var bodyParser = require("body-parser");
var LdapStrategy = require("passport-ldapauth");
var jwt = require("jsonwebtoken");
// Example of the real LDAP config
var OPTS = {
server: {
url: "ldap://address:port",
bindDN: "admin",
bindCredentials: "adminPassword",
searchBase: "OU=Users,DC=example,DC=com",
searchFilter: "(&(objectClass=user)(sAMAccountName={{username}}))"
},
credentialsLookup: basicAuth
};
Meteor.startup(() => {
passport.use(new LdapStrategy(OPTS));
WebApp.connectHandlers.use(bodyParser.json());
WebApp.connectHandlers.use(bodyParser.urlencoded({ extended: false }));
WebApp.connectHandlers.use(passport.initialize());
WebApp.connectHandlers.use(
"/",
(req, res, next) => {
// This part before the else is to trigger a basic auth popup to enter username/password
let credentials = basicAuth(req);
if (!credentials) {
res.statusCode = 401;
res.setHeader("WWW-Authenticate", "Basic");
res.end("Access denied");
} else {
passport.authenticate(
"ldapauth",
{
session: false
},
function(err, user, info) {
if (err) {
return next(err);
}
if (user) {
var token = jwt.sign(
{ username: user.sAMAccountName },
"someSecretString"
);
console.log("token", token);
next();
}
}
)(req, res, next);
}
},
function(req, res) {
console.log("debug point#2");
res.send({ status: "ok" });
}
);
}

Node/Express - Good approach to secure communication between client/server

I'm building a backend API with Node/Express, which get the data from a MongoDB. The front will be written in React.
I would like to secure the communication client/server, but I don't know how I have to think about the process.
I see many tutorial about passport or JWT, but this is good for an user authentication.
I don't know if creating a token for every request based on the time (for example) is a good approach or it's too consuming for a web app.
But my goal is to secure the data because even if the API is private you can easily find out the route and try to figure it out how to fake request with Postman or something else to scrap the data.
The accepted standard is to use a fixed API KEY. This peace of info should be a randomly generated string that you send in each request in the header. Your server has to check the HTTP request each time to see if the API KEY is present in the header, and if it is, then it has to check against the stored value in the environment variable (never store the API KEY in code).
If the API KEY gets compromised, then you can easily update the env variable, and you are good again.
Now, this solution will be pointless without a HTTPS connection, because anyone will be able to sniff the traffic and see the API KEY. An encrypted connection is a must in this case.
This approach is used by virtually every company that has a public API: Twitter, Facebook, Twilio, Google etc.
Google for example has an extra step where they give you a token that will expire, but this will be an over kill in your case: at least in the beginning.
The following code is an example of my implementation of a API KEY check
app.use(function(req, res, next) {
//
// 1. Check if the APIKey is present
//
if(!req.headers.authorization)
{
return res.status(400).json(
{
message: "Missing APIKey.",
description: "Unable to find the APIKey"
}
);
}
//
// 2. Remove Basic from the beginning of the string
//
let noBasic = req.headers.authorization.replace('Basic ', '');
//
// 3. Convert from base64 to string
//
let b64toString = new Buffer(noBasic, 'base64').toString("utf8");
//
// 4. Remove the colon from the end of the string
//
let userAPIKey = b64toString.replace(':', '');
//
// 5. Check if the APIKey matches the one on the server side.
//
if(userAPIKey != process.env.API_KEY)
{
return res.status(400).json(
{
message: "APIKey don't match",
description: "Make sure what you are sending is what is in your server."
}
);
}
//
// -> Go to the next stage
//
next()
});
You can check the whole file with the whole implementation hear.
As I just finished the auth part of my AngularJS application. The answer will be JWT and Passport, you should use the great technologies to protect your data / API.
If you use the JWT library, it will help you hold the http heads for authorization.
Some of the code I used:
app.js
var jwt = require('express-jwt');
var auth = jwt({
secret: config.jwt.secret,
userProperty: 'payload'
});
app.use('/api/secret', auth, apiSecretRoutes);
login.js
module.exports.login = function (req, res) {
if (!req.body.username || !req.body.password) {
return tools.sendJSONresponse(res, 400, {
message: 'All fields required!'
});
}
passport.authenticate('local', function (err, user, info) {
var token;
if (err) {
return tools.sendJSONresponse(res, 404, err);
}
if (user) {
token = user.generateJwt();
return tools.sendJSONresponse(res, 200, {
ok: true,
message: 'welcome ' + user.name,
token: token
});
} else {
return tools.sendJSONresponse(res, 400, info);
}
})(req, res);
};
user.js
userSchema.methods.generateJwt = function() {
var expiryDays = 1;
var expiry = new Date();
expiry.setDate(expiry.getDate() + expiryDays);
return jwt.sign({
_id: this._id,
username: this.username,
name: this.name,
exp: parseInt(expiry.getTime() / 1000)
}, config.jwt.secret);
};
More Refs:
https://thinkster.io/angularjs-jwt-auth
http://devdactic.com/restful-api-user-authentication-1/
http://devdactic.com/restful-api-user-authentication-2/
http://jwt.io
https://github.com/auth0/express-jwt

Loopback - Implementing custom authentication

We are developing a REST service but we already have an infrastructure in place to manage users. But we want to leverage the authentication and authorization mechanism of Loopback. The requirement is to
Add a remote method and receive the user credentials
Manually verify the credentials through stored procedure call
Generate the access token through Loopback
Going forward use Loopback authorization mechanisms such as roles in the application
Should I be implementing a custom login service provider using Loopback's third party login support ? I couldn't find a very good resource on this area. Any pointers would be much appreciated.
Please check some of the following examples to see if it fits your use case:
https://github.com/strongloop/loopback-example-access-control
https://github.com/strongloop/loopback-example-passport
My example is using a bootscript in express but you could easily change it into a remote method.
module.exports = function(app) {
//get User model from the express app
var UserModel = app.models.User;
app.post('/login', function(req, res) {
console.log(req.body);
//parse user credentials from request body
const userCredentials = {
"username": req.body.username,
"password": req.body.password
}
UserModel.findOne({
"where": {
"username": userCredentials.username
}
}, function(err, user) {
// Custom Login - Put the stored procedure call here
if (err) {
//custom logger
console.error(err);
res.status(401).json({
"error": "login failed"
});
return;
}
// Create the accesstoken and return the Token
user.createAccessToken(5000, function(err, token) {
console.log(token)
res.json({
"token": result.id,
"ttl": result.ttl
});
})
})
});
}
Now you can use that Token for Loopbacks authorization mechanism.

Resources