If you’re reading this post without a primer on serverless Zoom Apps I recommend checking out the first post in this Serverless Zoom Apps series - Building Serverless Zoom Apps.
This post will take a different, more casual, tone and instead focus on what I was able to learn about my creative process while implementing PKCE. Much of the concepts for this post are inspired by a non-zoomie dev.to post from Remo H. Jansen called Strategic Procrastination and Software Design. Check out that post for even more context.
Security First
When I first set out to write the Serverless Zoom App Sample implementing proper OAuth, with PKCE and a state verifier, seemed to be the most challenging part of development.
Why? Well, there’s nothing more important than the security of your app especially when it comes to authorization. Sure, Zoom is the most secure platform in the world but as a developer one wrong move could throw all that security out the window.
That’s why you’ll see we have a focus on showing security best practices when it comes to the sample applications we publish. It’s critical for your team and ours that Zoom API/SDK credentials are kept secure.
The Road Not Taken
With an eye for security in mind, I quickly found that there was little to no guidance online on how to handle OAuth authorization with PKCE or state verification. Sure, there was some generic information here and there but nothing concrete. Nothing fitting my use case. Nothing you would want to base your best practices app on.
I could set up firebase user authentication but that seemed like overkill and a lot of complexity for what was supposed to be a quick start. I needed a method to identify a user behind the scenes to store a unique state and verifier on the backend.
No, I didn’t need to find a method. I needed to find the best method.
What is PKCE anyways?
Let’s take a step back. Why exactly do I need to implement PKCE? What are the benefits it provides?
PKCE (pronounced as pixie) is an extension to the Authorization Code OAuth flow that was originally designed for mobile applications that were unable to keep a client secret secure.
These days, we use it with web development along with our client secrets to prevent CSRF attacks as well as an injection of an authorization code from a bad actor.
If you want to get into the nitty gritty, I recommend reading the RFC Specification but I’ve pulled some of the important parts out so we can see what exactly PKCE is:
This extension utilizes a dynamically created cryptographically random key called "code verifier".
A unique code verifier is created for every authorization request, and its transformed value, called "code challenge", is sent to the authorization server to obtain the authorization code.
The authorization code obtained is then sent to the token endpoint with the "code verifier", and the server compares it with the previously received request code so that it can perform the proof of possession of the "code verifier" by the client.
This works as the mitigation since the attacker would not know this one-time key, since it is sent over TLS and cannot be intercepted.
In other words, for each request to authorize we need to create a session for that user so that we can associate their request with a uniquely generated “Code Verifier”.
What About the State Parameter?
The state
parameter has a similar use as a code verifier and code challenge except we can customize that parameter to hold whatever data is useful for us.
In this fashion, not only does it serve as a CSRF protection mechanism but also allows us to pass data to our Zoom App.
If you’re interested in learning more about using a state parameter I recommend checking out this guide from Auth0.
A Point of Saturation
After even further research I had a few options none of which seemed to fit exactly what I needed. I almost convinced myself to write a signup and login flow at one point. The irony here is that the development for that login code would likely eclipse that of the to-do app it was supposed to federate.
Frozen from too many choices or perhaps just an inability to find the right solution I decided to postpone the crux of the project - the PKCE implementation. Instead, I would write a basic Cloud Function OAuth implementation and would extend it when I continued my research.
When I made this decision it felt like a major bump in the road. I was looking behind me trying to decide if it was too late to turn around and go back or if I could manage to make it to my destination.
Nevertheless, I continued down my path of writing a serverless Zoom App saving the PKCE implementation for when I had everything else sorted out.
Security Last
Fast forward a couple of weeks and Zoomtopia is right around the corner. My to-do app is complete and I can rest easy. Not only that but it has passed most approvals and is just awaiting security approval before being published.
I’m getting ready for bed and planning what I’m going to do for the next day when I remember my recently submitted security review. I begin wondering when I’ll hear back from our team and what sort of feedback I’ll hear from them.
Concerned that I might not pass my review in time I recollect my past sample applications thinking of the various feedback I’ve had to address in the past and how long that took me.
I console myself by confirming I have my bases covered and the changes should be quick to address. After all, it’s not like I’ll have to implement any new functionality.
That’s when it hit me. PKCE! I completely forgot about PKCE and I had done none of the research I planned.
Bedtime was over it was now time to panic!
Get Back to Work
I ran downstairs to my office and immediately started my laptop. Hoping that my security review hadn’t been seen so I could at least spare myself the embarrassment of submitting an insecure app for review.
For the first time ever, I’m happy to see that no one has gotten back to me on my review. I open up my IDE to work on my to-do app with only panic at the forefront of my mind.
I looked at my code for a moment, focused my mind on the problem and to my great surprise, I knew exactly what to do.
It was so obvious! How was I confused before? Well, I’m certainly confused now as I begin writing this PKCE implementation at lightning speed. My first test works. Wait. What?!
How is this happening? This is working too well. I test some more before realizing it’s only been about 30 minutes but everything seems to be working. I must be sleep deprived, I think.
I push my changes up to my repository and await the harsh reality that will be the upcoming security review.
Lessons Learned
If it isn’t clear, my app did indeed pass the security review and you can download it right now on our GitHub!
So the question is, without putting additional research into PKCE, how was I able to implement it? Well to be honest that would be nearly impossible.
The key here is that I strategically procrastinated the implementation while accidentally researching everything about Firebase and PKCE along the way. What I mean by this is that even though I wasn’t directly focused on OAuth or PKCE I would mull it over and ponder it as I went through the development lifecycle.
For instance, when I stumbled across the Firebase Authentication documentation I found myself reading at length about anonymous authentication and the process for anonymous users. Almost without thinking about it, I filed that away for later.
On another occasion, I’m looking through previous apps to pull sample code and I find myself reviewing my old PKCE implementation. Taking a moment to remember just why PKCE is useful and what it is used for as we covered above.
Without realizing it, I had spent the last two weeks taking every opportunity possible to learn about how to implement PKCE with a serverless architecture.
Strategic Procrastination
It wasn’t until after my security review was approved that I found a name for what I did. After reading the blog Strategic Procrastination and Software Design it became clear that this was part of my creative process when my mind is overwhelmed with a topic.
As explained by Remo H. Jansen,
Strategic procrastination involves taking massive action in both physical and mental aspects. You put in extreme labor, back off from it to recover, allowing your subconscious mind to process all of it and then enter the fray with newfound vigor.
Remo goes on to explain the 5 stages of strategic procrastination:
1. Have a vision or goal.
Start working on them immediately, today, as soon as possible.2. Take massive action every single day
Move closer constantly. Put in significant amounts of effort and work. You have to become obsessed with your thing if you want extraordinary results.3. Reach a point of saturation
Exert your mental powers and feel as if your mind has been drained like a sponge.4. Initiate strategic procrastination
Back off and focus on recovery. Walk, exercise, play around and relax. Don’t forget about your work entirely but don’t manically focus on it either. Ponder over it quickly but not too hard.5. Get back to work
After proper recollection of resources, you have to start putting in the work again; otherwise, you’re only procrastinating not strategically procrastinating. This part is essential and the one in which you produce your most excellent work. You re-enter the zone with newfound vigor and creative insight so that you could achieve glory.
The Implementation
Strategic or not, what did this procrastination lead to? What was the ultimate solution to implementing PKCE with a serverless application?
Firebase Firestore
Reminding myself of why and how PKCE is used throughout the development of the project helped to clarify the requisites for a secure PKCE implementation. In the simplest terms, I just needed to be able to store a random value for a specific user and then recall that value when they came back to my site.
I knew that I was already using Firebase Firestore for storage so that problem was solved but I wasn’t sure how I could map user sessions together.
Anonymous Authentication
We can solve the need to map requests to users by using Firebase Anonymous Authentication. This creates temporary accounts that are tied to a user's session. Using this authentication, we now have a User ID that we can use in our Firebase Cloud Functions.
Let’s take a look at the code. Here we have the function responsible for installing the app.
On line 72 you can see that we immediately have access to the context.auth.uid variable. This is thanks to enabling anonymous authentication. We use the uid
along with a random value in our state
to ensure that the state is unique for each request while also passing the uid
to the redirect URL.
One line 73 we are generating a random Code Verifier for use with PKCE. Looking down at line 98, we can see that we then store this in our Firestore database. We set the ID of the document as the uid
so that we can reference it later and then simply store the state
and verifier
values that we generated.
Now, let’s take a look at the function that serves as our redirect URL for OAuth. This function is responsible for receiving the state
and verifier
information that we set previously.
On line 119 we are parsing out the uid from the state value. Then on line 127 we’re fetching the state and verifier parameters that we stored earlier using that uid value.
Then on lines 135 - 139, we are validating our parameters. Finally, we use the Zoom OAuth API along with our verifier to get our accessToken and use the Zoom API to deep link our user to our app.
What’s Next?
Make sure to follow us on dev.to so you can stay updated on our latest blogs. If you haven’t already check out our github! Get started right away using our sample application so you can build out the next killer Zoom App!
Also, make sure that you check out Strategic Procrastination and Software Design if your brain works like mine does.
Thank You for Reading!
Thanks for taking the time to read this post. If you have any lingering questions or just want to chat about building the future of collaboration please head over to our Developer Forum.
Happy Coding!
Top comments (0)