Last active
November 26, 2024 21:35
-
-
Save markelliot/6627143be1fc8209c9662c504d0ff205 to your computer and use it in GitHub Desktop.
Converts Google service user OAuth2 credentials into an access token in Cloudflare-compatible JS
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
/** | |
* Get a Google auth token given service user credentials. This function | |
* is a very slightly modified version of the one found at | |
* https://community.cloudflare.com/t/example-google-oauth-2-0-for-service-accounts-using-cf-worker/258220 | |
* | |
* @param {string} user the service user identity, typically of the | |
* form [user]@[project].iam.gserviceaccount.com | |
* @param {string} key the private key corresponding to user | |
* @param {string} scope the scopes to request for this token, a | |
* listing of available scopes is provided at | |
* https://developers.google.com/identity/protocols/oauth2/scopes | |
* @returns a valid Google auth token for the provided service user and scope or undefined | |
*/ | |
async function getGoogleAuthToken(user, key, scope) { | |
function objectToBase64url(object) { | |
return arrayBufferToBase64Url( | |
new TextEncoder().encode(JSON.stringify(object)), | |
) | |
} | |
function arrayBufferToBase64Url(buffer) { | |
return btoa(String.fromCharCode(...new Uint8Array(buffer))) | |
.replace(/=/g, "") | |
.replace(/\+/g, "-") | |
.replace(/\//g, "_") | |
} | |
function str2ab(str) { | |
const buf = new ArrayBuffer(str.length); | |
const bufView = new Uint8Array(buf); | |
for (let i = 0, strLen = str.length; i < strLen; i++) { | |
bufView[i] = str.charCodeAt(i); | |
} | |
return buf; | |
}; | |
async function sign(content, signingKey) { | |
const buf = str2ab(content); | |
const plainKey = signingKey | |
.replace("-----BEGIN PRIVATE KEY-----", "") | |
.replace("-----END PRIVATE KEY-----", "") | |
.replace(/(\r\n|\n|\r)/gm, ""); | |
const binaryKey = str2ab(atob(plainKey)); | |
const signer = await crypto.subtle.importKey( | |
"pkcs8", | |
binaryKey, | |
{ | |
name: "RSASSA-PKCS1-V1_5", | |
hash: { name: "SHA-256" } | |
}, | |
false, | |
["sign"] | |
); | |
const binarySignature = await crypto.subtle.sign({ name: "RSASSA-PKCS1-V1_5" }, signer, buf); | |
return arrayBufferToBase64Url(binarySignature); | |
} | |
const jwtHeader = objectToBase64url({ alg: "RS256", typ: "JWT" }); | |
try { | |
const assertiontime = Math.round(Date.now() / 1000) | |
const expirytime = assertiontime + 3600 | |
const claimset = objectToBase64url({ | |
"iss": user, | |
"scope": scope, | |
"aud": "https://oauth2.googleapis.com/token", | |
"exp": expirytime, | |
"iat": assertiontime | |
}) | |
const jwtUnsigned = jwtHeader + "." + claimset | |
const signedJwt = jwtUnsigned + "." + (await sign(jwtUnsigned, key)) | |
const body = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + signedJwt; | |
const response = await fetch("https://oauth2.googleapis.com/token", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/x-www-form-urlencoded", | |
"Cache-Control": "no-cache", | |
"Host": "oauth2.googleapis.com" | |
}, | |
body: body | |
}); | |
const oauth = await response.json(); | |
return oauth.access_token; | |
} catch (err) { | |
console.log(err) | |
} | |
} |
Interesting @KeKs0r any chance to give it a little try on your side by spinning up an hello world container on cloud run ?
@Moumouls I tried it with calling document ai and it works from my cloudflare worker.
Love you all tyty
is this still functional getting a 1042 error for some reason
is this still functional getting a 1042 error for some reason
@Schachte false flag this seems to be working again, wasnt working for a brief while because of the cloudflare --remote service went down.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Since I came to this gist trying to solve the same problem. I found a solution that works with self signing and therefore doe snot add an additional request.
It might not work with all google apis, but I tested it with pubsub and document ai
Source: https://gist.github.com/KeKs0r/92be7af08d1d10eae8d1328c78de5f07