There are many ways to secure our functions. This topic is not clear when you start using the Cloud Function and it is also constantly changing. I decided to implement my own method that uses the basic and simplest authentication, the Basic Auth. It certainly is not the safest method but it was enough for my purpose. The only flaw is that the function has to be added and implemented in every Cloud function that we make.This implementation then gives us the possibility to call our function from anywhere we want to by simply adding as a parameter of the request the user and the password encrypted in the Basic Auth.
To simplify the verification, I created a function that I place in each Cloud Function. This function does the parsing of the request data, verifies if there is a basic auth and, if present, it checks whether the user and password data match. Here is the code I used:
var compare = require('tsscmp')
const checkAuth = async() => {
// parse login and password from headers
const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
const strauth = new Buffer(b64auth, 'base64').toString()
const splitIndex = strauth.indexOf(':')
const login = strauth.substring(0, splitIndex)
const password = strauth.substring(splitIndex + 1)
//function to validate credentials using https://www.npmjs.com/package/tsscmp
//Prevents timing attacks using Brad Hill's Double HMAC pattern to perform secure string comparison
function check (name, pass) {
var valid = true
// Simple method to prevent short-circut and use timing-safe compare
valid = compare(name, 'user') && valid
valid = compare(pass,'password') && valid
return valid
}
if (!check(login, password)) {
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="example"')
res.end('Access denied')
}
}
Then we can use the function within the method called by our Cloud Function in the following way:
exports.helloWorld = async (req, res) => {
await checkAuth();
let message = req.query.message || req.body.message || 'Hello World!';
res.status(200).send(message);
};
And that’s it, our functions are now safe! Not much but it’s a starting point. So in the end if our system is breached,it is not a big problem, in this function we only return Hello World!
How do we instead call our function with Flutter? We simply have to create the Basic Auth with user and password and add it to the request as follows:
var basicAuth = 'Basic '+base64Encode(utf8.encode(user:password'));
http.Response response = await http.get(apiUrl,
headers: <String, String>{'authorization': basicAuth});
print(response.statusCode); //200
print(response.body ); //Hello world!
This method is not one of the best, however, it is very fast to implement and also gives us an advantage because it can be used in the same way from all Cloud providers. Finally, we can recall it from anywhere using any language, and it does not force us to use json keys that could be displayed.
The only problem I had was having the username and password inside the code that I didn’t really like. I solved it by putting these parameters within the firebase configurations. If you are interested in this, you can read this article Flutter 2 indispensable plugins with great potential.