I am trying out Google Cloud Functions today following this guide: https://cloud.google.com/functions/docs/quickstart
I created a function with an HTTP trigger, and was able to perform a POST request to trigger a function to write to Datastore.
I was wondering if there's a way I can secure this HTTP endpoint? Currently it seems that it will accept a request from anywhere/anyone.
When googling around, I see most results talk about securing things with Firebase. However, I am not using the Firebase service here.
Would my options be either let it open, and hope no one knows the URL endpoint (security by obscurity), or implement my own auth check in the function itself?
One way to control access to a function is to require that the requesting entity identify itself by using a credential. A credential is a "name" of some sort, secured by a secret that the entity knows or has access to, like a password or a hardware dongle.
After looking into this further, and taking a hint from @ricka's answer, I have decided to implement an authentication check for my cloud functions with a JWT token passed in in the form of an Authorization header access token.
Here's the implementation in Node:
const client = jwksClient({ cache: true, rateLimit: true, jwksRequestsPerMinute: 5, jwksUri: "https://<auth0-account>.auth0.com/.well-known/jwks.json" }); function verifyToken(token, cb) { let decodedToken; try { decodedToken = jwt.decode(token, {complete: true}); } catch (e) { console.error(e); cb(e); return; } client.getSigningKey(decodedToken.header.kid, function (err, key) { if (err) { console.error(err); cb(err); return; } const signingKey = key.publicKey || key.rsaPublicKey; jwt.verify(token, signingKey, function (err, decoded) { if (err) { console.error(err); cb(err); return } console.log(decoded); cb(null, decoded); }); }); } function checkAuth (fn) { return function (req, res) { if (!req.headers || !req.headers.authorization) { res.status(401).send('No authorization token found.'); return; } const parts = req.headers.authorization.split(' '); if (parts.length != 2) { res.status(401).send('Bad credential format.'); return; } const scheme = parts[0]; const credentials = parts[1]; if (!/^Bearer$/i.test(scheme)) { res.status(401).send('Bad credential format.'); return; } verifyToken(credentials, function (err) { if (err) { res.status(401).send('Invalid token'); return; } fn(req, res); }); }; }
I use jsonwebtoken
to verify the JWT token, and jwks-rsa
to retrieve the public key. I use Auth0, so jwks-rsa
reaches out to the list of public keys to retrieve them.
The checkAuth
function can then be used to safeguard the cloud function as:
exports.get = checkAuth(function (req, res) { // do things safely here });
You can see this change on my github repo at https://github.com/tnguyen14/functions-datastore/commit/a6b32704f0b0a50cd719df8c1239f993ef74dab6
The JWT / access token can be retrieved in a number of way. For Auth0, the API doc can be found at https://auth0.com/docs/api/authentication#authorize-client
Once this is in place, you can trigger the cloud function (if you have yours enabled with http trigger) with something like
curl -X POST -H "Content-Type: application/json" \ -H "Authorization: Bearer access-token" \ -d '{"foo": "bar"}' \ "https://<cloud-function-endpoint>.cloudfunctions.net/get"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With