gRPC
WIPAuthenticating @grpc/grpc-js unary calls from their metadata, and service-to-service tokens
withGrpc wraps a unary handler. It reads the token from the call's authorization metadata, resolves the session through the engine's transport, and puts it on the call:
import { withGrpc } from '@gentleduck/auth/server/grpc'
server.addService(GreeterService, {
sayHello: withGrpc<HelloRequest, HelloReply>(auth, (call, callback) => {
callback(null, { message: `Hello, ${call.identity?.profile.email}` })
}),
})
- A call with no session is answered
UNAUTHENTICATEDwithout running the handler.{ required: false }runs it withcall.sessionunset. - An
AuthErroris answered with its code as the message, at the gRPC status its HTTP status maps to: 401UNAUTHENTICATED, 403PERMISSION_DENIED, 429RESOURCE_EXHAUSTED.httpStatusToGrpcis that mapping. headerNamereads another metadata key. Acookieentry is read too, for grpc-web bridges that forward one.- The handler runs inside the actor scope. Pass
getCallerto also run hijack and anomaly checks;grpcCaller(call)answers the User-Agent, and a peer address is yours to add.
Service-to-service
The engine's transport decides what the token is, so a gRPC service usually runs on the JWT transport. A calling service trades an API key for a token through client credentials, built on the engine's own transport so the engine can verify what it signs, and sends it:
import { Metadata } from '@grpc/grpc-js'
const metadata = new Metadata()
metadata.add('authorization', `Bearer ${accessToken}`)
client.sayHello({ name: 'Ada' }, metadata, (err, reply) => {})
TLS, and mutual TLS, are configured on the gRPC server; withGrpc does not look at them.