Serverless API Authentication with Firebase
We've seen building web apps with serverless architecture becomes popular these years. Static HTML web app talks to cloud functions as its backend. If you are hosting everything on AWS, then the API gateway could be leveraged for API authentication. I'm going to share how to do it if app is hosting on Firebase in this post.
Assumptions
- The web app is built on Firebase
- There're 2 simple components in this app, static HTML deployed on Firebase hosting, and API on cloud function
- The web app is using Firebase authentication
If it's a mobile app with a client, it also works in the same way.
Architecture
I had a simple side project Grade Calculator deployed on Firebase this way:
Web app get user authenticated by firebase.auth APIs. And it talks to cloud functions from client when required. And then the REST APIs would be authenticated by cloud functions before writing to database or something like that.
Solutions
It's for sure ok if you implement a home brew JWT token authentication all by yourself. But for our assumptions, Firebase auth is already integrated in the web app. So there's no need to re-invent the wheel.
Firebase provides 3 user identities:
- uid
- server side session
- idToken
uid is an ID for a user. It is created when the user created in Firebase authentication database, and will not change in whole life time. It is not safe to use this ID for API authentication because it never change.
server side session is a cookie generated by Firebase authentication each time after user authenticated. It's safe for Firebase authentication itself. But it has 2 problems. First, it has an expire time for 2 weeks, it's too long. The 2nd, it is httpOnly cookie, web app is not able to get the cookie from frontend JavaScript.
id Token is the recommended solution for REST authentication officially. It changes every hour, safe enough and easy to verify in API side.
To get idToken from client side:
user.getIdToken()
.then(idToken =>{
// calling API with idToken
})
To verify this token in API implementation:
admin.auth().verifyIdToken(idToken)
.then(decoded => {
return admin.auth().getUser(decoded.uid)
})
.then(user => {
// something with user object
})