So the stack for one of my projects palms off the bulk of its middleware responsibilities to Auth0 and Hasura. They fit quite seamlessly together, but all the same here are my notes on some initial stumbles - hope it helps others just starting to poke at both technologies.
When using Create react app, or similar client-side rendering UX frameworks, go the “SPA” route when setting up your Auth0 application. When building a nextjs app, or anything with SSR (server-side rendering) capabilities, choose the “regular web application” type. Do this, otherwise you’ll be in for a very lively time indeed.
So calling Auth0’s authorize
endpoint via something like fetch()
is… completely different from letting the browser just navigate to that endpoint via a regular <a>
or even react-fuelled <Link>
tag! The main reason is that with auth0’s “Universal Login” widget (the one that isn’t embedded; the one you have to redirect to)… well, the response to the authorization request is a browser redirect! A browser-less fetcher like fetch
is just going to blink stupidly at a redirect, wondering what on earth it’s supposed to do with it. A full-fledged browser otoh, knows just what to do with a redirect. So, call the endpoint in a browser-ly manner!
This one got me a couple of times. As you define new or changed roles for authorization in your db schema, don’t forget to update the rules / hooks that populate your JWT claims with the corresponding user roles! E.g. if you introduce a role named editor, or guestuser in Hasura (or whatever backend you are using), they have to come through in the JWT claims for the corresponding users… otherwise those users will get some pretty useless access tokens (and resource servers will understandably be quite guarded about fessing up any data to those users, and they’ll just experience a broken site!)
Auth0 doesn’t (at the time of writing) generate certs that work with Hasura at that jwk url they like to hand out to their tenants. Instead, download the .pem
file at <yourdomain>.auth0.com/pem
and stuff that into the “key” portion of this string:
{"type": "RS512", "key": "-----BEGIN CERTIFICATE-----\n<xxx>\n-----END CERTIFICATE-----"}
… Oh but wait. Turns out YOU HAVE TO STITCH EVERYTHING BACK TOGETHER WITH “\n” for all the linebreaks! In the end Hasura just built a tiny lil tool to do this for you. Big clue that this cert handshake isn’t taking place is if you get a graphql log line showing "code":"validation-failed"
.
After creating an API with google so that I could generate production client Ids and secrets for the OAuth handshake with google, I blithely overwrote auth0’s dev keys and immediately regretted it. Google (and in fact any other OAuth partner) does not care about your cutesy lil local dev environment. They have hordes of PII to protect and will not stoop to the level of chatting with plebeian hosts named localhost… much less give non-https-wrapped probes the time of day. In short, when you stick prod keys into your social connectors in Auth0, you have sealed the fate of your local dev environment. It will faceplant the moment it tries to auth. (Also: In my case, free-tier ip tunneling via ngrok and the like did not help: web assets choked on their rate limits within seconds).
I strongly urge you to create 2 auth0 tenancies, reserving one for prod, and all its attendant proper https endpoints.
This isn’t a pitfall so much as a friendly reminder, because it is tempting to worry about the security of the JWTs flying back and forth between your authentication server and your backend. Alas: JWTS aren’t meant to be private. They’re only meant to be tamper-proof, and thats what JWT.verify(yourToken)
call verifies. So you don’t, for example, need to pull your hair out worrying about how to encrypt the little things… esp since you’re not supposed to be stuffing your crown jewels inside the payload in the first place. I had been using JWT tokens in other contexts where it made a wee bit more sense to encrypt them, so even I had a brief moment of “but surely these need to be encrypted!“. Nope.