grpc-node icon indicating copy to clipboard operation
grpc-node copied to clipboard

Add server interceptors

Open anjmao opened this issue 7 years ago • 19 comments

Hi, I'm using grpc-node native package for building backend API. I noticed that there is not server interceptors (correct me if I'm wrong). I want to write server interceptors for logging, auth and so on. Without it grpc-node is pretty match useless for production. Could you share what is the status on this feature, because I'm thinking to implement it in my fork branch. Thanks.

anjmao avatar Jun 26 '18 11:06 anjmao

Server interceptors aren't in our roadmap at the moment.

nicolasnoble avatar Jun 26 '18 14:06 nicolasnoble

@nicolasnoble Thanks for response. Would you accept PR if I implement them, since I need server interceptors any way to be able to use grpc for production. I saw that there is also no native C++ server interceptors, but I'm looking to implement them in js side.

anjmao avatar Jul 01 '18 10:07 anjmao

We would require this to go through a GRFC process in order to discuss the API, but otherwise, yes.

Check https://github.com/grpc/proposal

nicolasnoble avatar Jul 01 '18 20:07 nicolasnoble

@anjmao I've been using https://github.com/echo-health/node-grpc-interceptors

theogravity avatar Jul 11 '18 01:07 theogravity

@theogravity Thanks for a link. I rewrote my server in go since it has the best grpc implementation.

anjmao avatar Aug 07 '18 06:08 anjmao

any updates on server interceptors as of now? @nicolasnoble

ShrawanLakhe avatar Dec 21 '18 07:12 ShrawanLakhe

Hi, any updates?

thodoris-tsiridis avatar May 23 '19 12:05 thodoris-tsiridis

Any updates about server interceptors?

edvardchen avatar Aug 07 '19 08:08 edvardchen

Nobody has proposed an API design yet, so nothing has changed.

murgatroid99 avatar Aug 07 '19 16:08 murgatroid99

Any updates here? Seems like interceptors would be pretty useful for things like auth and observability. Now that gRPC-web has hit 1.0, one could serve websites with gRPC and we'll need some sort of auth mechanism. It's not hard to roll your own interceptors, but it would be better if there was a standard way to do this.

richardpringle avatar May 06 '20 19:05 richardpringle

Any updates about server interceptors?

lwmxiaobei avatar May 19 '20 06:05 lwmxiaobei

Any updates on this?

anooshcnayak avatar Sep 03 '20 12:09 anooshcnayak

Also curious about this. Would be nice for rolling gRPC servers in Node to have some “official” support for interceptors

loudmouth avatar Oct 27 '20 14:10 loudmouth

In case someone is interested and wants to support this, there're some discussion about this here - https://groups.google.com/g/grpc-io/c/sF8rO9ltkpA/m/qWC8an4IAwAJ

Abhi347 avatar Nov 01 '21 16:11 Abhi347

@Abhi347 thanks for pushing this moving forward, appreciate it!

huan avatar Nov 04 '21 14:11 huan

https://github.com/deeplay-io/nice-grpc/tree/master/packages/nice-grpc has implemented server side middleware for grpc-js.

CMCDragonkai avatar Nov 11 '21 03:11 CMCDragonkai

Any updates about server interceptors?

0101-0101 avatar Jun 23 '22 09:06 0101-0101

@murgatroid99 Any plan to implement interceptors on grpc-node server side?

jiangtaoli2016 avatar Aug 09 '22 17:08 jiangtaoli2016

We currently have no plans to implement server interceptors in the gRPC Node library.

murgatroid99 avatar Aug 10 '22 18:08 murgatroid99

Hey @murgatroid99 - thanks for still being active on this thread

Do you have the grpc-node roadmap documented? I'm surprised after ~4 years interceptors have still have not been prioritised

Someone made a comment that you need a proposed API design - do you have a guide on contributing?

jakeowenp avatar Oct 20 '22 01:10 jakeowenp

Do you have the grpc-node roadmap documented?

No. We don't really have a roadmap. It's really just team-level quarterly objectives. For the past couple of years, the main thing I have been working on is xDS features.

Someone made a comment that you need a proposed API design - do you have a guide on contributing?

The proposal process is described in the README in the https://github.com/grpc/proposal repository. Check out the Node client interceptors proposal for an example that is probably similar to what a server interceptors proposal might look like.

murgatroid99 avatar Oct 20 '22 16:10 murgatroid99

@murgatroid99 and other memeber of grpc. Please keep on going. The interceptors on the node server side is very importent for users authentication. Is there workaround about authentication with jwt (json web token)? Thanks.

TheBestOrNothing avatar Feb 07 '23 10:02 TheBestOrNothing

Is there workaround about authentication with jwt (json web token)?

You may have an idea how to do it with this code :

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const proxyHandler = {
    get(target, propKey) {
        if (propKey !== 'addService') {
            return target[propKey];
        }
        return (service, implementation) => {
            const newImplementation = {};
            const lookup = lookupServiceMetadata(service, implementation);
            for (const k in implementation) {
                const name = k;
                const fn = implementation[k];
                newImplementation[name] = (call, callback) => {
                    const ctx = {
                        call,
                        service: lookup(name),
                    };
                    const newCallback = callback => {
                        return (...args) => {
                            ctx.status = {
                                code: 0,//grpc.status.OK,
                            };
                            const err = args[0];
                            if (err) {
                                ctx.status = {
                                    code: 2,//grpc.status.UNKNOWN,
                                    details: err,
                                };
                            }
                            callback(...args);
                        };
                    };

                    const interceptors = target.intercept();
                    const first = interceptors.next();
                    if (!first.value) { // if we don't have any interceptors
                        return new Promise(resolve => {
                            return resolve(fn(call, newCallback(callback)));
                        });
                    }
                    first.value(ctx, function next() {
                        return new Promise(resolve => {
                            const i = interceptors.next();
                            if (i.done) {
                                return resolve(fn(call, newCallback(callback)));
                            }
                            return resolve(i.value(ctx, next, callback));
                        });
                    }, callback);
                };
            }
            return target.addService(service, newImplementation);
        };
    },
};

const proxyServer = (server) => {
  server.interceptors = [];
  server.use = fn => {
      server.interceptors.push(fn);
  };
  server.intercept = function* intercept() {
      let i = 0;
      while (i < server.interceptors.length) {
          yield server.interceptors[i];
          i++;
      }
  };
  return new Proxy(server, proxyHandler);
}

function startServer() {

    const protoDefinition = protoLoader.loadSync("PATH_TO_PROTOFILE",
            {
                keepCase: true,
                longs: String,
                enums: String,
                defaults: true,
                oneofs: true,
            });
        const proto = grpc.loadPackageDefinition(protoDefinition);
        const server = proxyServer(new grpc.Server());

      async function login(call, callback) {
          // login code where you generate a token for the session, etc. ...
      }

       server.addService( proto.myservice, login); // ... adaptation necessary

       //middleware function that will be called before each grpc methods
        const grpcInterceptor = async (ctx, next, callback) => {
            function copyMetadata(call) {
                const metadata = call.metadata.getMap();
                const responseMetadata = new grpc.Metadata();
                for (let key in metadata) {
                    responseMetadata.set(key, metadata[key]);
                }
                return responseMetadata;
            }
            //token passed by http metadata
            const token = ctx.call.metadata.internalRepr.get("token")[0]
            
            // ... block of code to validate token, etc.
            let validated = true;

            // if all is well, we call original grpc method
            if (validated) {
                await next();
            } else {
                  error = new Error("Invalid token");
                  error.code = 16; //UNAUTHENTICATED
                  callback(error);
            }           
        };

    
        server.use(grpcInterceptor);
        server.bindAsync(address, grpc.ServerCredentials.createInsecure(), (err, result) => !err ? server.start() : console.error(err));
       
}

startServer();




update : added syntax highlighting to the code example. Thanks to @richardpringle.

mfecteau42 avatar May 19 '23 20:05 mfecteau42

@mfecteau42, use ```js to get syntax highlighting in your code example

richardpringle avatar May 23 '23 16:05 richardpringle

I have proposed a design for a server interceptors API: grpc/proposal#406. If you have any feedback on the design please comment there.

murgatroid99 avatar Jan 05 '24 19:01 murgatroid99

@murgatroid99 do you plan to merge these changes soon as they are approved? thanks for proposing a design! 🙏

vincen7tran avatar Jan 17 '24 18:01 vincen7tran

Our convention is to have a review period of at least two weeks before finalizing a proposal. I am working on the implementation in parallel to the proposal review, and even once the proposal is accepted it will still take me some time to finish the implementation.

murgatroid99 avatar Jan 17 '24 18:01 murgatroid99

Update: the design proposal grpc/proposal#406 is going through final review next week, and the implementation PR is out at #2650.

murgatroid99 avatar Jan 26 '24 22:01 murgatroid99