@sarfarajey/gcp-edge-auth
v1.0.0
Published
Google Cloud service account auth for edge runtimes — JWT, OAuth2 access tokens, Cloud Run ID tokens, Firestore REST. Zero dependencies, Web Crypto only.
Maintainers
Readme
@sarfarajey/gcp-edge-auth
Google Cloud service account authentication for edge runtimes. Zero dependencies — uses only Web Crypto, so it runs anywhere crypto.subtle is available:
- Cloudflare Workers
- Vercel Edge Functions
- Deno / Bun
- Node.js 18+
Solves the recurring problem that the official google-auth-library ships Node-only crypto and won't run in V8 isolate / edge runtimes.
Install
npm install @sarfarajey/gcp-edge-authOAuth2 access token
import { getServiceAccountToken } from '@sarfarajey/gcp-edge-auth';
const token = await getServiceAccountToken(env.SERVICE_ACCOUNT_JSON);
if (!token) return new Response('auth failed', { status: 500 });
const res = await fetch('https://aiplatform.googleapis.com/v1/...', {
headers: { Authorization: `Bearer ${token}` },
});Tokens are cached by ${client_email}:${scope} and reused while >5 minutes of life remain. Pass cache: false to opt out, or supply your own Map-like store for cross-request caching in cold-start environments:
const myCache = new Map(); // or a KV/Redis adapter exposing get(k)/set(k,v)
const token = await getServiceAccountToken(saJson, { cache: myCache });Custom scope:
const token = await getServiceAccountToken(saJson, {
scope: 'https://www.googleapis.com/auth/datastore',
});Cloud Run ID token (service-to-service)
import { getCloudRunIdToken } from '@sarfarajey/gcp-edge-auth';
const idToken = await getCloudRunIdToken(saJson, 'https://my-svc-abc-uc.a.run.app');
const res = await fetch('https://my-svc-abc-uc.a.run.app/api', {
headers: { Authorization: `Bearer ${idToken}` },
});Firestore REST helpers
Imported separately so you only pay for what you use:
import { getServiceAccountToken } from '@sarfarajey/gcp-edge-auth';
import {
firestoreBase,
fsGet,
fsCreate,
fsPatch,
fsUpdate,
fsRunQuery,
} from '@sarfarajey/gcp-edge-auth/firestore';
const token = await getServiceAccountToken(saJson);
const base = firestoreBase('my-project');
// Read
const doc = await fsGet(`${base}/users/alice`, token);
// Upsert (no mask)
await fsCreate(`${base}/users/alice`, {
email: { stringValue: '[email protected]' },
role: { stringValue: 'admin' },
}, token);
// Partial update — derives mask from keys
await fsUpdate(`${base}/users/alice`, {
role: { stringValue: 'viewer' },
}, token);
// Explicit mask
await fsPatch(`${base}/users/alice`, fields, ['email', 'role'], token);
// Query
const docs = await fsRunQuery(base, {
from: [{ collectionId: 'users' }],
where: {
fieldFilter: {
field: { fieldPath: 'role' },
op: 'EQUAL',
value: { stringValue: 'admin' },
},
},
}, token);Error handling
All functions return null / false / [] on failure rather than throwing. This matches edge-runtime ergonomics where you usually want a graceful 500 rather than an unhandled rejection. Inspect the returned value and decide how to handle the auth failure in your handler.
License
MIT
