@sagi.io/workers-jwt
helps you
generate a JWT
on Cloudflare Workers with the WebCrypto API. Helper function for GCP Service Accounts included.
$ npm i @sagi.io/workers-jwt
We currently expose two methods: getToken
for general purpose JWT
generation
and getTokenFromGCPServiceAccount
for JWT
generation using a GCP
service account.
Function definition:
const getToken = async ({
privateKeyPEM,
payload,
alg = 'RS256',
cryptoImppl = null,
headerAdditions = {},
}) => { ... }
Where:
privateKeyPEM
is the private keystring
inPEM
format.payload
is theJSON
payload to be signed, i.e. the{ aud, iat, exp, iss, sub, scope, ... }
.alg
is the signing algorithm as defined inRFC7518
, currently onlyRS256
andES256
are supported.cryptoImpl
is aWebCrypto
API
implementation. Cloudflare Workers supportWebCrypto
out of the box. ForNode.js
you can usenode-webcrypto-ossl
- see examples below and in the tests.headerAdditions
is an object with keys and string values to be added to the header of theJWT
.
Function definition:
const getTokenFromGCPServiceAccount = async ({
serviceAccountJSON,
aud,
alg = 'RS256',
cryptoImppl = null,
expiredAfter = 3600,
headerAdditions = {},
payloadAdditions = {}
}) => { ... }
Where:
serviceAccountJSON
is the service accountJSON
object .aud
is the audience field in theJWT
's payload. e.g.https://www.googleapis.com/oauth2/v4/token
'.expiredAfter
- the duration of the token's validity. Defaults to 1 hour - 3600 seconds.payloadAdditions
is an object with keys and string values to be added to the payload of theJWT
. Example -{ scope: 'https://www.googleapis.com/auth/chat.bot' }
.alg
,cryptoImpl
,headerAdditions
are defined as above.
Suppose you'd like to use Firestore
's REST API. The first step is to generate
a service account with the "Cloud Datastore User" role. Please download the
service account and store its contents in the SERVICE_ACCOUNT_JSON_STR
environment
variable.
The aud
is defined by GCP's service definitions
and is simply the following concatenated string: 'https://' + SERVICE_NAME + '/' + API__NAME
.
More info here.
For Firestore
the aud
is https://firestore.googleapis.com/google.firestore.v1.Firestore
.
Cloudflare Workers expose the crypto
global for the Web Crypto API
.
const { getTokenFromGCPServiceAccount } = require('@sagi.io/workers-jwt')
const serviceAccountJSON = await ENVIRONMENT.get('SERVICE_ACCOUNT_JSON','json')
const aud = `https://firestore.googleapis.com/google.firestore.v1.Firestore`
const token = await getTokenFromGCPServiceAccount({ serviceAccountJSON, aud} )
const headers = { Authorization: `Bearer ${token}` }
const projectId = 'example-project'
const collection = 'exampleCol'
const document = 'exampleDoc'
const docUrl =
`https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents`
+ `/${collection}/${document}`
const response = await fetch(docUrl, { headers })
const documentObj = await response.json()
We use the node-webcrypto-ossl
package to imitate the Web Crypto API
in Node.
const { Crytpo }= require('node-webcrypto-ossl');
const cryptoImpl = new Crypto();
const { getTokenFromGCPServiceAccount } = require('@sagi.io/workers-jwt')
const serviceAccountJSON = { ... }
const aud = `https://firestore.googleapis.com/google.firestore.v1.Firestore`
const token = await getTokenFromGCPServiceAccount({ serviceAccountJSON, aud, cryptoImpl } )
<... SAME AS CLOUDFLARE WORKERS ...>
Node 15 introduces the Web Crypto API, so you don't have to pass in cryptoImpl
.
const { getTokenFromGCPServiceAccount } = require('@sagi.io/workers-jwt')
const serviceAccountJSON = { ... }
const aud = 'https://firestore.googleapis.com/google.firestore.v1.Firestore';
const token = await getTokenFromGCPServiceAccount({ serviceAccountJSON, aud });
<... SAME AS CLOUDFLARE WORKERS ...>