gRPC adapter
WIPServer 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:
- Reads
authorizationfromcall.metadata. - Calls
auth.resolveSession({ headers })to validate. - Attaches
call.sessionandcall.identityfor handlers to read. - Raises a typed gRPC
StatusforAUTH/*errors:UNAUTHENTICATED(16) forAUTH/UNAUTHENTICATEDPERMISSION_DENIED(7) forAUTH/AAL_INSUFFICIENTRESOURCE_EXHAUSTED(8) forAUTH/RATE_LIMITEDFAILED_PRECONDITION(9) forAUTH/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).