Clerk cofounder here: I hope this isn’t a Clerk stakeholder! It’s definitely misaligned with our culture around not speaking about competitors and instead playing our own game.
I think the bigger shifts are that developers use Checkout more, and complete more admin tasks in Stripe's Dashboard. By providing end-user UX (for both customers and backoffice), Stripe has reduced the API surface area that most developers need to consume. These changes end up having no impact on them.
10,000 MAUs are now included in every plan. We also now offer "First Day Free", so if a user churns in their first 24 hours after signing up, they are never counted as active. For GenAI, this has led to 60-70% of sign ups never being charged as active users.
Cofounder of Clerk here - we definitely want free plan users to be aware of this limitation - any suggestions to improve visibility?
On https://clerk.com/pricing , “Customizable session duration” is listed as a primary benefit of the pro plan, and in the chart we show that the free plan is “Fixed to 7 days”
Apologies we failed to make it clear before you started, that’s definitely not intended. We thought this was a good limitation for the free plan because it doesn’t impact your ability to learn if your product is resonating. If it is, and our default doesn’t work for your app, then we hope you can upgrade now that your product is validated. (It’s maybe worth mentioning that the default of 7 days was selected by copying Google’s session lifetime, also not meant to be nefarious.)
Glad we were able to mitigate this one for our customers, but have also been a bit surprised this vulnerability hasn't been generating more chatter.
tl;dr: if you use Google OAuth, any XSS on your site can likely be chained into a long-lived account takeover. In a roundabout way, it works around the protections afforded by HttpOnly cookies.
You can mitigate by always redirecting to a URL with an empty fragment (#) if your oauth callback URL experiences any failure.
1. It's a great question and we're still learning the answer. But, I believe the approach is ~95% compatible, and the last 5% just needs tweaking on the margins vs a major overhaul. Let me try to explain...
First: SSR definitely makes the framing of "frontend api" vs "backend api" very confusing. So ignore that, and think of it purely as "api authenticated with a session token" vs "api authenticated with a secret key."
I think authenticating via session token is the key to enabling faster development with Clerk than tools like Auth0 (or even Stripe/Twilio/etc). The reason why is that it shifts the problem of _authorization_ from our customer's backend to Clerk's backend.
As an example, consider a user updating their email...
In the past, you would build a frontend for collecting their new email, send it to your backend, ensure that the user is updating their own name (the authorization step), then forward the update along to your account system (Auth0, your own database, whatever).
With Clerk, you build a frontend for collecting their new email, then send it straight to Clerk to handle the update with the user's session token. We are responsible for ensuring the update is to the users own account, and there's no requirement to hop to your backend to relay the secret key.
In the end, that hop to the backend and authorization check is responsible for a lot of the "clunk" that Clerk eliminates. And ultimately, SSR doesn't change our ability to make things easier – we can authenticate our API with a session token just-as-well during SSR as we can from the frontend.
This feels like a paradox, right? A session token has such limited power compared to secret key, so surely it can't be used to build an easier API. But in practice, confidently knowing which user is making the request is necessary for shifting the authorization step to our service.
I'd add that this idea isn't particularly novel. Stripe Checkout depends on a CheckoutSession object, which you initialize by passing in the active user's ID. So there, you see that Stripe having the active user's ID enables them remove a ton of steps for building a checkout. Implicitly, under-the-hood Checkout relies on an API that uses a session token for authentication.
We just took the idea one step further and are exposing the API, instead of only using it to power a single, fairly rigid UI. With Clerk, developers can use React Hooks to build their own UI.
---
Now, regarding the 5% that we still need to figure out. It pertains exactly to the currentUser() and currentOrg() functions you're calling out. Those are compatible, but they require some extra thoughtfulness.
As an example, Clerk's User object has a field called "privateMetadata". From the backend, it's completely okay for currentUser() to return this private data, but Clerk needs to make sure it doesn't leak to the frontend. That creates some oddities - the User object on frontend is different than on backend, and I don't think we've really nailed the ergonomics / education on this part yet. But it generally feels like a solvable problem.
Clerk | Frontend & Backend SWE | Remote or in-person in SF
Clerk is hiring frontend and backend engineers, remote or in-office in San Francisco
We build developers tools for authentication. We're known for our React components like SignIn and UserProfile that "just work" when they're added to the page.
Our components are powered by a new type of API: a frontend-facing API that relies on session tokens for authorization, instead of a backend-facing API that relies on a secret key.
We've found this pattern unlocks a new level of efficiency. Developers can implement Clerk faster than traditional APIs because it comes built-in with UX and UI. To learn more about the approach, see our talk on A Component is Worth a Thousand APIs:
We're especially excited to work with engineers who are thoughtful about speed and craft. Clerk is defining the gold standard for components-as-a-service, and we are constantly searching for new ways to evolve and improve our approach. Apply here:
Separately, I'm one of Clerk's cofounders, and I'll be watching this thread through the day if I can be help with any questions. Or feel to email me (colin@ our domain)
If anyone's facing this in their auth flows, we're happy to help at https://clerk.dev
We're in the same cat-and-mouse game with the attackers as everyone else, but since we're an auth company, we have full-time folks monitoring for issues and resolving when they come up.
It's worth mentioning that Twilio is in an understandably tough position here. They only receive API requests from your server, and real requests look the same as attack requests except for the phone number.
Clerk is in a better position to help because our API accepts traffic directly from the attacker (e.g. POST /verify-phone-number). We know their IP, user agent, whether they're connecting from AWS, etc, etc. We very much rely on this data to help stop them.
Also, since you're concerned about subdomain attacks, make sure you set the cookie on a subdomain rather than the naked domain to prevent it from leaking.
Edit: you can put it on the naked domain if your app is on the naked domain. If you do that, do not set the Domain= attribute in your Set-Cookie because that will cause it to leak to subdomains.
I don’t agree that the complex system is mediocre.
I like it because it’s faster and it’s enabling more powerful integrations.
We (Clerk) abstracted away the complexity so it’s just an implementation detail, and think other tools should do the same instead of letting developers trip over the hazards.
Usually people experience this when they're using a React frontend that's served from a CDN, while the backend is served from a separate service.
If that's the case, the pattern you're describing is pretty normal and not an inherent cause for concern.
If you really want to workaround it, you can do this:
First, host your backend and your frontend from the same origin, so the initial window navigation includes the httpOnly cookie (make sure your cookie is set to SameSite=Lax, NOT SameSite=Strict). Then, before the CDN serves your static content, run some middleware to ensure the httpOnly cookie is valid, and add some kind of marker to the page body to indicate they're signed in (or redirect away if they're signed out).
The not-so-trivial part is running middleware in front of the CDN, but that's possible with things like Cloudflare Workers, and these days it's built-in to Next.js for most hosts.
For this approach you'll also want to make sure you can check cookie validity quickly at the edge, or else you'll be slowing down your CDN a lot.
Auth isn't "hard" per se, but it's still a big struggle because there's just so much to implement.
For example, one of the most common attacks today is "credential stuffing," which is simply the practice of taking leaked credentials and trying them on other websites.
Protection against this is arguably trivial: implement HaveIBeenPwned and a rate limiter, and create a mechanism to update the password when credentials leak.
But that's a lot of work, it can take a lot of tuning to get the rate limiter right, the UX/UI for updating passwords is time consuming, etc, etc.
And that's just one piece of authentication security – there are so many described in NIST 800-63B.
And then there's also the challenge of keeping up with user preferences - Sign in with Google, SAML, Touch ID, magic links, etc, etc. This seems to be fragmenting rather than consolidating.
Obviously biased, but all-in-all, I think you're better off sticking with a service than pulling it in-house, even if you stay with one of our competitors :)