Signing in
SparrowKit signs people in with passkeys. They are the primary way in and they are always on, because they are the only method that cannot be phished: a passkey is locked to one domain, so a copy of your sign-in page on another domain cannot ask for it.
Everything else on the panel either supports passkeys or offers a way in beside them.
Set the domain before you deploy
The one setting you cannot undo.
A passkey is permanently bound to a domain. It cannot be moved, so getting this wrong is not fixed by editing the field afterwards — it is fixed by every one of your users enrolling again.
Write the bare domain: no scheme, no port, no path.
example.com |
the whole of it |
https://example.com |
no scheme |
example.com:3000 |
no port |
www.example.com |
works, then dies the day somebody visits without the www |
Use the bare form rather than www. — it also covers www., app. and every
other subdomain.
Left blank, the domain is derived from whichever address the first request happens to arrive on. That is right in development and a coin toss in production.
Elsewhere this is called the Relying Party ID, or RP ID — the WebAuthn standard’s name for it, and the one you will see in browser errors. The Relying Party is the site relying on the authenticator to vouch for somebody, so the ID is just that site’s domain.
localhost is a perfectly good value while you are developing. WebAuthn treats
it as a secure address, which is what makes passkeys work on a laptop at all.
Before you deploy, turn on config.force_ssl in production. It is one line,
it is Rails’ rather than ours, and a passkey will not work at all without it —
WebAuthn requires a secure address.
The ladder
Three rungs, in the order SparrowKit prefers them:
Safe to leave to a stranger?
| Cannot be phished | Cannot be reused elsewhere | Cannot be guessed at scale | |
|---|---|---|---|
| Passkey | yes | yes | yes |
| Emailed sign-in code | not in the moment | yes | yes |
| Password | no | no | no |
Passkeys cannot be turned off, and neither can proving an email address. An emailed sign-in is always available too, because a brand new account has no passkey yet and would otherwise have no way in at all.
What you choose is its shape: a six-digit code somebody types, or a link they open. One or the other, never both and never neither.
Which to choose. A code works when the mail is read somewhere else — a phone, a shared mailbox, a machine that is not the one they started on. A link is one click and cannot be finished anywhere but the browser that asked for it, which is exactly what stops anything scanning the mailbox from spending it, and exactly what makes “read it on my phone, finish on my laptop” impossible. Neither is more secure; they fail in different places.
One thing follows from choosing links: a link cannot create an account. It has to hang on an account that already exists, so an address nobody has yet gets a “you have no account here” email instead, and new people arrive by invitation or by signing up.
Passwords are off by default, and turning them on adds the weakest rung — the only credential here that somebody else’s breach can hand to an attacker already working. Sometimes that is the right trade. It is never the free one.
Two password settings live in config/initializers/sparrow_auth.rb rather than
on the panel, because they are code rather than values: the minimum length
(twelve characters by default) and the check that refuses a password already
known to have been breached.
Google and Apple
Off entirely unless you set them up. Each one needs a gem in your Gemfile as well as keys, which is why the panel names the gem beside the fields rather than letting you save a provider that would raise on the first visitor.
An address arriving from Google or Apple is only trusted if the provider says it has been proved. An unproved one is treated like any other unproved address.
Signing keys
Leave it blank. There is nowhere to get one from — no dashboard, no provider, no account.
If it is empty, Rails derives the key from the master key you already have: stable across restarts and deploys, and distinct from every other derived key, so a leak of one does not compromise the others. That is the right answer for almost every application.
Set it only if you need to rotate it on its own schedule, or to share it with another service that has to verify the same codes. Generate it, never invent it:
bin/rails secret
Every code already issued under the previous key stops verifying the moment you save, so do it deliberately.
Who may do what
Not a setting, and not something SparrowKit has any view on. A role is a name your application chooses, and what it permits is ordinary Ruby in your own code.
That is covered under Teams and tenancy.
Where the settings live
Everything on the panel is written to sparrow_auth: in your application’s
Rails encrypted credentials — committed with your code, never sitting in a
.env file waiting to be pasted into a chat window.
You can edit them by hand with bin/rails credentials:edit. The panel is the
easier way, not the only way.
The things a text box cannot hold live in
config/initializers/sparrow_auth.rb instead: the hooks your application is
told through when somebody first signs in, proves an address, accepts an
invitation or creates a team; the expiry windows; and the path Rodauth serves
on.
The pages
SparrowKit ships no screens. No sign-in page of ours, no invitation page, no member list, no team switcher, no billing page. There is no generator that writes them for you.
What you get instead is Rodauth’s own pages, served under /auth:
| Path | What it is |
|---|---|
/auth/login, /auth/logout |
Signing in and out |
/auth/create-account |
Signing up |
/auth/verify-account |
Proving an email address |
/auth/webauthn-setup, /auth/webauthn-auth |
Enrolling and using a passkey |
They already wear your styling. Those pages render through a controller that
sets layout "application" — your application’s own layout — so they arrive
inside your header, your footer and your stylesheet from the first request.
There is nothing of ours to theme and no stylesheet of ours to override.
When you want to change the markup itself:
bin/rails generate rodauth:views
That copies Rodauth’s templates into your application, where they are yours.
Everything a customer of yours looks at beyond those pages is yours to write. You have the models to write it against:
| You write | Using |
|---|---|
| Landing from an invitation | SparrowAuth::Invitation.redeem!(token:, account:) |
| Who is in a team | organization.memberships |
| Somebody’s passkeys | Rodauth’s /auth/webauthn-setup, and the credentials on the account |
| Active sessions | SparrowAuth::Session rows |
| Connected Google and Apple accounts | SparrowAuth::Identity rows |
They are yours rather than ours because every one of them carries your navigation, your words and your styling. A page shipped from a gem is a page you would replace on the first afternoon.
A different address
/auth is a default, not a rule. Install with --mount-at=/accounts, or move
it later by changing the mount line in config/routes.rb and
config.path_prefix in the initializer together. Rodauth runs above the
Rails router rather than through it, so its paths do not follow your mount point
on their own — the two have to be told to agree.
Housekeeping
Two tables gain a row on activity and lose one only when something deletes it, and both hold personal data while they wait — one row per sign-in attempt, and one per emailed code.
bin/rails sparrow_auth:prune
It deletes exactly those two kinds of row, prints what it removed, and takes no arguments. Run it on whatever schedule your application already has for periodic work; daily is generous, hourly is fine.