Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

loopback.io rest connector - how to pass through oAuth token

Using loopback, I have created a connection to an existing API using the REST connector, which is working well. I would however like to pass through the oAuth token coming from the client.

I can get hold of the oAuth token by grabbing ctx.req.headers.authorization from the Model.beforeRemote method, but can't seem to figure out a way of passing it to the REST connector as a new header.

I've tried a couple of things:

  1. Adding a hook using Model.observe (but this doesn't seem to fire with the REST connector).
  2. Using a template with an authorization field - but have not been able to get this working correctly.

Any ideas appreciated.

like image 491
Simon Guest Avatar asked Sep 29 '22 03:09

Simon Guest


2 Answers

With the connector below you should be able to pass the OAuth token into the function (as first parameter in the example). Does something like this not work for you?

{
  connector: 'rest',
  debug: false,
  options: {
    "headers": {
      "accept": "application/json",
      "content-type": "application/json",
      "authorization": "{oauth}"
    },
    strictSSL: false,
  },
  operations: [
    {
      template: {
        "method": "GET",
        "url": "http://maps.googleapis.com/maps/api/geocode/{format=json}",
        "query": {
          "address": "{street},{city},{zipcode}",
          "sensor": "{sensor=false}"
        },
        "options": {
          "strictSSL": true,
          "useQuerystring": true
        },
        "responsePath": "$.results[0].geometry.location"
      },
      functions: {
        "geocode": ["oauth", "street", "city", "zipcode"]
      }
    }
  ]}
like image 119
Bryan Clark Avatar answered Oct 08 '22 09:10

Bryan Clark


Wanted to answer this, and build on Bryan's comments. Firstly, in datasources.json, you'll want to setup the REST connector:

{
    "name": "connect",
    "connector": "rest",
    "debug": "true",
    "operations": [
      {
        "template": {
          "method": "GET",
          "url": "http://server/api",
          "headers":{
            "authorization": "Bearer {token}"
          }
        },
        "functions": {
          "get": ["token"]
        }
      }
    ]
  }

As Bryan covered, it possible to put the auth header in each call, or at the root of the connector.

Secondly, and this is the bit I was stuck on, in order to pass the token to the API call from a model, it's required to generate a remote method that passes the token as a query parameter. This is what it looks like in this example:

module.exports = function (Model) {
  Model.disableRemoteMethod('invoke', true);
  Model.disableRemoteMethod('get', true);

  Model.call = function (req, cb) {
    var token = req.token;
    Model.get(token, function (err, result) {
      cb(null, result);
    });
  };
  Model.remoteMethod(
    'call',
    {
      http: {path: '/', verb: 'get'},
      accepts: [
        {arg: 'req', type: 'object', http: {source: 'req'}}
      ],
      returns: {
        root: true
      }
    }
  );
};

Notice how the req argument is required in order to provide the request to the model. You also notice that I've disabled the original get and invoke methods (replacing it with a more REST-friendly resource).

Finally, you'll need to get the token into the request. For this, it's easy enough to use some middleware. Here's an example from server.js:

app.use('/api', function (req, res, next) {
  oidc.authenticate(req, function (err, token) {
    if (err) {
      return res.send({status: 401, message: err});
    }
    req.token = token;
    next();
  });
});

In the above example, I'm using an internal OIDC provider to validate the token, but of course, you can use anything.

like image 25
Simon Guest Avatar answered Oct 08 '22 08:10

Simon Guest