-
-
Save markelliot/6627143be1fc8209c9662c504d0ff205 to your computer and use it in GitHub Desktop.
/** | |
* 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) | |
} | |
} |
Pushed as module as this was a bit of a pain for fun personal projects.
Supports Typescript.
npm install cloudflare-workers-and-google-oauth
import GoogleAuth, { GoogleKey } from 'cloudflare-workers-and-google-oauth'
// Add secret using Wranlger or the Cloudflare dash
export interface Env {
GCP_SERVICE_ACCOUNT: string;
}
export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
const scopes: string[] = ['https://www.googleapis.com/auth/devstorage.full_control']
const googleAuth: GoogleKey = JSON.parse(env.GCP_SERVICE_ACCOUNT)
const oauth = new GoogleAuth(googleAuth, scopes)
const token = await oauth.getGoogleAuthToken()
// Example with Google Cloud Storage
const res = await fetch('https://storage.googleapis.com/storage/v1/b/MY_BUCKET/o/MY_OBJECT.png?alt=media', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'image/png',
'Accept': 'image/png',
},
})
return new Response(res.body, { headers: { 'Content-Type': 'image/png' } });
},
};
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
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.
This is great - the original works perfectly for me, thank you!