Doing It the right way
Github OAuth for Github apps
So, I am building a desktop app for codereader.dev. I decided to build it with Tauri with a static React app built with Vite. I thought making the GitHub OAuth was going to be a simple process, but it wasn’t. Here’s how it went
First attempt: Using tauri-plugin-oauth like Supabase does
I found out this repo from supabase to handle their OAuth flow. I read the code and it seemed great.
The flow would be like this: we'd have our Auth component that when mounted calls a Rust function provided by tauri-plugin-oauth and starts a local webserver on a free port. This local server will then be able to receive a payload when the flow is done. Our Auth component would also render a sign in button. All this button would do is to start the OAuth flow with the supabase provided on our native browser. When the flow succeeds, our local web server will receive a code. This code then needs to be exchanged for access tokens. IF the provided you rely on has PKCE then you can eschange the code right there on our React Tauri app. Unfortunately, Github requires using our client secret for the code exchange and thus it would force us to bundle it our client apps. No matter how much you try to hide it, the secret will be there somewhere on your bundle and thus be compromised.
So now what, since liking this Github discussion for PKCE will achieve nothing I had to find another way.
Second try: Exchange the code on the server
So for this to work out now I'd need to make an ednpoint on my server to handle the token exchange. To do this I need to send the code and the port my webserver is open to my backend. As Supabase did, we are also using the tauri-plugin-oauth for the local server. The backend would then use the client-secret and exchange that for tokens. We would then return the tokens to the local server and tada!
BUT wait, is it safe to have a publicly accessible endpoint to exchange codes for tokens?
It turns out that it is. Although not impossible, since github auth codes are 36 character (32 without dashes) and expire in 10 minutes the chances of an attacker guessing one of this is extremely small. I mean 1/35^32 small, which corresponds to to the size fot he alfabet (digits 0-9 plus english alfabet) to the lenght of the string without dashes. I have to admit i did not like relying on "luck" but the probabilities are son incredibly small that I'll have to go with it. Here's how one fo the code looks
Third option: the device flow
If you find this setup too complicated for your side project you can definitely go with the device flow. The device flow is part of the OAuth spec and it is implemented by Github. The bad thing is that it only support OAuth apps on Github and not Github Apps. OAuth apps and Github apps on github are pretty different. They have different endpoints available, different sign in flow and different scopes available so they do have an impact on the sign in experience. I personally don't like the user experience the device flow has, BUT it is extremely simple to implement it on the other hand. As it's name suggests it enables a sign in to be realized fully client-side.
I'll not go over the implementation details but you need to know this is also an option.
Doing this was way more tedious than I expected. For me, Auth work like this is extremely boring. It lacks any creativity or problem solving. Just a ton of standards and following recipes, so I had to write this so anyone else going through this can just take it from here.
If you want to learn to do the OAuth flow with a provider that does support PKCE you can read this