npm.io
0.11.0 • Published 3d ago

@nodii/grpc-auth

Licence
MIT
Version
0.11.0
Deps
7
Size
447 kB
Vulns
0
Weekly
1.7K

@nodii/grpc-auth

Service-to-service gRPC auth for the Nodii microservice stack.

Provides:

  • createServerAuthInterceptor — verifies an incoming Bearer token from the authorization metadata header and attaches an S2SAuthContext to the call.
  • createClientAuthInterceptor — acquires a token via an S2STokenManager and attaches it to outgoing calls.
  • S2STokenManager — caches/refreshes tokens from a SigV4-signed API Gateway endpoint.
  • verifyS2SToken — JWT verifier with JWKS resolution.
  • createSigV4S3JWKSet — JWKS resolver that pulls a private .well-known/jwks.json from S3 using SigV4 (not anonymous HTTPS).
  • withAuthUnary + requireScope — handler-level wrapper for unary RPC scope enforcement.

Install

bun add @nodii/grpc-auth
# peer deps
bun add @grpc/grpc-js jose

Server: verify incoming tokens

import * as grpc from "@grpc/grpc-js";
import {
  createServerAuthInterceptor,
  createSigV4S3JWKSet,
  setJwksResolver,
} from "@nodii/grpc-auth";

// Configure JWKS source once, at boot.
setJwksResolver(
  createSigV4S3JWKSet({
    region: "ap-south-1",
    bucket: "nucleus-cloud-s2s-auth",
    key: ".well-known/jwks.json",
    cacheMaxAgeMs: 60_000,
    cooldownDurationMs: 5_000,
  }),
);

const server = new grpc.Server({
  interceptors: [
    createServerAuthInterceptor({
      expectedIssuer: "nucleus-s2s-auth",
      expectedAudience: "nucleus-internal",
    }),
  ],
});

The interceptor extracts Bearer <token>, calls verifyS2SToken, and attaches the auth context to the call as (call as any).auth. Handlers read it via the same path:

function handler(call: ServerUnaryCall<Req, Res>, cb: sendUnaryData<Res>) {
  const auth = (call as any).auth as S2SAuthContext | undefined;
  // auth.serviceName, auth.scopes, ...
}

Note. (call as any).auth is the production-proven pattern from provisioning. A typed getCallContext(call) helper is planned for v0.3.0 (lifted from inventory). It will live alongside the legacy reader, not replace it.

Server: per-handler scope enforcement

import { withAuthUnary } from "@nodii/grpc-auth";

const REQUIRED_SCOPES_BY_METHOD: Record<string, string> = {
  createCustomer: "provisioning:customers:write",
  getCurrentIpAssignmentsForIp: "provisioning:ip_assignments:read",
};

const wrap = withAuthUnary({
  expectedIssuer: "nucleus-s2s-auth",
  expectedAudience: "nucleus-internal",
  requiredScopesByMethod: REQUIRED_SCOPES_BY_METHOD,
  // Optional: skip auth in local dev. Defaults to false.
  skipAuth: () => process.env.LOCAL === "true",
});

server.addService(MyServiceDefinition, {
  createCustomer: wrap("createCustomer", impl.createCustomer),
  // ...
});

Client: acquire and attach a token

import {
  S2STokenManager,
  createClientAuthInterceptor,
} from "@nodii/grpc-auth";

const tokenManager = new S2STokenManager({
  apiBaseUrl: process.env.S2S_AUTH_API_BASE!,
  region: process.env.AWS_REGION ?? "ap-south-1",
  refreshSkewSeconds: 30,
});

const client = new MyGrpcServiceClient(target, credentials, {
  interceptors: [
    createClientAuthInterceptor({
      tokenManager,
      audience: "nucleus-internal",
      scopes: ["flows:write"],
      instanceId: process.env.INSTANCE_ID,
    }),
  ],
});

Versioning notes

v0.1.0 is a 1:1 lift of provisioning's S2S auth code (production-proven, also running in traffic-logs). Two narrow changes vs the source:

  1. The JWKS source is configured via setJwksResolver(...) instead of a module-level constant. Provisioning's hardcoded region: "ap-south-1", bucket: "nucleus-cloud-s2s-auth", key: ".well-known/jwks.json" move into the consumer's bootstrap.
  2. withAuthUnary accepts requiredScopesByMethod and skipAuth as parameters instead of importing them from a service-specific env.

These are dependency-injection refactors, not behavior changes. Verifier semantics (algorithms, clock skew, claim shape, scope parsing), token cache logic, and JWKS refresh+cooldown are unchanged.

Planned follow-ups:

  • v0.2.0 — pluggable test JWKS source (TEST_S2S_JWKS_PATH pattern from traffic-logs).
  • v0.3.0 — typed CallContext / getCallContext / mutateCallContext API alongside the legacy (call as any).auth reads (pattern from inventory).
  • v0.4.0 — pluggable logger (replaces console.log calls; quiet by default).

Keywords