Skip to main content

gRPC adapter

WIP

Server interceptor for @grpc/grpc-js. Resolves the session from per-call metadata, raises typed AUTH/* errors.

When to use

duck-auth's gRPC adapter is for service-to-service authentication - typically with JwtTransport + Bearer style tokens in gRPC metadata (authorization: Bearer <jwt>). For browser-facing endpoints, prefer the HTTP adapters.

Setup

import * as grpc from '@grpc/grpc-js'
import { authInterceptor } from '@gentleduck/auth/server/grpc'
import { auth } from './lib/auth'

const server = new grpc.Server({
  interceptors: [authInterceptor(auth)],
})

The interceptor:

  1. Reads authorization from call.metadata.
  2. Calls auth.resolveSession({ headers }) to validate.
  3. Attaches call.session and call.identity for handlers to read.
  4. Raises a typed gRPC Status for AUTH/* errors:
    • UNAUTHENTICATED (16) for AUTH/UNAUTHENTICATED
    • PERMISSION_DENIED (7) for AUTH/AAL_INSUFFICIENT
    • RESOURCE_EXHAUSTED (8) for AUTH/RATE_LIMITED
    • FAILED_PRECONDITION (9) for AUTH/MAINTENANCE, AUTH/READONLY_MODE

In a handler

import type { ServerUnaryCall, sendUnaryData } from '@grpc/grpc-js'

const Greeter = {
  SayHello: async (call: ServerUnaryCall<HelloRequest, HelloReply>, cb: sendUnaryData<HelloReply>) => {
    // The interceptor already resolved the session; if it returned an error,
    // this handler isn't called.
    const reply = new HelloReply()
    reply.setMessage(`Hello, ${call.identity.profile.displayName}`)
    cb(null, reply)
  },
}

server.addService(GreeterService, Greeter)

Service-to-service with M2M

For machine-to-machine, wire apiKey or m2m providers and have clients authenticate via client-credentials:

// Client side:
const client = new GreeterClient('api.example.com:443', credentials.createSsl(), {})
const metadata = new grpc.Metadata()
metadata.add('authorization', `Bearer ${process.env.MY_SERVICE_TOKEN}`)
client.sayHello(request, metadata, callback)

The metadata token can be:

  • A short-lived JWT minted by your own M2M flow.
  • A long-lived API key (ak_live_...).
  • An OAuth2 client-credentials token from your IdP.

duck-auth resolves all three through the same resolveSession.

TLS / mTLS

duck-auth's gRPC interceptor doesn't touch TLS - configure that at the gRPC server level (grpc.ServerCredentials.createSsl(...)). For mTLS, the client cert is available on call.getPeer() but duck-auth doesn't verify it; that's the gRPC layer's job.

A common pattern is: mTLS at the transport layer (network-level trust), plus duck-auth bearer tokens at the application layer (audit trail, scope checks, revocation).