Created
January 24, 2023 14:11
-
-
Save KeKs0r/92be7af08d1d10eae8d1328c78de5f07 to your computer and use it in GitHub Desktop.
Get JWT Token from service account for GCP (to use Google Api in cloudflare workers)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { subtle } from 'crypto' | |
import { Base64 } from 'js-base64' | |
type ServiceAccount = { | |
private_key_id: string | |
private_key: string | |
client_email: string | |
} | |
type Scope = 'https://documentai.googleapis.com/' | |
type TokenOptions = { | |
aud: Scope | |
} | |
export async function getJWTFromServiceAccount(sa: ServiceAccount, { aud }: TokenOptions) { | |
const privateKey = await importPrivateKey(sa.private_key) | |
const header = Base64.encodeURI( | |
JSON.stringify({ | |
alg: 'RS256', | |
typ: 'JWT', | |
kid: sa.private_key_id, | |
}) | |
) | |
const iat = Math.floor(Date.now() / 1000) | |
const exp = iat + 3600 | |
const payload = Base64.encodeURI( | |
JSON.stringify({ | |
iss: sa.client_email, | |
sub: sa.client_email, | |
aud, | |
exp, | |
iat, | |
}) | |
) | |
const textEncoder = new TextEncoder() | |
const inputArrayBuffer = textEncoder.encode(`${header}.${payload}`) | |
const outputArrayBuffer = await subtle.sign( | |
{ name: 'RSASSA-PKCS1-v1_5' }, | |
privateKey, | |
inputArrayBuffer | |
) | |
const signature = Base64.fromUint8Array(new Uint8Array(outputArrayBuffer), true) | |
const token = `${header}.${payload}.${signature}` | |
return token | |
} | |
function importPrivateKey(pem: string) { | |
const pemContents = getPemContent(pem) | |
const buffer = Base64.toUint8Array(pemContents) | |
const algorithm = { | |
name: 'RSASSA-PKCS1-v1_5', | |
hash: { | |
name: 'SHA-256', | |
}, | |
} | |
return subtle.importKey('pkcs8', buffer, algorithm, false, ['sign']) | |
} | |
function getPemContent(rawPem: string) { | |
const pemHeader = '-----BEGIN PRIVATE KEY-----' | |
const pemFooter = '-----END PRIVATE KEY-----' | |
const pem = rawPem.replace(/\n/g, '') | |
if (!pem.startsWith(pemHeader) || !pem.endsWith(pemFooter)) { | |
throw new Error('Invalid service account private key') | |
} | |
const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length) | |
return pemContents | |
} |
@heshamMassoud I think you need nodejs compatibility in your wrangler.toml:
compatibility_flags = [ "streams_enable_constructors", "nodejs_compat" ]
I haven't tried it yet but instead of
import { subtle } from 'crypto'
You should just be able to use it from globalThis
return globalThis.crypto.subtle.importKey('pkcs8', buffer, algorithm, false, ['sign'])
Just sharing that I got this working by swapping out
-import { subtle } from 'crypto'
-
import { Base64 } from 'js-base64'
+
+const { subtle } = globalThis.crypto;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You sure this works on a cloudflare worker? I am getting the following error: