← All documentation

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.