0
votes

I am working on an iOS application which communicates with a nodejs backend server REST API and I am thinking about API authentication.

I want that only the iOS application can communicate with the API.

On iOS application side, users are authenticated through Facebook login. They thus get a fb access_token and a fbid after authentication on the iOS app.

For the API authentication, I plan to make it this way:

  • When the user logs in into the iOS app, a call to /api/auth with his fb access_token and fbid is done;
  • If the user is new, I create a random api_token for this user, store it into Users DB, and send it back to the iOS app;
  • If the user is already in the DB, I refresh the fb access_token in my DB and the api_token and send it back to the iOS app;
  • For each API call, I give the api_token as a POST parameter and on server side I check if it is valid by fetching in the DB before executing the API call.

Am I missing something to be enough secured?

Any feedback or improvement will be very welcome.

Regards,

EDIT:

Another way would be the following:

  • On /api/auth I checked on FacebookB API (/me) if the fb access_token is still valid;
  • If not I refuse the authentication;
  • If yes I create and manage my api_token with JSON Web Tokens.
1

1 Answers

0
votes

If anyone is interested, I finally implemented the second solution. Very simple and do the job!

config.js

module.exports = {
    'secret': 'apisupersecrethere',
};

route.js

var config = require('./config');
app.set('api_secret', config.secret);
api = express.Router();

// function that checks the api_token
api.use(function(req, res, next) {
    var token = req.headers['x-access-token'];
    if (token) {
        jwt.verify(token,app.get('api_secret'),function(err, decoded) {
            if (err) {
                return res.json({
                    success: false,
                    message: 'Failed to authenticate token.'
                });
            } else {
                req.decoded = decoded;
                next();
            }
        });
    } else {
        return res.status(403).send({ 
            success: false, 
            message: 'No token provided.'
        });
    }
});

// route protected by the authentication
router.use('/users', api);

// authentication route
router.post('/auth', function(req, res) {
    verifyFacebookUserAccessToken(req.body.access_token).
        then(function(user) {
            var token = jwt.sign(user, app.get('api_secret'), {
                expiresIn: 1440*60 // expires in 24 hours
            });
            res.status(200).json({
                success: true,
                message: "Authentication success!",
                token: token
            });
        }, function(error) {
            res.status(401).json({
                success: false,
                message: error.message
            });
        }).
        catch(function(error){
            res.status(500).json({
                success: false,
                message: error.message
            });
        });
    });

// Call facebook API to verify the token is valid
function verifyFacebookUserAccessToken(token) {
    var deferred = Q.defer();
    var path = 'https://graph.facebook.com/me?access_token=' + token;
    request(path, function (error, response, body) {
        var data = JSON.parse(body);
        if (!error && response && response.statusCode && response.statusCode == 200) {
            var user = {
                facebookUserId: data.id,
                username: data.username,
                firstName: data.first_name,
                lastName: data.last_name,
                email: data.email
        };
            deferred.resolve(user);
        }
        else {
            deferred.reject({
                code: response.statusCode,
                message: data.error.message
            });
        }
    });
    return deferred.promise;
}

Any feedback welcomed.

Regards