strongloop / loopback

LoopBack makes it easy to build modern applications that require complex integrations.
http://loopback.io
Other
13.23k stars 1.2k forks source link

Authenticating by Application.restApiKey #1879

Closed behrad closed 7 years ago

behrad commented 8 years ago

I couldn't be able to create a working example and use generated restApiKey in my application instance to secure and call REST endpoints.

0candy commented 8 years ago

@behrad What error are you getting?

behrad commented 8 years ago

I don't how I should use this @0candy ?! I'm currently using token based User authentication, however tokens need login, and do have TTL. I prefer permanent API keys for my REST clients. How can I integrate this across loopback's default token based authentication.

0candy commented 8 years ago

For now loopback supports token based authentication, but there is a feature request out there to support permanent tokens which I am unable to find at the moment. (Will post when I find it.)

Are you able to make authenticated request with access tokens for now? https://docs.strongloop.com/display/public/LB/Making+authenticated+requests#Makingauthenticatedrequests-Makingauthenticatedrequestswithaccesstokens

0candy commented 8 years ago

@behrad Were you able to get it working?

0candy commented 7 years ago

@behrad Closing due to inactivity. If you are still running into problems, feel free to leave a comment and I will reopen the issue.

behrad commented 7 years ago

I've overridden AccessToken#findForRequest to use Application's one of clientkey, restApiKey, masterKey, ... as a permanent token mechanism, However I'd rather to see this built in loopback core.

dyaa commented 7 years ago

@behrad can you please tell me how you implemented that !

i'm facing the same issue trying to authorize some end points using the restApiKey ...

behrad commented 7 years ago

I've a boot script which is overriding AccessToken.findForRequest @dyaa

note that the dirty part is that I was forced to copy/paste private tokenIdForRequest inside loopback auth imlementation.

module.exports = function (server) {
var _findForRequest = AccessToken.findForRequest.bind(AccessToken);
  AccessToken.findForRequest = function(req, options, cb){
    _findForRequest(req, options, function(err, token){
      if(!err && !token) { //check if the normal loopback token auth has found the token
        var reqToken = tokenIdForRequest(req, options);
        if( reqToken ) {
          Application
            .authenticateKey(reqToken)
            .then(token => {
              cb && cb(err, token);
            })
            .catch(err => {
              cb && cb(err);
            });
          return;
        }
      }
      cb && cb(err, token);
    });
  };

function tokenIdForRequest(req, options) {
    var params = options.params || [];
    var headers = options.headers || [];
    var cookies = options.cookies || [];
    var i = 0;
    var length;
    var id;
    // https://github.com/strongloop/loopback/issues/1326
    if (options.searchDefaultTokenKeys !== false) {
      params = params.concat(['access_token']);
      headers = headers.concat(['X-Access-Token', 'authorization']);
      cookies = cookies.concat(['access_token', 'authorization']);
    }
    for (length = params.length; i < length; i++) {
      var param = params[i];
      // replacement for deprecated req.param()
      id = req.params && req.params[param] !== undefined ? req.params[param] :
        req.body && req.body[param] !== undefined ? req.body[param] :
          req.query && req.query[param] !== undefined ? req.query[param] :
            undefined;
      if (typeof id === 'string') {
        return id;
      }
    }
    for (i = 0, length = headers.length; i < length; i++) {
      id = req.header(headers[i]);
      if (typeof id === 'string') {
        // Add support for oAuth 2.0 bearer token
        // http://tools.ietf.org/html/rfc6750
        if (id.indexOf('Bearer ') === 0) {
          id = id.substring(7);
          // Decode from base64
          var buf = new Buffer(id, 'base64');
          id = buf.toString('utf8');
        } else if (/^Basic /i.test(id)) {
          id = id.substring(6);
          id = (new Buffer(id, 'base64')).toString('utf8');
          // The spec says the string is user:pass, so if we see both parts
          // we will assume the longer of the two is the token, so we will
          // extract "a2b2c3" from:
          //   "a2b2c3"
          //   "a2b2c3:"   (curl http://a2b2c3@localhost:3000/)
          //   "token:a2b2c3" (curl http://token:a2b2c3@localhost:3000/)
          //   ":a2b2c3"
          var parts = /^([^:]*):(.*)$/.exec(id);
          if (parts) {
            id = parts[2].length > parts[1].length ? parts[2] : parts[1];
          }
        }
        return id;
      }
    }
    if (req.signedCookies) {
      for (i = 0, length = cookies.length; i < length; i++) {
        id = req.signedCookies[cookies[i]];
        if (typeof id === 'string') {
          return id;
        }
      }
    }
    return null;
  }

}
kflip commented 7 years ago

I think if you simply want to protect some REST paths with permanent keys, you could register a custom middleware for the auth-phase. ... you will run into problems when combining this middleware with other authentication methods for the same rest points.

The middleware would check for a http header: var apiKey = req.headers["x-api-key"];

If the key is not valid, you'd return the res object with res.status(401).send('Missing/Bad API Key');

carlos-herrera-cervantes commented 3 years ago

If you create the middleware, How can I show this in the loopback-component-explorer?. To use it in the documentation of REST API. Same as the token section to put the token and set the access token for all operations.