I made this because I was tired of asking the same question.
There are a bunch of services out there trying to address the same thing, but unfortunately they get wildly expensive and they own/control your user data.
Professionally, I've been a consultant and freelance developer for 10+ years. Time and time again I'd see teams start with Auth0/etc, only to eventually:
1. duplicate user data into their own DB/Cache to avoid the latency of asking Auth0 for user data
2. remove Auth0 entirely because having active users !== having paying users... so Auth0's monthly bill of $3000 to $10000+ couldn't be justified
3. #1 then eventually #2
btn.social is just as simple to set up, but has predictable & transparent pricing with volume discounts the more you use it. At its most expensive, it's 100 logins per penny ($0.01), which is to say that "MAUs" are a thing of the past.
As quick pricing comparison:
- Auth0's (public) pricing calculator [1] shows 10k MAUs for $228/mo. That just means 10k users logged in 1+ time(s) that month. In btn.social terms, that's 10k, 20k, or 30k logins depending on if each of those users logged in 1x, 2x, or 3x (etc). For $5, you get 50k logins with btn.social.... and for $169 you get 2.5M logins... [2]
- Supabase charges $25/mo, which includes a lot more than just OAuth(!) but if you look at Auth alone, that $25 includes 100k MAUs and is an additional $0.00325 per MAU thereafter [3]. Assuming 1x login per MAU, that's $2950/mo for 1M users. With btn's "Business" tier, you get 1M logins for $79/mo, but of course there's still the $169 for 2.5M if you don't like the "1x login per MAU" assumption.
Another key difference is that btn.social saves nothing about your users.
It's your app so you get to keep your data. You'll never need to have to ask btn.social for Alex's profile because we won't have it :D This means we're privacy-first & we don't add/pose a risk for GDPR concerns.
btn.social was just launched last week [4], so some additional guides & examples are still underway, but there are already 10+ OAuth providers [5] to choose from & a free/"Hobby" tier so anyone can start playing around in just a few minutes.
First off, congratulations on launching! I'm not sure why you're getting downvoted, because it seems quite relevant to the discussion. I'll definitely look at btn.social for my next hobby project. The pricing is super reasonable and the documentation very clear.
One question I have around OAuth in general is whether it only applies for SPAs or if I could also use it for more traditional multi-page apps? I guess I could do something similar to what you're doing in your guide [0] and do a POST from the onlogin callback, but how does my backend know that the payload is valid?
Thank you! Not sure, just part of the orange site mystery~
Your `onlogin` callback will only ever be invoked by the SDK on successful login. So the data coming thru there is always the result of the OAuth flow itself. For MPAs, you can create your own document cookie and/or modify local/sessionStorage, and then every new page load will pull/load from those storage systems. SPAs can & should do the same – they just have the added benefit of keeping in-memory state alive.
As for POSTing to your backend, the same XSRF/CSRF/CORS rules & mechanisms apply as with any other client->server transaction. You can rely on CORS for limiting who can talk to your API, you can pass along a unique token for CSRF prevention, and/or come up with your own Authorization header format.
Soon I'll be adding a `redirect_uri` config option for all btn.social apps. Much like the normal `redirect_uri` in OAuth, this will be a target URL that btn.social redirects users to post-login. This will enable server-side redirects, which means that your API just has to verify the "login.btn.social" referer.
Ah great, thanks! It would be great to see a larger example on your site to see how this all ties together. For example, I don't really understand what's stopping anyone from opening dev console and just calling my 'onlogin' callback and passing it fake data to impersonate another user. It seems to me that somewhere I'd still need to verify the user data with the provider.
I fully realize that that's probably due to my own ignorance and no knock on you. However, maybe there's others like me who'd benefit from some more examples.
For sure! There are some larger examples coming so that people can see it plugged into a more real-world application.
RE: onlogin, that method accepts a callback handler that is added to an internal/private array of callbacks to run once the `popup` or `redirect` sequence has finalized. It's the only externally-facing "doorway" into the OAuth flow. Everything else is inciting action (`popup()` and `redirect()`) or a read-out of the last-known payload (`last()`). It's actually very similar to a state manager I wrote a while back[1]. Callbacks are added via `$.on()` which assembles an internal `tree` map to be `dispatch`d once a change happens.
You've probably already seen the API docs for the btn.social SDK, but linking just in case[2]
Well said – everything new does not need to be a replacement for something else. Options/choices are good, especially because each one can lean into a different solution space.
It isn't exactly a drop-in replacement, but the tweaks needed are incredibly minor.
The GET requests are made via `new Image()`, which is a low priority network request, so your critical requests won't be blocked or delayed because of a trivial `pageview` analytic.
Internet Explorer is not supported by default; however, it's VERY easy to regain compatibility. `Object.assign` is the only roadblock, which has a Babel quick-fix.
Hope you enjoy! And feel free to ask questions. Thanks!
A smaller footprint _does_ usually signify a faster load time, but that can easily be overruled by how its internals are parsed (aka, interpreted by the browser).
For example, 1kb script can intentionally block the mainframe for 10seconds, thus making it slower than a 40kb moderate-performing competitor.
Inferno is optimized for the entire performance profile. Preact may load & parse faster -- but only by a HAIR (10-50ms). Inferno does everything else much faster.
So, do you consider the `load` event (which will occur once in the UX) to be more valuable than the every other interaction?
Inferno only works super fast if you use the babel transpiler that creates the blueprint calls. While this is a great idea, debugging it is a nightmare at times.
I'd rather have simple h or createElement calls. I personally prefer Typescript jsx as all my Components are type validate and refactoring is a breeze.
What I like about preact is its simplicity. I can debug preact and figure out what's going on. Inferno on the other hand seems a bit like assembly. Reusing property names across different contexts is a no-no in my book.
I once worked on a database with generic extra_1 extra_2,... fields. They were abused pretty bad and only one guy knew what they meant in what context.
While trueadm is a perf wizard I would definitely love to see some of the opts being done at the V8/chakra level rather than obfuscated js code.
This isn't true anymore. Blueprints were removed from Inferno quite some time ago. Inferno uses createVNode calls, which are very much like createElement calls.